Compare commits

...

37 commits

Author SHA1 Message Date
Consolatis
259803178e [wip] chase wlroots: track master branch 2025-10-19 05:34:23 +02:00
tokyo4j
e764fc4964 chase wlroots: ime: don't use data in kb grab destroy handler (MR 5170)
Ref: 06275103f249cd2954630e59383342e102a6c1a3
(input-method-v2: Destroy keyboard grab before input method)

Background:

My MR in wlroots (!5107) stopped emitting `wlr_input_method_v2`
on its `commit`/`destroy` events, but didn't stop emitting
`wlr_input_method_keyboard_grab_v2` on its `destroy` event. That was
because `handle_keyboard_grab_destroy()` was called *after*
`handle_input_method_destroy()` for some reason, which caused segfault
when dereferencing `relay->input_method.keyboard_grab` in
`handle_keyboard_grab_destroy()`.

MR 5170 reversed this weired order of destroy handler calls, and finally
stopped emitting `wlr_input_method_keyboard_grab_v2` on its `destroy`
event.
2025-10-19 05:33:54 +02:00
Consolatis
a71723f1ba chase wlroots: ime: rename to new_input_method (MR 5107)
Ref: 06aacb2a6fd237a5e1062d611909432bbcf5b566
(input-method: rename input_method event to new_input_method)
2025-10-19 05:32:41 +02:00
Christopher Snowhill
92c92ae28d [wip] chase wlroots: Add wl_fixes interface (MR + subproject commit missing)
Ref: 812675ba34ce612e9294e8a9814b1baf4b4775d4
(fixes: add implementation)
2025-10-19 05:32:41 +02:00
Consolatis
c9b9c3d227 chase wlroots: ime: rename to new_text_input (MR 5032)
Ref: 536100488fc4c64528786801860f96cfa1a55765
(text-input-v3: Name new text input event correctly)
2025-10-19 05:32:41 +02:00
Consolatis
a9f1145802 chase wlroots: increase wlroots meson dep 2025-10-19 05:32:41 +02:00
Consolatis
8583637331 [revert later] CI: allow compiling wlroots as subproject 2025-10-19 05:32:41 +02:00
tokyo4j
7f67b9c866 Don't remove newlines when parsing config, menu and XBM
Removing newlines in rc.xml and menu.xml caused parser error with
following content:

<!--
 -
 - Some comments
 -
-->

...though it is a valid XML.

Let's not do that. I moved `grab_file()` to `buf.c` and renamed it to
`buf_from_file()`, because it now directly touches `struct buf` and
I don't like having a source file only for one function.
2025-10-17 22:08:41 +09:00
Weblate
eebf5b3e4e Translation updates from weblate
Co-authored-by: alvaroelpob <alvaropobladoresteban9@gmail.com>
Co-authored-by: p-bo <pavel.borecki@gmail.com>
Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/ca/
Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/cs/
Translation: Labwc/labwc
2025-10-14 21:48:28 +01:00
tokyo4j
89fab2d449 theme: replace THEME_(IN)ACTIVE with SSD_(IN)ACTIVE
Some checks failed
labwc.github.io / notify (push) Has been cancelled
2025-10-14 07:48:27 +02:00
Tomi Ollila
c78a0fe1b4 windowswitcher: show 's' as "state" for shaded views
While at it sorted the code to show 'm' before 's' and 's' before 'M'
- from the least visible to the most visible state.
2025-10-13 22:57:23 +02:00
Tobias Bengfort
77a11568a7 implement is_modal_dialog() for xdg shell 2025-10-14 04:19:08 +09:00
tokyo4j
953249249c rcxml: call labnag with --keyboard-focus on-demand by default
Some checks are pending
labwc.github.io / notify (push) Waiting to run
2025-10-13 19:03:43 +01:00
tokyo4j
ef73431367 labnag: add --keyboard-focus option
The new `--keyboard-focus [none|on-demand|exclusive]` option (default:
`none`) allows to some keyboard controls in labnag:

  Right-arrow or Tab: move the button selection to the right
  Left-arrow or Shift-Tab: move the button selection to the left
  Enter: press the selected button
  Escape: close labnag

The selected button is highlighted with the inner 1px border. Maybe we can
instead use different colors for the selected button, but I prefer the
inner border for now because it doesn't require us to add new color
options or make them inherit labwc's theme.
2025-10-13 19:03:43 +01:00
tokyo4j
03c70e8a5e labnag: remove redundant lines in conf_init() 2025-10-13 19:03:43 +01:00
tokyo4j
2b3aadb6af labnag: s/LAB_EXIT_TIMEOUT/LAB_EXIT_CANCELLED/ 2025-10-13 19:03:43 +01:00
Consolatis
364a1d5207 osd: allow window switcher to temporary unshade windows
This can be configured with a new unshade="yes|no"
argument for windowSwitcher in rc.xml

Fixes: #3111
2025-10-13 19:45:46 +02:00
tokyo4j
babd7af8f8 view: store title/app_id in view
This simplifies our codes and eliminates duplicated
`view.events.new_{title,app_id}` events. This should not change any
behaviors.
2025-10-14 02:27:13 +09:00
tokyo4j
27cc738985 osd-thumbnail: make sure item->{normal,active}_title are non-null
The if-statement doesn't make sense, because `view_get_string_prop()`
never returns NULL. And if it did, it would cause segfault in
`osd_thumbnail_update()`.
2025-10-14 02:27:13 +09:00
John Lindgren
da96513e70 menu: remove redundant cast
Some checks are pending
labwc.github.io / notify (push) Waiting to run
2025-10-12 14:15:19 -04:00
John Lindgren
40eed3915a osd,ssd: don't cast away const 2025-10-12 14:15:19 -04:00
John Lindgren
e6f54a0fc8 menu: use xmlFree() for return value of xmlGetProp() 2025-10-12 14:15:19 -04:00
tokyo4j
5e8df27f84 osd-classic: add theme options for selected window item
This commit adds new theme options:
- osd.window-switcher.style-classic.item.active.border.color
- osd.window-switcher.style-classic.item.active.bg.color

These theme options configures the border/background of selected window
item in the `classic` style window switcher. Their default values are
identical to `thumbnail` style window switcher, which means the default
border color is now `osd.label.text.color` with 50% opacity and the
default background color is now `osd.label.text.color` with 15% opacity.
2025-10-13 01:46:43 +09:00
tokyo4j
5f981226c2 action: simplify action_prompt_command() 2025-10-11 23:53:02 +09:00
tokyo4j
2f96664670 build: fix build with libinput as a subproject
Unfortunately, has_header_symbol() doesn't work with internal dependencies.

Ref: https://github.com/mesonbuild/meson/issues/13553
2025-10-11 23:51:28 +09:00
Johan Malm
70e5beb5ec build: bump version to 0.9.2
Some checks failed
labwc.github.io / notify (push) Has been cancelled
2025-10-10 19:53:23 +01:00
tokyo4j
d94e5da815 view: fix unexpected view->tiled with SnapToEdge against centered view
In 2ac4811, I was missing that windows can be tiled to "center".
As a result, after executing
`<action name="SnapToEdge" combined="yes" direction="left" />` against a
center-tiled window, `view->tiled` is set to `CENTER|LEFT`.
2025-10-10 19:48:42 +01:00
Johan Malm
e44a489530 NEWS.md: update notes for 0.9.2 2025-10-10 19:37:40 +01:00
tokyo4j
6cdfe32af0 rcxml: move <maximizedDecoration> from <core> to <theme> 2025-10-09 22:01:05 +01:00
Tobias Bengfort
e1820adcd3 fix typo in comment 2025-10-10 00:19:22 +09:00
Tobias Bengfort
474c513ed6 fix double free for libxml2 < 2.13
xmlAddChild() only unlinks the second argument since libxml2 2.13.

regression from 503af105
2025-10-10 00:19:22 +09:00
tokyo4j
c27d4955a4 desktop-entry: fix wrong description of sfdo-icon flags 2025-10-09 00:59:43 +02:00
Consolatis
7166efe7bf CI: also run on clients/ changes 2025-10-08 22:17:48 +02:00
Johan Malm
017152da52 build: use spaces instead of tab 2025-10-08 21:05:25 +01:00
Consolatis
814af0ae4d desktop-entry.c: don't demote error messages with LABWC_DEBUG_LIBSFDO
Also add additional logging to tell users how to get
more information about failures to load the icon theme.
2025-10-08 20:23:12 +01:00
Consolatis
cb0a4b875e desktop-entry.c: on detecting a broken icon theme, fall back to hicolor
Fixes: #3126
Reported-By: Kreevoz
2025-10-08 20:23:12 +01:00
tokyo4j
998ff9e7b5 osd-thumbnail: update default colors of selected window item
Some checks failed
labwc.github.io / notify (push) Has been cancelled
Previously, the default values of
`osd.window-switcher.style-thumbnail.item.active.{bg,border}.color`
were blue. But they caused the selected window title in the window
switcher to be unreadable due to duplicated colors of the text and
background with Openbox themes like Numix.

Instead, this commit updates them to follow other themes configurations.
The default border color of the selected window item is now
`osd.label.text.color` with 50% opacity and the background is
`osd.label.text.color` with 15% opacity.

For subpixel antialiasing to work, the background color is calculated by
manually blending `osd.label.text.color` and `osd.bg.color`, rather than
just updating the alpha with 50% or 15%.
2025-10-07 19:47:50 +01:00
51 changed files with 840 additions and 604 deletions

View file

@ -17,6 +17,7 @@ on:
- 'src/**'
- 'include/**'
- 'protocols/**'
- 'clients/**'
- 'scripts/**'
- '.github/workflows/**'
@ -93,6 +94,7 @@ jobs:
apt-get install -y git gcc clang gdb xwayland
apt-get build-dep -y labwc
apt-get build-dep -y libwlroots-0.19-dev
apt-get build-dep -y libxkbcommon-dev
- name: Install FreeBSD dependencies
if: matrix.name == 'FreeBSD'
@ -119,7 +121,7 @@ jobs:
xbps-install -y git meson gcc clang pkg-config scdoc \
cairo-devel glib-devel libpng-devel librsvg-devel libxml2-devel \
pango-devel wlroots0.19-devel gdb bash xorg-server-xwayland \
dejavu-fonts-ttf libsfdo-devel foot
dejavu-fonts-ttf libsfdo-devel foot hwids
# These builds are executed on all runners
- name: Build with gcc

47
NEWS.md
View file

@ -9,7 +9,7 @@ The format is based on [Keep a Changelog]
| Date | All Changes | wlroots version | lines-of-code |
|------------|---------------|-----------------|---------------|
| 2025-09-15 | [unreleased] | 0.19.0 | 28686 |
| 2025-10-10 | [0.9.2] | 0.19.1 | 28818 |
| 2025-08-02 | [0.9.1] | 0.19.0 | 28605 |
| 2025-07-11 | [0.9.0] | 0.19.0 | 28586 |
| 2025-05-02 | [0.8.4] | 0.18.2 | 27679 |
@ -39,6 +39,7 @@ The format is based on [Keep a Changelog]
| 2021-03-05 | [0.1.0] | 0.12.0 | 4627 |
[unreleased]: NEWS.md#unreleased
[0.9.2]: NEWS.md#092---2025-10-10
[0.9.1]: NEWS.md#091---2025-08-02
[0.9.0]: NEWS.md#090---2025-07-11
[0.8.4]: NEWS.md#084---2025-05-02
@ -100,23 +101,35 @@ There are some regression warnings worth noting for the switch to wlroots 0.19:
around a bug on the wlroots side which is expected to be fixed in wlroots
`0.19.1` [#2887]
With wlroots compiled with libwayland (>= 1.24.0), there is an invisible margin
preventing pointer focus on some layer-shell surfaces including those created by
Gtk. In simple words, this is because libwayland now rounds floats a bit
differently [#3099]. There is a pending fix [wlroots-5159].
[wlroots-4878]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4878
[wlroots-5098]:https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5098
[wlroots-5159]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5159
[gtk-8792]: https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/8792
## unreleased
[unreleased-commits]
## 0.9.2 - 2025-10-10
[0.9.2-commits]
### Added
- Allow `SnapToEdge` and `ToggleSnapToEdge` to combine two cardinal directions
with the config option `combine="yes|no"`. [#3081] @tokyo4j
- Support `Border` context for mousebinds as an alias for `Top`...`BRCorner` to
make configuration easier. @tokyo4j [#3047]
- Add window-switcher mode with thumbnails. This can be enabled with:
`<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 `<core 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]
@ -126,6 +139,7 @@ There are some regression warnings worth noting for the switch to wlroots 0.19:
[#2994]
- Add `labnag` (a dialog client with message and buttons) and associated
`<prompt>` option in 'If' actions. @johanmalm @Consolatis @tokyo4j [#2699]
- Support config option `<core><promptCommand>` @johanmalm [#3097]
- Allow snapping to corner edges during interactive move with associated config
options `<snapping><cornerRange>`. @tokyo4j [#2885]
- Support new values "up-left", "up-right", "down-left" and "down-right" with
@ -147,6 +161,7 @@ There are some regression warnings worth noting for the switch to wlroots 0.19:
### Fixed
- On detecting broken icon theme, fall back on 'hicolor' @Consolatis [#3126]
- Restore initially-maximized window position after unplug/plug @tokyo4j [#3042]
- Fix large client-side icon not being loaded when the rendered icon size is
larger than icon sizes from the client. @tokyo4j [#3033]
@ -162,6 +177,27 @@ There are some regression warnings worth noting for the switch to wlroots 0.19:
### Changed
- 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
`combine="no"` as shown below. [#3081] @tokyo4j
```
<keybind key="W-Left">
<action name="SnapToEdge" direction="left" combine="no" />
</keybind>
<keybind key="W-Right">
<action name="SnapToEdge" direction="right" combine="no" />
</keybind>
<keybind key="W-Up">
<action name="SnapToEdge" direction="up" combine="no" />
</keybind>
<keybind key="W-Down">
<action name="SnapToEdge" direction="down" combine="no" />
</keybind>
```
- `Focus` and `Raise` on window border press because it is probably what most
people expect and it makes the behavior consistent with that of Openbox.
@johanmalm [#3039] [#3049]
@ -2337,7 +2373,8 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
ShowMenu
[Keep a Changelog]: https://keepachangelog.com/en/1.0.0/
[unreleased-commits]: https://github.com/labwc/labwc/compare/0.9.0...HEAD
[unreleased-commits]: https://github.com/labwc/labwc/compare/0.9.2...HEAD
[0.9.2-commits]: https://github.com/labwc/labwc/compare/0.9.1...0.9.2
[0.9.1-commits]: https://github.com/labwc/labwc/compare/0.9.0...0.9.1
[0.9.0-commits]: https://github.com/labwc/labwc/compare/0.8.4...0.9.0
[0.8.4-commits]: https://github.com/labwc/labwc/compare/0.8.3...0.8.4
@ -2800,3 +2837,7 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
[#3043]: https://github.com/labwc/labwc/pull/3043
[#3047]: https://github.com/labwc/labwc/pull/3047
[#3049]: https://github.com/labwc/labwc/pull/3049
[#3081]: https://github.com/labwc/labwc/pull/3081
[#3097]: https://github.com/labwc/labwc/pull/3097
[#3099]: https://github.com/labwc/labwc/pull/3099
[#3126]: https://github.com/labwc/labwc/pull/3126

View file

@ -19,6 +19,7 @@
#ifdef __FreeBSD__
#include <sys/event.h> /* For signalfd() */
#endif
#include <sys/mman.h>
#include <sys/signalfd.h>
#include <sys/timerfd.h>
#include <sys/wait.h>
@ -26,6 +27,7 @@
#include <unistd.h>
#include <wayland-cursor.h>
#include <wlr/util/log.h>
#include <xkbcommon/xkbcommon.h>
#include "action-prompt-codes.h"
#include "pool-buffer.h"
#include "cursor-shape-v1-client-protocol.h"
@ -38,6 +40,7 @@ struct conf {
char *output;
uint32_t anchors;
int32_t layer; /* enum zwlr_layer_shell_v1_layer or -1 if unset */
enum zwlr_layer_surface_v1_keyboard_interactivity keyboard_focus;
/* Colors */
uint32_t button_text;
@ -69,11 +72,18 @@ struct pointer {
int y;
};
struct keyboard {
struct wl_keyboard *keyboard;
struct xkb_keymap *keymap;
struct xkb_state *state;
};
struct seat {
struct wl_seat *wl_seat;
uint32_t wl_name;
struct nag *nag;
struct pointer pointer;
struct keyboard keyboard;
struct wl_list link; /* nag.seats */
};
@ -130,6 +140,7 @@ struct nag {
struct conf *conf;
char *message;
struct wl_list buttons;
int selected_button;
struct pollfd pollfds[NR_FDS];
struct {
@ -409,7 +420,8 @@ render_detailed(cairo_t *cairo, struct nag *nag, uint32_t y)
}
static uint32_t
render_button(cairo_t *cairo, struct nag *nag, struct button *button, int *x)
render_button(cairo_t *cairo, struct nag *nag, struct button *button,
bool selected, int *x)
{
int text_width, text_height;
get_text_size(cairo, nag->conf->font_description, &text_width,
@ -439,6 +451,14 @@ render_button(cairo_t *cairo, struct nag *nag, struct button *button, int *x)
button->width, button->height);
cairo_fill(cairo);
if (selected) {
cairo_set_source_u32(cairo, nag->conf->button_border);
cairo_set_line_width(cairo, 1);
cairo_rectangle(cairo, button->x + 1.5, button->y + 1.5,
button->width - 3, button->height - 3);
cairo_stroke(cairo);
}
cairo_set_source_u32(cairo, nag->conf->button_text);
cairo_move_to(cairo, button->x + padding, button->y + padding);
render_text(cairo, nag->conf->font_description, 1, true,
@ -464,11 +484,13 @@ render_to_cairo(cairo_t *cairo, struct nag *nag)
int x = nag->width - nag->conf->button_margin_right;
x -= nag->conf->button_gap_close;
int idx = 0;
struct button *button;
wl_list_for_each(button, &nag->buttons, link) {
h = render_button(cairo, nag, button, &x);
h = render_button(cairo, nag, button, idx == nag->selected_button, &x);
max_height = h > max_height ? h : max_height;
x -= nag->conf->button_gap;
idx++;
}
if (nag->details.visible) {
@ -555,6 +577,15 @@ seat_destroy(struct seat *seat)
if (seat->pointer.pointer) {
wl_pointer_destroy(seat->pointer.pointer);
}
if (seat->keyboard.keyboard) {
wl_keyboard_destroy(seat->keyboard.keyboard);
}
if (seat->keyboard.keymap) {
xkb_keymap_unref(seat->keyboard.keymap);
}
if (seat->keyboard.state) {
xkb_state_unref(seat->keyboard.state);
}
wl_seat_destroy(seat->wl_seat);
wl_list_remove(&seat->link);
free(seat);
@ -939,12 +970,170 @@ static const struct wl_pointer_listener pointer_listener = {
.axis_discrete = wl_pointer_axis_discrete,
};
static void
wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
uint32_t format, int32_t fd, uint32_t size)
{
struct seat *seat = data;
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
wlr_log(WLR_ERROR, "unreconizable keymap format: %d", format);
close(fd);
return;
}
char *map_shm = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
if (map_shm == MAP_FAILED) {
wlr_log_errno(WLR_ERROR, "mmap()");
close(fd);
return;
}
if (seat->keyboard.keymap) {
xkb_keymap_unref(seat->keyboard.keymap);
seat->keyboard.keymap = NULL;
}
if (seat->keyboard.state) {
xkb_state_unref(seat->keyboard.state);
seat->keyboard.state = NULL;
}
struct xkb_context *xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
seat->keyboard.keymap = xkb_keymap_new_from_string(xkb, map_shm,
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
if (seat->keyboard.keymap) {
seat->keyboard.state = xkb_state_new(seat->keyboard.keymap);
} else {
wlr_log(WLR_ERROR, "failed to compile keymap");
}
xkb_context_unref(xkb);
munmap(map_shm, size);
close(fd);
}
static void
wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
struct wl_surface *surface, struct wl_array *keys)
{
}
static void
wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
struct wl_surface *surface)
{
}
static void
wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
uint32_t time, uint32_t key, uint32_t state)
{
struct seat *seat = data;
struct nag *nag = seat->nag;
if (!seat->keyboard.keymap || !seat->keyboard.state) {
wlr_log(WLR_ERROR, "keymap/state unavailable");
return;
}
if (state != WL_KEYBOARD_KEY_STATE_PRESSED) {
return;
}
key += 8;
const xkb_keysym_t *syms;
if (!xkb_keymap_key_get_syms_by_level(seat->keyboard.keymap,
key, 0, 0, &syms)) {
wlr_log(WLR_ERROR, "failed to translate key: %d", key);
return;
}
xkb_mod_mask_t mods = xkb_state_serialize_mods(seat->keyboard.state,
XKB_STATE_MODS_EFFECTIVE);
xkb_mod_index_t shift_idx = xkb_keymap_mod_get_index(
seat->keyboard.keymap, XKB_MOD_NAME_SHIFT);
bool shift = shift_idx != XKB_MOD_INVALID && (mods & (1 << shift_idx));
int nr_buttons = wl_list_length(&nag->buttons);
switch (syms[0]) {
case XKB_KEY_Left:
case XKB_KEY_Right:
case XKB_KEY_Tab: {
if (nr_buttons <= 0) {
break;
}
int direction;
if (syms[0] == XKB_KEY_Left || (syms[0] == XKB_KEY_Tab && shift)) {
direction = 1;
} else {
direction = -1;
}
nag->selected_button += nr_buttons + direction;
nag->selected_button %= nr_buttons;
render_frame(nag);
close_pollfd(&nag->pollfds[FD_TIMER]);
break;
}
case XKB_KEY_Escape:
exit_status = LAB_EXIT_CANCELLED;
nag->run_display = false;
break;
case XKB_KEY_Return:
case XKB_KEY_KP_Enter: {
int idx = 0;
struct button *button;
wl_list_for_each(button, &nag->buttons, link) {
if (idx == nag->selected_button) {
button_execute(nag, button);
close_pollfd(&nag->pollfds[FD_TIMER]);
exit_status = idx;
break;
}
idx++;
}
break;
}
}
}
static void
wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
uint32_t mods_locked, uint32_t group)
{
struct seat *seat = data;
if (!seat->keyboard.state) {
wlr_log(WLR_ERROR, "xkb state unavailable");
return;
}
xkb_state_update_mask(seat->keyboard.state, mods_depressed,
mods_latched, mods_locked, 0, 0, group);
}
static void
wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
int32_t rate, int32_t delay)
{
}
static const struct wl_keyboard_listener keyboard_listener = {
.keymap = wl_keyboard_keymap,
.enter = wl_keyboard_enter,
.leave = wl_keyboard_leave,
.key = wl_keyboard_key,
.modifiers = wl_keyboard_modifiers,
.repeat_info = wl_keyboard_repeat_info,
};
static void
seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
enum wl_seat_capability caps)
{
struct seat *seat = data;
bool cap_pointer = caps & WL_SEAT_CAPABILITY_POINTER;
bool cap_keyboard = caps & WL_SEAT_CAPABILITY_KEYBOARD;
if (cap_pointer && !seat->pointer.pointer) {
seat->pointer.pointer = wl_seat_get_pointer(wl_seat);
wl_pointer_add_listener(seat->pointer.pointer,
@ -953,6 +1142,15 @@ seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
wl_pointer_destroy(seat->pointer.pointer);
seat->pointer.pointer = NULL;
}
if (cap_keyboard && !seat->keyboard.keyboard) {
seat->keyboard.keyboard = wl_seat_get_keyboard(wl_seat);
wl_keyboard_add_listener(seat->keyboard.keyboard,
&keyboard_listener, seat);
} else if (!cap_keyboard && seat->keyboard.keyboard) {
wl_keyboard_destroy(seat->keyboard.keyboard);
seat->keyboard.keyboard = NULL;
}
}
static void
@ -1075,7 +1273,7 @@ handle_global(void *data, struct wl_registry *registry, uint32_t name,
}
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
nag->layer_shell = wl_registry_bind(
registry, name, &zwlr_layer_shell_v1_interface, 1);
registry, name, &zwlr_layer_shell_v1_interface, 4);
} else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) {
nag->cursor_shape_manager = wl_registry_bind(
registry, name, &wp_cursor_shape_manager_v1_interface, 1);
@ -1170,6 +1368,8 @@ nag_setup(struct nag *nag)
&layer_surface_listener, nag);
zwlr_layer_surface_v1_set_anchor(nag->layer_surface,
nag->conf->anchors);
zwlr_layer_surface_v1_set_keyboard_interactivity(nag->layer_surface,
nag->conf->keyboard_focus);
wl_registry_destroy(registry);
@ -1233,7 +1433,7 @@ nag_run(struct nag *nag)
wl_display_cancel_read(nag->display);
}
if (nag->pollfds[FD_TIMER].revents & POLLIN) {
exit_status = LAB_EXIT_TIMEOUT;
exit_status = LAB_EXIT_CANCELLED;
break;
}
if (nag->pollfds[FD_SIGNAL].revents & POLLIN) {
@ -1250,13 +1450,7 @@ conf_init(struct conf *conf)
| ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
| ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
conf->layer = ZWLR_LAYER_SHELL_V1_LAYER_TOP;
conf->button_background = 0x333333FF;
conf->details_background = 0x333333FF;
conf->background = 0x323232FF;
conf->text = 0xFFFFFFFF;
conf->button_text = 0xFFFFFFFF;
conf->button_border = 0x222222FF;
conf->border_bottom = 0x444444FF;
conf->keyboard_focus = ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE;
conf->bar_border_thickness = 2;
conf->message_padding = 8;
conf->details_border_thickness = 3;
@ -1364,6 +1558,7 @@ nag_parse_options(int argc, char **argv, struct nag *nag,
{"debug", no_argument, NULL, 'd'},
{"edge", required_argument, NULL, 'e'},
{"layer", required_argument, NULL, 'y'},
{"keyboard-focus", required_argument, NULL, 'k'},
{"font", required_argument, NULL, 'f'},
{"help", no_argument, NULL, 'h'},
{"detailed-message", no_argument, NULL, 'l'},
@ -1402,6 +1597,8 @@ nag_parse_options(int argc, char **argv, struct nag *nag,
" -e, --edge top|bottom Set the edge to use.\n"
" -y, --layer overlay|top|bottom|background\n"
" Set the layer to use.\n"
" -k, --keyboard-focus none|exclusive|on-demand|\n"
" Set the policy for keyboard focus.\n"
" -f, --font <font> Set the font to use.\n"
" -h, --help Show help message and quit.\n"
" -l, --detailed-message Read a detailed message from stdin.\n"
@ -1433,7 +1630,7 @@ nag_parse_options(int argc, char **argv, struct nag *nag,
optind = 1;
while (1) {
int c = getopt_long(argc, argv, "B:Z:c:de:y:f:hlL:m:o:s:t:vx", opts, NULL);
int c = getopt_long(argc, argv, "B:Z:c:de:y:k:f:hlL:m:o:s:t:vx", opts, NULL);
if (c == -1) {
break;
}
@ -1487,6 +1684,23 @@ nag_parse_options(int argc, char **argv, struct nag *nag,
return LAB_EXIT_FAILURE;
}
break;
case 'k':
if (strcmp(optarg, "none") == 0) {
conf->keyboard_focus =
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE;
} else if (strcmp(optarg, "exclusive") == 0) {
conf->keyboard_focus =
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE;
} else if (strcmp(optarg, "on-demand") == 0) {
conf->keyboard_focus =
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND;
} else {
fprintf(stderr, "Invalid keyboard focus: %s\n"
"Usage: --keyboard-focus none|exclusive|on-demand\n",
optarg);
return LAB_EXIT_FAILURE;
}
break;
case 'f': /* Font */
pango_font_description_free(conf->font_description);
conf->font_description = pango_font_description_from_string(optarg);
@ -1629,6 +1843,14 @@ main(int argc, char **argv)
wl_list_insert(nag.buttons.prev, &nag.details.button_details->link);
}
int nr_buttons = wl_list_length(&nag.buttons);
if (conf.keyboard_focus && nr_buttons > 0) {
/* select the leftmost button */
nag.selected_button = nr_buttons - 1;
} else {
nag.selected_button = -1;
}
wlr_log(WLR_DEBUG, "Output: %s", nag.conf->output);
wlr_log(WLR_DEBUG, "Anchors: %lu", (unsigned long)nag.conf->anchors);
wlr_log(WLR_DEBUG, "Message: %s", nag.message);

View file

@ -49,9 +49,10 @@ executable(
wlroots,
server_protos,
epoll_dep,
xkbcommon,
],
include_directories: [labwc_inc],
install: true
install: true,
)
clients = files('lab-sensible-terminal')

View file

@ -31,6 +31,9 @@ _labnag_ [options...]
*-y, --layer* overlay|top|bottom|background
Set the layer to use.
*-k, --keyboard-focus none|exclusive|on-demand*
Set the policy for keyboard focus.
*-f, --font* <font>
Set the font to use.

View file

@ -171,7 +171,6 @@ this is for compatibility with Openbox.
```
<core>
<decoration>server</decoration>
<maximizedDecoration>titlebar</maximizedDecoration>
<gap>0</gap>
<adaptiveSync>no</adaptiveSync>
<allowTearing>no</allowTearing>
@ -188,11 +187,6 @@ this is for compatibility with Openbox.
that it is not always possible to turn off client side decorations.
Default is server.
*<core><maximizedDecoration>* [titlebar|none]
Specify how server side decorations are shown for maximized windows.
*titlebar* shows titlebar above a maximized window. *none* shows no server
side decorations around a maximized window. Default is titlebar.
*<core><gap>*
The distance in pixels between windows and output edges when using
movement actions, for example MoveToEdge. Default is 0.
@ -291,6 +285,7 @@ this is for compatibility with Openbox.
--button-text-color '%t' \\
--border-bottom-size 1 \\
--button-border-size 3 \\
--keyboard-focus on-demand \\
--timeout 0
```
@ -352,7 +347,7 @@ this is for compatibility with Openbox.
</windowSwitcher>
```
*<windowSwitcher show="" style="" preview="" outlines="" allWorkspaces="">*
*<windowSwitcher show="" style="" preview="" outlines="" allWorkspaces="" unshade="">*
*show* [yes|no] Draw the OnScreenDisplay when switching between
windows. Default is yes.
@ -370,6 +365,9 @@ this is for compatibility with Openbox.
they are on. Default no (that is only windows on the current workspace
are shown).
*unshade* [yes|no] Temporarily unshade windows when switching between
them and permanently unshade on the final selection. Default is yes.
*<windowSwitcher><fields><field content="" width="%">*
Define window switcher fields when using *<windowSwitcher style="classic" />*.
@ -403,9 +401,9 @@ this is for compatibility with Openbox.
fields are:
- 'B' - shell type, values [xwayland|xdg-shell]
- 'b' - shell type (short form), values [X|W]
- 'S' - state of window, values [M|m|F] (3 spaces allocated)
(maximized, minimized, fullscreen)
- 's' - state of window (short form), values [M|m|F] (1 space)
- 'S' - state of window, values [m|s|M|F] (4 spaces allocated)
(minimized, shaded, maximized, fullscreen)
- 's' - state of window (short form), values [m|s|M|F] (1 space)
- 'I' - wm-class/app-id
- 'i' - wm-class/app-id trimmed, remove "org." if available
- 'n' - desktop entry/file application name, falls back to
@ -603,6 +601,11 @@ extending outward from the snapped edge.
Even when disabling server side decorations via ToggleDecorations,
keep a small border (and resize area) around the window. Default is yes.
*<theme><maximizedDecoration>* [titlebar|none]
Specify how server side decorations are shown for maximized windows.
*titlebar* shows titlebar above a maximized window. *none* shows no server
side decorations around a maximized window. Default is titlebar.
*<theme><dropShadows>* [yes|no]
Should drop-shadows be rendered behind windows. Default is no.

View file

@ -327,6 +327,14 @@ all are supported.
Border width of the selection box in the window switcher in pixels.
Default is 2.
*osd.window-switcher.style-classic.item.active.border.color*
Border color around the selected window switcher item.
Default is *osd.label.text.color* with 50% opacity.
*osd.window-switcher.style-classic.item.active.bg.color*
Background color of the selected window switcher item.
Default is *osd.label.text.color* with 15% opacity.
*osd.window-switcher.style-classic.item.icon.size*
Size of the icon in window switcher, in pixels.
If not set, the font size derived from <theme><font place="OnScreenDisplay">
@ -358,10 +366,12 @@ all are supported.
Border width of selected window switcher items in pixels. Default is 2.
*osd.window-switcher.style-thumbnail.item.active.border.color*
Color of border around selected window switcher items. Default is #589bda.
Color of border around selected window switcher items.
Default is *osd.label.text.color* with 50% opacity.
*osd.window-switcher.style-thumbnail.item.active.bg.color*
Color of selected window switcher items. Default is #c7e2fc.
Color of selected window switcher items.
Default is *osd.label.text.color* with 15% opacity.
*osd.window-switcher.style-thumbnail.item.icon.size*
Size of window icons in window switcher items in pixels. Default is 60.

View file

@ -11,7 +11,6 @@
<core>
<decoration>server</decoration>
<maximizedDecoration>titlebar</maximizedDecoration>
<gap>0</gap>
<adaptiveSync>no</adaptiveSync>
<allowTearing>no</allowTearing>
@ -45,6 +44,7 @@
</titlebar>
<cornerRadius>8</cornerRadius>
<keepBorder>yes</keepBorder>
<maximizedDecoration>titlebar</maximizedDecoration>
<dropShadows>no</dropShadows>
<dropShadowsOnTiled>no</dropShadowsOnTiled>
<font place="ActiveWindow">
@ -79,7 +79,8 @@
</font>
</theme>
<windowSwitcher show="yes" style="classic" preview="yes" outlines="yes" allWorkspaces="no">
<windowSwitcher show="yes" style="classic" preview="yes"
outlines="yes" allWorkspaces="no" unshade="yes">
<fields>
<field content="icon" width="5%" />
<field content="desktop_entry_name" width="30%" />

View file

@ -97,6 +97,8 @@ osd.window-switcher.style-classic.padding: 4
osd.window-switcher.style-classic.item.padding.x: 10
osd.window-switcher.style-classic.item.padding.y: 1
osd.window-switcher.style-classic.item.active.border.width: 2
osd.window-switcher.style-classic.item.active.border.color: #706f6d
osd.window-switcher.style-classic.item.active.bg.color: #bfbcba
# The icon size the same as the font size by default
# osd.window-switcher.style-classic.item.icon.size: 50
@ -106,8 +108,8 @@ osd.window-switcher.style-thumbnail.item.width: 300
osd.window-switcher.style-thumbnail.item.height: 250
osd.window-switcher.style-thumbnail.item.padding: 10
osd.window-switcher.style-thumbnail.item.active.border.width: 2
osd.window-switcher.style-thumbnail.item.active.border.color: #589bda
osd.window-switcher.style-thumbnail.item.active.bg.color: #c7e2fc
osd.window-switcher.style-thumbnail.item.active.border.color: #706f6d
osd.window-switcher.style-thumbnail.item.active.bg.color: #bfbcba
osd.window-switcher.style-thumbnail.item.icon.size: 60
osd.window-switcher.preview.border.width: 1

View file

@ -3,7 +3,7 @@
#define LABWC_ACTION_PROMPT_CODES_H
#define LAB_EXIT_FAILURE 255
#define LAB_EXIT_TIMEOUT 254
#define LAB_EXIT_CANCELLED 254
#define LAB_EXIT_SUCCESS 0
#endif /* LABWC_ACTION_PROMPT_CODES_H */

View file

@ -1,12 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef LABWC_ACTION_PROMPT_COMMAND_H
#define LABWC_ACTION_PROMPT_COMMAND_H
struct buf;
struct action;
struct theme;
void action_prompt_command(struct buf *buf, const char *format,
struct action *action, struct theme *theme);
#endif /* LABWC_ACTION_PROMPT_COMMAND_H */

View file

@ -109,4 +109,11 @@ void buf_reset(struct buf *s);
*/
void buf_move(struct buf *dst, struct buf *src);
/**
* buf_from_file - read file into memory buffer
* @filename: file to read
* Free returned buffer with buf_reset().
*/
struct buf buf_from_file(const char *filename);
#endif /* LABWC_BUF_H */

View file

@ -1,20 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Read file into memory
*
* Copyright Johan Malm 2020
*/
#ifndef LABWC_GRAB_FILE_H
#define LABWC_GRAB_FILE_H
#include "common/buf.h"
/**
* grab_file - read file into memory buffer
* @filename: file to read
* Free returned buffer with buf_reset().
*/
struct buf grab_file(const char *filename);
#endif /* LABWC_GRAB_FILE_H */

View file

@ -179,6 +179,7 @@ struct rcxml {
bool show;
bool preview;
bool outlines;
bool unshade;
enum lab_view_criteria criteria;
struct wl_list fields; /* struct window_switcher_field.link */
enum window_switcher_style style;

View file

@ -303,6 +303,7 @@ struct server {
/* Set when in cycle (alt-tab) mode */
struct osd_state {
struct view *cycle_view;
bool preview_was_shaded;
bool preview_was_enabled;
struct wlr_scene_node *preview_node;
struct wlr_scene_tree *preview_parent;

View file

@ -83,7 +83,7 @@ struct theme {
/*
* Themes/textures for each active/inactive window. Indexed by
* THEME_INACTIVE and THEME_ACTIVE.
* ssd_active_state.
*/
struct {
/* title background pattern (solid or gradient) */
@ -170,6 +170,8 @@ struct theme {
int item_padding_x;
int item_padding_y;
int item_active_border_width;
float item_active_border_color[4];
float item_active_bg_color[4];
int item_icon_size;
bool width_is_percent;
@ -210,10 +212,6 @@ struct theme {
int mag_border_width;
};
/* TODO: replace with enum ssd_active_state */
#define THEME_INACTIVE 0
#define THEME_ACTIVE 1
struct server;
/**

View file

@ -106,7 +106,6 @@ struct view_size_hints {
struct view_impl {
void (*configure)(struct view *view, struct wlr_box geo);
void (*close)(struct view *view);
const char *(*get_string_prop)(struct view *view, const char *prop);
void (*map)(struct view *view);
void (*set_activated)(struct view *view, bool activated);
void (*set_fullscreen)(struct view *view, bool fullscreen);
@ -173,6 +172,10 @@ struct view {
struct wlr_scene_tree *scene_tree;
struct wlr_scene_tree *content_tree;
/* These are never NULL and an empty string is set instead. */
char *title;
char *app_id; /* WM_CLASS for xwayland windows */
bool mapped;
bool been_mapped;
enum lab_ssd_mode ssd_mode;
@ -571,9 +574,8 @@ bool view_on_output(struct view *view, struct output *output);
*/
bool view_has_strut_partial(struct view *view);
const char *view_get_string_prop(struct view *view, const char *prop);
void view_update_title(struct view *view);
void view_update_app_id(struct view *view);
void view_set_title(struct view *view, const char *title);
void view_set_app_id(struct view *view, const char *app_id);
void view_reload_ssd(struct view *view);
void view_set_shade(struct view *view, bool shaded);

View file

@ -1,7 +1,7 @@
project(
'labwc',
'c',
version: '0.9.0',
version: '0.9.2',
license: 'GPL-2.0-only',
meson_version: '>=0.59.0',
default_options: [
@ -51,9 +51,9 @@ endif
add_project_arguments('-DLABWC_VERSION=@0@'.format(version), language: 'c')
wlroots = dependency(
'wlroots-0.19',
'wlroots-0.20',
default_options: ['default_library=static', 'examples=false'],
version: ['>=0.19.0', '<0.20.0'],
version: ['>=0.20.0', '<0.21.0'],
)
wlroots_has_xwayland = wlroots.get_variable('have_xwayland') == 'true'
@ -127,7 +127,9 @@ conf_data.set10('HAVE_RSVG', have_rsvg)
conf_data.set10('HAVE_LIBSFDO', have_libsfdo)
foreach sym : ['LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY', 'LIBINPUT_CONFIG_3FG_DRAG_ENABLED_3FG']
conf_data.set10('HAVE_' + sym, cc.has_header_symbol('libinput.h', sym, dependencies: input))
has_sym = input.type_name() != 'internal' \
and cc.has_header_symbol('libinput.h', sym, dependencies: input)
conf_data.set10('HAVE_' + sym, has_sym)
endforeach
if get_option('static_analyzer').enabled()

View file

@ -8,9 +8,10 @@ msgstr ""
"Project-Id-Version: labwc\n"
"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
"POT-Creation-Date: 2024-09-19 21:09+1000\n"
"PO-Revision-Date: 2025-03-29 11:25+0000\n"
"Last-Translator: Davidmp <opensusecatala@gmail.com>\n"
"Language-Team: Catalan <https://translate.lxqt-project.org/projects/labwc/labwc/ca/>\n"
"PO-Revision-Date: 2025-10-11 20:01+0000\n"
"Last-Translator: alvaroelpob <alvaropobladoresteban9@gmail.com>\n"
"Language-Team: Catalan <https://translate.lxqt-project.org/projects/labwc/"
"labwc/ca/>\n"
"Language: ca\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -24,7 +25,7 @@ msgstr "Ves-hi..."
#: src/menu/menu.c:1034
msgid "Terminal"
msgstr ""
msgstr "Terminal"
#: src/menu/menu.c:1040
msgid "Reconfigure"

View file

@ -8,29 +8,32 @@ msgstr ""
"Project-Id-Version: labwc\n"
"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
"POT-Creation-Date: 2024-09-19 21:09+1000\n"
"PO-Revision-Date: 2024-03-02 02:00+0100\n"
"Last-Translator: zenobit <zenobit@disroot.org>\n"
"Language-Team: Czech <zenobit@disroot.org>\n"
"PO-Revision-Date: 2025-10-11 20:01+0000\n"
"Last-Translator: p-bo <pavel.borecki@gmail.com>\n"
"Language-Team: Czech <https://translate.lxqt-project.org/projects/labwc/"
"labwc/cs/>\n"
"Language: cs\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
"X-Generator: Weblate 4.2.1\n"
#: src/menu/menu.c:1016
msgid "Go there..."
msgstr ""
msgstr "Přejít tam..."
#: src/menu/menu.c:1034
msgid "Terminal"
msgstr ""
msgstr "Terminál"
#: src/menu/menu.c:1040
msgid "Reconfigure"
msgstr "Překonfigurovat"
msgstr "Přenastavit"
#: src/menu/menu.c:1042
msgid "Exit"
msgstr "Odejít"
msgstr "Ukončit"
#: src/menu/menu.c:1056
msgid "Minimize"
@ -46,7 +49,7 @@ msgstr "Na celou obrazovku"
#: src/menu/menu.c:1062
msgid "Roll Up/Down"
msgstr "Rolovat nahoru/dolů"
msgstr "Posouvat nahoru/dolů"
#: src/menu/menu.c:1064
msgid "Decorations"
@ -66,11 +69,11 @@ msgstr "Posunout doprava"
#: src/menu/menu.c:1083
msgid "Always on Visible Workspace"
msgstr "Vždy na viditelné Pracovní Ploše"
msgstr "Vždy na viditelné Pracovní ploše"
#: src/menu/menu.c:1086
msgid "Workspace"
msgstr "Pracovní Plocha"
msgstr "Pracovní plocha"
#: src/menu/menu.c:1089
msgid "Close"

View file

@ -1,108 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
#define _POSIX_C_SOURCE 200809L
#include "action-prompt-command.h"
#include <stdio.h>
#include <wlr/util/log.h>
#include "action.h"
#include "common/buf.h"
#include "theme.h"
#include "translate.h"
enum {
LAB_PROMPT_NONE = 0,
LAB_PROMPT_MESSAGE,
LAB_PROMPT_NO,
LAB_PROMPT_YES,
LAB_PROMPT_BG_COL,
LAB_PROMPT_TEXT_COL,
LAB_PROMPT_COUNT
};
typedef void field_conversion_type(struct buf *buf, struct action *action,
struct theme *theme);
struct field_converter {
const char fmt_char;
field_conversion_type *fn;
};
/* %m */
static void
set_message(struct buf *buf, struct action *action, struct theme *theme)
{
buf_add(buf, action_get_str(action, "message.prompt", "Choose wisely"));
}
/* %n */
static void
set_no(struct buf *buf, struct action *action, struct theme *theme)
{
buf_add(buf, _("No"));
}
/* %y */
static void
set_yes(struct buf *buf, struct action *action, struct theme *theme)
{
buf_add(buf, _("Yes"));
}
/* %b */
static void
set_bg_col(struct buf *buf, struct action *action, struct theme *theme)
{
buf_add_hex_color(buf, theme->osd_bg_color);
}
/* %t */
static void
set_text_col(struct buf *buf, struct action *action, struct theme *theme)
{
buf_add_hex_color(buf, theme->osd_label_text_color);
}
static const struct field_converter field_converter[LAB_PROMPT_COUNT] = {
[LAB_PROMPT_MESSAGE] = { 'm', set_message },
[LAB_PROMPT_NO] = { 'n', set_no },
[LAB_PROMPT_YES] = { 'y', set_yes },
[LAB_PROMPT_BG_COL] = { 'b', set_bg_col },
[LAB_PROMPT_TEXT_COL] = { 't', set_text_col },
};
void
action_prompt_command(struct buf *buf, const char *format,
struct action *action, struct theme *theme)
{
if (!format) {
wlr_log(WLR_ERROR, "missing format");
return;
}
for (const char *p = format; *p; p++) {
/*
* If we're not on a conversion specifier (like %m) then just
* keep adding it to the buffer
*/
if (*p != '%') {
buf_add_char(buf, *p);
continue;
}
/* Process the %* conversion specifier */
++p;
bool found = false;
for (unsigned char i = 0; i < LAB_PROMPT_COUNT; i++) {
if (*p == field_converter[i].fmt_char) {
field_converter[i].fn(buf, action, theme);
found = true;
break;
}
}
if (!found) {
wlr_log(WLR_ERROR,
"invalid prompt command conversion specifier '%c'", *p);
}
}
}

View file

@ -10,7 +10,6 @@
#include <wlr/types/wlr_scene.h>
#include <wlr/util/log.h>
#include "action-prompt-codes.h"
#include "action-prompt-command.h"
#include "common/buf.h"
#include "common/macros.h"
#include "common/list.h"
@ -30,6 +29,7 @@
#include "regions.h"
#include "ssd.h"
#include "theme.h"
#include "translate.h"
#include "view.h"
#include "workspaces.h"
@ -831,11 +831,55 @@ handle_view_destroy(struct wl_listener *listener, void *data)
prompt->view = NULL;
}
static void
print_prompt_command(struct buf *buf, const char *format,
struct action *action, struct theme *theme)
{
assert(format);
for (const char *p = format; *p; p++) {
/*
* If we're not on a conversion specifier (like %m) then just
* keep adding it to the buffer
*/
if (*p != '%') {
buf_add_char(buf, *p);
continue;
}
/* Process the %* conversion specifier */
++p;
switch (*p) {
case 'm':
buf_add(buf, action_get_str(action,
"message.prompt", "Choose wisely"));
break;
case 'n':
buf_add(buf, _("No"));
break;
case 'y':
buf_add(buf, _("Yes"));
break;
case 'b':
buf_add_hex_color(buf, theme->osd_bg_color);
break;
case 't':
buf_add_hex_color(buf, theme->osd_label_text_color);
break;
default:
wlr_log(WLR_ERROR,
"invalid prompt command conversion specifier '%c'", *p);
break;
}
}
}
static void
action_prompt_create(struct view *view, struct server *server, struct action *action)
{
struct buf command = BUF_INIT;
action_prompt_command(&command, rc.prompt_command, action, rc.theme);
print_prompt_command(&command, rc.prompt_command, action, rc.theme);
wlr_log(WLR_INFO, "prompt command: '%s'", command.data);
@ -890,7 +934,7 @@ action_check_prompt_result(pid_t pid, int exit_code)
if (exit_code == LAB_EXIT_SUCCESS) {
wlr_log(WLR_INFO, "Selected the 'then' branch");
actions = action_get_actionlist(prompt->action, "then");
} else if (exit_code == LAB_EXIT_TIMEOUT) {
} else if (exit_code == LAB_EXIT_CANCELLED) {
/* no-op */
} else {
wlr_log(WLR_INFO, "Selected the 'else' branch");

View file

@ -6,6 +6,7 @@
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <wlr/util/log.h>
#include "common/macros.h"
#include "common/mem.h"
#include "common/string-helpers.h"
@ -203,3 +204,37 @@ buf_move(struct buf *dst, struct buf *src)
*dst = *src;
*src = BUF_INIT;
}
struct buf
buf_from_file(const char *filename)
{
struct buf buf = BUF_INIT;
FILE *stream = fopen(filename, "r");
if (!stream) {
return buf;
}
if (fseek(stream, 0, SEEK_END) == -1) {
wlr_log_errno(WLR_ERROR, "fseek(%s)", filename);
fclose(stream);
return buf;
}
long size = ftell(stream);
if (size == -1) {
wlr_log_errno(WLR_ERROR, "ftell(%s)", filename);
fclose(stream);
return buf;
}
rewind(stream);
buf_expand(&buf, size + 1);
if (fread(buf.data, 1, size, stream) == (size_t)size) {
buf.len = size;
buf.data[size] = '\0';
} else {
wlr_log_errno(WLR_ERROR, "fread(%s)", filename);
buf_reset(&buf);
}
fclose(stream);
return buf;
}

View file

@ -1,36 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Read file into memory
*
* Copyright Johan Malm 2020
*/
#define _POSIX_C_SOURCE 200809L
#include "common/grab-file.h"
#include "common/buf.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct buf
grab_file(const char *filename)
{
char *line = NULL;
size_t len = 0;
FILE *stream = fopen(filename, "r");
if (!stream) {
return BUF_INIT;
}
struct buf buffer = BUF_INIT;
while ((getline(&line, &len, stream) != -1)) {
char *p = strrchr(line, '\n');
if (p) {
*p = '\0';
}
buf_add(&buffer, line);
}
free(line);
fclose(stream);
return buffer;
}

View file

@ -6,7 +6,6 @@ labwc_sources += files(
'fd-util.c',
'file-helpers.c',
'font.c',
'grab-file.c',
'graphic-helpers.c',
'lab-scene-rect.c',
'match.c',

View file

@ -48,7 +48,7 @@ create_attribute_tree(const xmlAttr *attr)
}
/*
* Consider <keybind name.action="ShowMenu" x.position.action="1" y.position="2" />.
* Consider <keybind name.action="ShowMenu" x.position.action="1" y.position.action="2" />.
* These three attributes are represented by following trees.
* action(dst)---name
* action(src)---position---x
@ -79,7 +79,8 @@ merge_two_trees(xmlNode *dst, xmlNode *src)
&& !strcasecmp((char *)dst->name, (char *)src->name)) {
xmlNode *next_dst = dst->last;
xmlNode *next_src = src->children;
xmlAddChild(dst, src->children);
xmlUnlinkNode(next_src);
xmlAddChild(dst, next_src);
xmlUnlinkNode(src);
xmlFreeNode(src);
src = next_src;

View file

@ -1083,12 +1083,6 @@ entry(xmlNode *node, char *nodename, char *content)
} else {
rc.xdg_shell_server_side_deco = true;
}
} else if (!strcasecmp(nodename, "maximizedDecoration.core")) {
if (!strcasecmp(content, "titlebar")) {
rc.hide_maximized_window_titlebar = false;
} else if (!strcasecmp(content, "none")) {
rc.hide_maximized_window_titlebar = true;
}
} else if (!strcmp(nodename, "gap.core")) {
rc.gap = atoi(content);
} else if (!strcasecmp(nodename, "adaptiveSync.core")) {
@ -1130,6 +1124,12 @@ entry(xmlNode *node, char *nodename, char *content)
rc.corner_radius = atoi(content);
} else if (!strcasecmp(nodename, "keepBorder.theme")) {
set_bool(content, &rc.ssd_keep_border);
} else if (!strcasecmp(nodename, "maximizedDecoration.theme")) {
if (!strcasecmp(content, "titlebar")) {
rc.hide_maximized_window_titlebar = false;
} else if (!strcasecmp(content, "none")) {
rc.hide_maximized_window_titlebar = true;
}
} else if (!strcasecmp(nodename, "dropShadows.theme")) {
set_bool(content, &rc.shadows_enabled);
} else if (!strcasecmp(nodename, "dropShadowsOnTiled.theme")) {
@ -1217,6 +1217,8 @@ entry(xmlNode *node, char *nodename, char *content)
rc.window_switcher.criteria &=
~LAB_VIEW_CRITERIA_CURRENT_WORKSPACE;
}
} else if (!strcasecmp(nodename, "unshade.windowSwitcher")) {
set_bool(content, &rc.window_switcher.unshade);
/* Remove this long term - just a friendly warning for now */
} else if (strstr(nodename, "windowswitcher.core")) {
@ -1429,6 +1431,7 @@ rcxml_init(void)
rc.window_switcher.style = WINDOW_SWITCHER_CLASSIC;
rc.window_switcher.preview = true;
rc.window_switcher.outlines = true;
rc.window_switcher.unshade = true;
rc.window_switcher.criteria = LAB_VIEW_CRITERIA_CURRENT_WORKSPACE
| LAB_VIEW_CRITERIA_ROOT_TOPLEVEL
| LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER;
@ -1642,6 +1645,7 @@ post_processing(void)
"--button-text-color '%t' "
"--border-bottom-size 1 "
"--button-border-size 3 "
"--keyboard-focus on-demand "
"--timeout 0");
}
if (!rc.fallback_app_icon_name) {
@ -1868,25 +1872,13 @@ rcxml_read(const char *filename)
*/
for (struct wl_list *elm = iter(&paths); elm != &paths; elm = iter(elm)) {
struct path *path = wl_container_of(elm, path, link);
FILE *stream = fopen(path->string, "r");
if (!stream) {
struct buf b = buf_from_file(path->string);
if (!b.len) {
continue;
}
wlr_log(WLR_INFO, "read config file %s", path->string);
struct buf b = BUF_INIT;
char *line = NULL;
size_t len = 0;
while (getline(&line, &len, stream) != -1) {
char *p = strrchr(line, '\n');
if (p) {
*p = '\0';
}
buf_add(&b, line);
}
zfree(line);
fclose(stream);
rcxml_parse_xml(&b);
buf_reset(&b);
if (!should_merge_config) {

View file

@ -71,11 +71,10 @@ get_view_part(struct view *view, struct wlr_scene_node *node)
return NULL;
}
if (node == &view->scene_tree->node) {
const char *app_id = view_get_string_prop(view, "app_id");
if (string_null_or_empty(app_id)) {
if (string_null_or_empty(view->app_id)) {
return "view";
}
snprintf(view_name, sizeof(view_name), "view (%s)", app_id);
snprintf(view_name, sizeof(view_name), "view (%s)", view->app_id);
return view_name;
}
if (node == &view->content_tree->node) {

View file

@ -37,9 +37,10 @@ log_handler(enum sfdo_log_level level, const char *fmt, va_list args, void *tag)
/*
* To avoid logging issues with .desktop files as errors, all libsfdo
* error-logging is demoted to info level.
* error-logging is demoted to info level unless running with
* LABWC_DEBUG_LIBSFDO.
*/
if (level == SFDO_LOG_LEVEL_ERROR) {
if (!debug_libsfdo && level == SFDO_LOG_LEVEL_ERROR) {
level = SFDO_LOG_LEVEL_INFO;
}
@ -95,19 +96,37 @@ desktop_entry_init(struct server *server)
* We set some relaxed load options to accommodate delinquent themes in
* the wild, namely:
*
* - SFDO_ICON_THEME_LOAD_OPTION_ALLOW_MISSING to "impose less
* restrictions on the format of icon theme files"
* - SFDO_ICON_THEME_LOAD_OPTION_RELAXED to "impose less restrictions
* on the format of icon theme files"
*
* - SFDO_ICON_THEME_LOAD_OPTION_RELAXED to "continue loading even if it
* fails to find a theme or one of its dependencies."
* - SFDO_ICON_THEME_LOAD_OPTION_ALLOW_MISSING to "continue loading
* even if it fails to find a theme or one of its dependencies."
*/
int load_options = SFDO_ICON_THEME_LOAD_OPTIONS_DEFAULT
| SFDO_ICON_THEME_LOAD_OPTION_ALLOW_MISSING
| SFDO_ICON_THEME_LOAD_OPTION_RELAXED;
| SFDO_ICON_THEME_LOAD_OPTION_RELAXED
| SFDO_ICON_THEME_LOAD_OPTION_ALLOW_MISSING;
sfdo->icon_theme = sfdo_icon_theme_load(
sfdo->icon_ctx,
rc.icon_theme_name, load_options);
if (!sfdo->icon_theme) {
/*
* sfdo_icon_theme_load() falls back to hicolor theme with
* _ALLOW_MISSING flag when the theme is missing, but just
* fails when the theme is invalid.
* So manually call sfdo_icon_theme_load() again here.
*/
wlr_log(WLR_ERROR, "Failed to load icon theme %s, falling back to 'hicolor'",
rc.icon_theme_name);
if (!debug_libsfdo) {
wlr_log(WLR_ERROR, "Further information is available by setting "
"the LABWC_DEBUG_LIBSFDO=1 env var before starting labwc");
}
sfdo->icon_theme = sfdo_icon_theme_load(
sfdo->icon_ctx, "hicolor", load_options);
}
if (!sfdo->icon_theme) {
goto err_icon_theme;
}
@ -129,6 +148,10 @@ err_desktop_ctx:
err_basedir_ctx:
free(sfdo);
wlr_log(WLR_ERROR, "Failed to initialize icon loader");
if (!debug_libsfdo) {
wlr_log(WLR_ERROR, "Further information is available by setting "
"the LABWC_DEBUG_LIBSFDO=1 env var before starting labwc");
}
}
void

View file

@ -32,8 +32,8 @@ handle_new_app_id(struct wl_listener *listener, void *data)
assert(ext_toplevel->handle);
struct wlr_ext_foreign_toplevel_handle_v1_state state = {
.title = view_get_string_prop(ext_toplevel->view, "title"),
.app_id = view_get_string_prop(ext_toplevel->view, "app_id")
.title = ext_toplevel->view->title,
.app_id = ext_toplevel->view->app_id,
};
wlr_ext_foreign_toplevel_handle_v1_update_state(ext_toplevel->handle,
&state);
@ -47,8 +47,8 @@ handle_new_title(struct wl_listener *listener, void *data)
assert(ext_toplevel->handle);
struct wlr_ext_foreign_toplevel_handle_v1_state state = {
.title = view_get_string_prop(ext_toplevel->view, "title"),
.app_id = view_get_string_prop(ext_toplevel->view, "app_id")
.title = ext_toplevel->view->title,
.app_id = ext_toplevel->view->app_id,
};
wlr_ext_foreign_toplevel_handle_v1_update_state(ext_toplevel->handle,
&state);
@ -63,15 +63,15 @@ ext_foreign_toplevel_init(struct ext_foreign_toplevel *ext_toplevel,
ext_toplevel->view = view;
struct wlr_ext_foreign_toplevel_handle_v1_state state = {
.title = view_get_string_prop(view, "title"),
.app_id = view_get_string_prop(view, "app_id")
.title = view->title,
.app_id = view->app_id,
};
ext_toplevel->handle = wlr_ext_foreign_toplevel_handle_v1_create(
view->server->foreign_toplevel_list, &state);
if (!ext_toplevel->handle) {
wlr_log(WLR_ERROR, "cannot create ext toplevel handle for (%s)",
view_get_string_prop(view, "title"));
view->title);
return;
}

View file

@ -94,13 +94,8 @@ handle_new_app_id(struct wl_listener *listener, void *data)
wl_container_of(listener, wlr_toplevel, on_view.new_app_id);
assert(wlr_toplevel->handle);
const char *app_id = view_get_string_prop(wlr_toplevel->view, "app_id");
const char *wlr_app_id = wlr_toplevel->handle->app_id;
if (app_id && wlr_app_id && !strcmp(app_id, wlr_app_id)) {
/* Don't send app_id if they are the same */
return;
}
wlr_foreign_toplevel_handle_v1_set_app_id(wlr_toplevel->handle, app_id);
wlr_foreign_toplevel_handle_v1_set_app_id(wlr_toplevel->handle,
wlr_toplevel->view->app_id);
}
static void
@ -110,13 +105,8 @@ handle_new_title(struct wl_listener *listener, void *data)
wl_container_of(listener, wlr_toplevel, on_view.new_title);
assert(wlr_toplevel->handle);
const char *title = view_get_string_prop(wlr_toplevel->view, "title");
const char *wlr_title = wlr_toplevel->handle->title;
if (title && wlr_title && !strcmp(title, wlr_title)) {
/* Don't send title if they are the same */
return;
}
wlr_foreign_toplevel_handle_v1_set_title(wlr_toplevel->handle, title);
wlr_foreign_toplevel_handle_v1_set_title(wlr_toplevel->handle,
wlr_toplevel->view->title);
}
static void
@ -202,7 +192,7 @@ wlr_foreign_toplevel_init(struct wlr_foreign_toplevel *wlr_toplevel,
view->server->foreign_toplevel_manager);
if (!wlr_toplevel->handle) {
wlr_log(WLR_ERROR, "cannot create wlr foreign toplevel handle for (%s)",
view_get_string_prop(view, "title"));
view->title);
return;
}

View file

@ -12,7 +12,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common/grab-file.h"
#include "common/buf.h"
#include "common/mem.h"
#include "common/string-helpers.h"
#include "buffer.h"
@ -273,7 +273,7 @@ img_xbm_load(const char *filename, float *rgba)
uint32_t color = argb32(rgba);
/* Read file into memory as it's easier to tokenize that way */
struct buf token_buf = grab_file(filename);
struct buf token_buf = buf_from_file(filename);
if (token_buf.len) {
struct token *tokens = tokenize_xbm(token_buf.data);
pixmap = parse_xbm_tokens(tokens, color);

View file

@ -309,7 +309,8 @@ handle_keyboard_grab_destroy(struct wl_listener *listener, void *data)
{
struct input_method_relay *relay =
wl_container_of(listener, relay, keyboard_grab_destroy);
struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data;
struct wlr_input_method_keyboard_grab_v2 *keyboard_grab =
relay->input_method->keyboard_grab;
assert(keyboard_grab);
wl_list_remove(&relay->keyboard_grab_destroy.link);
@ -583,11 +584,11 @@ input_method_relay_create(struct seat *seat)
relay->popup_tree = wlr_scene_tree_create(&seat->server->scene->tree);
relay->new_text_input.notify = handle_new_text_input;
wl_signal_add(&seat->server->text_input_manager->events.text_input,
wl_signal_add(&seat->server->text_input_manager->events.new_text_input,
&relay->new_text_input);
relay->new_input_method.notify = handle_new_input_method;
wl_signal_add(&seat->server->input_method_manager->events.input_method,
wl_signal_add(&seat->server->input_method_manager->events.new_input_method,
&relay->new_input_method);
relay->focused_surface_destroy.notify = handle_focused_surface_destroy;

View file

@ -96,6 +96,9 @@ end_cycling(struct server *server)
/* FIXME: osd_finish() transiently sets focus to the old surface */
osd_finish(server);
/* Note that server->osd_state.cycle_view is cleared at this point */
if (rc.window_switcher.unshade) {
view_set_shade(cycle_view, false);
}
desktop_focus_view(cycle_view, /*raise*/ true);
}

View file

@ -482,13 +482,13 @@ fill_item(struct menu *menu, xmlNode *node)
goto out;
}
struct menuitem *item = item_create(menu, (char *)label, icon_name, false);
struct menuitem *item = item_create(menu, label, icon_name, false);
lab_xml_expand_dotted_attributes(node);
append_parsed_actions(node, &item->actions);
out:
free(label);
free(icon_name);
xmlFree(label);
xmlFree(icon_name);
}
static void
@ -619,10 +619,10 @@ fill_menu(struct server *server, struct menu *parent, xmlNode *n)
item->submenu = menu;
}
error:
free(label);
free(icon_name);
free(execute);
free(id);
xmlFree(label);
xmlFree(icon_name);
xmlFree(execute);
xmlFree(id);
}
/* This can be one of <separator> and <separator label=""> */
@ -631,7 +631,7 @@ fill_separator(struct menu *menu, xmlNode *n)
{
char *label = (char *)xmlGetProp(n, (const xmlChar *)"label");
separator_create(menu, label);
free(label);
xmlFree(label);
}
/* parent==NULL when processing toplevel menus in menu.xml */
@ -679,30 +679,6 @@ parse_buf(struct server *server, struct menu *parent, struct buf *buf)
return true;
}
/*
* @stream can come from either of the following:
* - fopen() in the case of reading a file such as menu.xml
* - popen() when processing pipemenus
*/
static void
parse_stream(struct server *server, FILE *stream)
{
char *line = NULL;
size_t len = 0;
struct buf b = BUF_INIT;
while (getline(&line, &len, stream) != -1) {
char *p = strrchr(line, '\n');
if (p) {
*p = '\0';
}
buf_add(&b, line);
}
free(line);
parse_buf(server, NULL, &b);
buf_reset(&b);
}
static void
parse_xml(const char *filename, struct server *server)
{
@ -715,13 +691,13 @@ parse_xml(const char *filename, struct server *server)
for (struct wl_list *elm = iter(&paths); elm != &paths; elm = iter(elm)) {
struct path *path = wl_container_of(elm, path, link);
FILE *stream = fopen(path->string, "r");
if (!stream) {
struct buf buf = buf_from_file(path->string);
if (!buf.len) {
continue;
}
wlr_log(WLR_INFO, "read menu file %s", path->string);
parse_stream(server, stream);
fclose(stream);
parse_buf(server, /*parent*/ NULL, &buf);
buf_reset(&buf);
if (!should_merge_config) {
break;
}
@ -900,8 +876,8 @@ update_client_list_combined_menu(struct server *server)
wl_list_for_each(view, &server->views, link) {
if (view->workspace == workspace) {
const char *title = view_get_string_prop(view, "title");
if (!view->foreign_toplevel || string_null_or_empty(title)) {
if (!view->foreign_toplevel
|| string_null_or_empty(view->title)) {
continue;
}
@ -909,9 +885,9 @@ update_client_list_combined_menu(struct server *server)
buf_add(&buffer, "*");
}
if (view->minimized) {
buf_add_fmt(&buffer, "(%s)", title);
buf_add_fmt(&buffer, "(%s)", view->title);
} else {
buf_add(&buffer, title);
buf_add(&buffer, view->title);
}
item = item_create(menu, buffer.data, NULL,
/*show arrow*/ false);

View file

@ -1,6 +1,5 @@
labwc_sources = files(
'action.c',
'action-prompt-command.c',
'buffer.c',
'debug.c',
'desktop.c',

View file

@ -19,9 +19,59 @@
struct osd_classic_scene_item {
struct view *view;
struct wlr_scene_node *highlight_outline;
struct wlr_scene_tree *normal_tree, *active_tree;
};
static void
create_fields_scene(struct server *server, struct view *view,
struct wlr_scene_tree *parent, const float *text_color,
const float *bg_color, int field_widths_sum, int x, int y)
{
struct theme *theme = server->theme;
struct window_switcher_classic_theme *switcher_theme =
&theme->osd_window_switcher_classic;
struct window_switcher_field *field;
wl_list_for_each(field, &rc.window_switcher.fields, link) {
int field_width = field_widths_sum * field->width / 100.0;
struct wlr_scene_node *node = NULL;
int height = -1;
if (field->content == LAB_FIELD_ICON) {
int icon_size = MIN(field_width,
switcher_theme->item_icon_size);
struct scaled_icon_buffer *icon_buffer =
scaled_icon_buffer_create(parent,
server, icon_size, icon_size);
scaled_icon_buffer_set_view(icon_buffer, view);
node = &icon_buffer->scene_buffer->node;
height = icon_size;
} else {
struct buf buf = BUF_INIT;
osd_field_get_content(field, &buf, view);
if (!string_null_or_empty(buf.data)) {
struct scaled_font_buffer *font_buffer =
scaled_font_buffer_create(parent);
scaled_font_buffer_update(font_buffer,
buf.data, field_width,
&rc.font_osd, text_color, bg_color);
node = &font_buffer->scene_buffer->node;
height = font_height(&rc.font_osd);
}
buf_reset(&buf);
}
if (node) {
int item_height = switcher_theme->item_height;
wlr_scene_node_set_position(node,
x, y + (item_height - height) / 2);
}
x += field_width + switcher_theme->item_padding_x;
}
}
static void
osd_classic_create(struct output *output, struct wl_array *views)
{
@ -126,62 +176,33 @@ osd_classic_create(struct output *output, struct wl_array *views)
+ switcher_theme->item_padding_x;
struct wlr_scene_tree *item_root =
wlr_scene_tree_create(output->osd_scene.tree);
item->normal_tree = wlr_scene_tree_create(item_root);
item->active_tree = wlr_scene_tree_create(item_root);
wlr_scene_node_set_enabled(&item->active_tree->node, false);
struct window_switcher_field *field;
wl_list_for_each(field, &rc.window_switcher.fields, link) {
int field_width = field_widths_sum * field->width / 100.0;
struct wlr_scene_node *node = NULL;
int height = -1;
if (field->content == LAB_FIELD_ICON) {
int icon_size = MIN(field_width,
switcher_theme->item_icon_size);
struct scaled_icon_buffer *icon_buffer =
scaled_icon_buffer_create(item_root,
server, icon_size, icon_size);
scaled_icon_buffer_set_view(icon_buffer, *view);
node = &icon_buffer->scene_buffer->node;
height = icon_size;
} else {
buf_clear(&buf);
osd_field_get_content(field, &buf, *view);
if (!string_null_or_empty(buf.data)) {
struct scaled_font_buffer *font_buffer =
scaled_font_buffer_create(item_root);
scaled_font_buffer_update(font_buffer,
buf.data, field_width,
&rc.font_osd, text_color, bg_color);
node = &font_buffer->scene_buffer->node;
height = font_height(&rc.font_osd);
}
}
if (node) {
int item_height = switcher_theme->item_height;
wlr_scene_node_set_position(node,
x, y + (item_height - height) / 2);
}
x += field_width + switcher_theme->item_padding_x;
}
float *active_bg_color = switcher_theme->item_active_bg_color;
float *active_border_color = switcher_theme->item_active_border_color;
/* Highlight around selected window's item */
int highlight_x = theme->osd_border_width
+ switcher_theme->padding;
struct lab_scene_rect_options highlight_opts = {
.border_colors = (float *[1]) {text_color},
.border_colors = (float *[1]) {active_border_color},
.bg_color = active_bg_color,
.nr_borders = 1,
.border_width = switcher_theme->item_active_border_width,
.width = w - 2 * theme->osd_border_width
- 2 * switcher_theme->padding,
.height = switcher_theme->item_height,
};
struct lab_scene_rect *highlight_rect = lab_scene_rect_create(
output->osd_scene.tree, &highlight_opts);
item->highlight_outline = &highlight_rect->tree->node;
wlr_scene_node_set_position(item->highlight_outline, highlight_x, y);
wlr_scene_node_set_enabled(item->highlight_outline, false);
item->active_tree, &highlight_opts);
wlr_scene_node_set_position(&highlight_rect->tree->node, highlight_x, y);
create_fields_scene(server, *view, item->normal_tree,
text_color, bg_color, field_widths_sum, x, y);
create_fields_scene(server, *view, item->active_tree,
text_color, active_bg_color, field_widths_sum, x, y);
y += switcher_theme->item_height;
}
@ -200,8 +221,9 @@ osd_classic_update(struct output *output)
{
struct osd_classic_scene_item *item;
wl_array_for_each(item, &output->osd_scene.items) {
wlr_scene_node_set_enabled(item->highlight_outline,
item->view == output->server->osd_state.cycle_view);
bool active = item->view == output->server->osd_state.cycle_view;
wlr_scene_node_set_enabled(&item->normal_tree->node, !active);
wlr_scene_node_set_enabled(&item->active_tree->node, active);
}
}

View file

@ -26,17 +26,13 @@ struct field_converter {
/* Internal helpers */
static const char *
get_app_id_or_class(struct view *view, bool trim)
get_identifier(struct view *view, bool trim)
{
/*
* XWayland clients return WM_CLASS for 'app_id' so we don't need a
* special case for that here.
*/
const char *identifier = view_get_string_prop(view, "app_id");
const char *identifier = view->app_id;
/* remove the first two nodes of 'org.' strings */
if (trim && !strncmp(identifier, "org.", 4)) {
char *p = (char *)identifier + 4;
const char *p = identifier + 4;
p = strchr(p, '.');
if (p) {
return ++p;
@ -49,14 +45,13 @@ static const char *
get_desktop_name(struct view *view)
{
#if HAVE_LIBSFDO
const char *app_id = view_get_string_prop(view, "app_id");
const char *name = desktop_entry_name_lookup(view->server, app_id);
const char *name = desktop_entry_name_lookup(view->server, view->app_id);
if (name) {
return name;
}
#endif
return get_app_id_or_class(view, /* trim */ true);
return get_identifier(view, /* trim */ true);
}
static const char *
@ -73,21 +68,12 @@ get_type(struct view *view, bool short_form)
return "???";
}
static const char *
get_title(struct view *view)
{
return view_get_string_prop(view, "title");
}
static const char *
get_title_if_different(struct view *view)
{
const char *identifier = get_app_id_or_class(view, /*trim*/ false);
const char *title = get_title(view);
if (!identifier) {
return title;
}
return (!title || !strcmp(identifier, title)) ? NULL : title;
const char *identifier = get_identifier(view, /*trim*/ false);
const char *title = view->title;
return !strcmp(identifier, title) ? NULL : title;
}
/* Field handlers */
@ -125,10 +111,12 @@ static void
field_set_win_state(struct buf *buf, struct view *view, const char *format)
{
/* custom type conversion-specifier: s */
if (view->maximized) {
buf_add(buf, "M");
} else if (view->minimized) {
if (view->minimized) {
buf_add(buf, "m");
} else if (view->shaded) {
buf_add(buf, "s");
} else if (view->maximized) {
buf_add(buf, "M");
} else if (view->fullscreen) {
buf_add(buf, "F");
} else {
@ -141,6 +129,7 @@ field_set_win_state_all(struct buf *buf, struct view *view, const char *format)
{
/* custom type conversion-specifier: S */
buf_add(buf, view->minimized ? "m" : " ");
buf_add(buf, view->shaded ? "s" : " ");
buf_add(buf, view->maximized ? "M" : " ");
buf_add(buf, view->fullscreen ? "F" : " ");
/* TODO: add always-on-top and omnipresent ? */
@ -169,14 +158,14 @@ static void
field_set_identifier(struct buf *buf, struct view *view, const char *format)
{
/* custom type conversion-specifier: I */
buf_add(buf, get_app_id_or_class(view, /*trim*/ false));
buf_add(buf, get_identifier(view, /*trim*/ false));
}
static void
field_set_identifier_trimmed(struct buf *buf, struct view *view, const char *format)
{
/* custom type conversion-specifier: i */
buf_add(buf, get_app_id_or_class(view, /*trim*/ true));
buf_add(buf, get_identifier(view, /*trim*/ true));
}
static void
@ -190,7 +179,7 @@ static void
field_set_title(struct buf *buf, struct view *view, const char *format)
{
/* custom type conversion-specifier: T */
buf_add(buf, get_title(view));
buf_add(buf, view->title);
}
static void

View file

@ -155,15 +155,12 @@ create_item_scene(struct wlr_scene_tree *parent, struct view *view,
}
/* title */
const char *title = view_get_string_prop(view, "title");
if (title) {
item->normal_title = create_title(item->tree, switcher_theme,
title, theme->osd_label_text_color,
view->title, theme->osd_label_text_color,
theme->osd_bg_color, title_y);
item->active_title = create_title(item->tree, switcher_theme,
title, theme->osd_label_text_color,
view->title, theme->osd_label_text_color,
switcher_theme->item_active_bg_color, title_y);
}
/* icon */
int icon_size = switcher_theme->item_icon_size;

View file

@ -9,6 +9,7 @@
#include "common/scene-helpers.h"
#include "config/rcxml.h"
#include "labwc.h"
#include "node.h"
#include "output.h"
#include "scaled-buffer/scaled-font-buffer.h"
#include "scaled-buffer/scaled-icon-buffer.h"
@ -160,9 +161,14 @@ restore_preview_node(struct server *server)
if (!osd_state->preview_was_enabled) {
wlr_scene_node_set_enabled(osd_state->preview_node, false);
}
if (osd_state->preview_was_shaded) {
struct view *view = node_view_from_node(osd_state->preview_node);
view_set_shade(view, true);
}
osd_state->preview_node = NULL;
osd_state->preview_parent = NULL;
osd_state->preview_anchor = NULL;
osd_state->preview_was_shaded = false;
}
}
@ -203,6 +209,7 @@ osd_finish(struct server *server)
server->osd_state.preview_node = NULL;
server->osd_state.preview_anchor = NULL;
server->osd_state.cycle_view = NULL;
server->osd_state.preview_was_shaded = false;
destroy_osd_scenes(server);
@ -244,6 +251,10 @@ preview_cycled_view(struct view *view)
if (!osd_state->preview_was_enabled) {
wlr_scene_node_set_enabled(osd_state->preview_node, true);
}
if (rc.window_switcher.unshade && view->shaded) {
view_set_shade(view, false);
osd_state->preview_was_shaded = true;
}
/*
* FIXME: This abuses an implementation detail of the always-on-top tree.
@ -294,6 +305,10 @@ update_osd(struct server *server)
}
}
if (rc.window_switcher.preview) {
preview_cycled_view(server->osd_state.cycle_view);
}
/* Outline current window */
if (rc.window_switcher.outlines) {
if (view_is_focusable(server->osd_state.cycle_view)) {
@ -301,9 +316,6 @@ update_osd(struct server *server)
}
}
if (rc.window_switcher.preview) {
preview_cycled_view(server->osd_state.cycle_view);
}
out:
wl_array_release(&views);
}

View file

@ -281,7 +281,7 @@ handle_view_new_app_id(struct wl_listener *listener, void *data)
struct scaled_icon_buffer *self =
wl_container_of(listener, self, on_view.new_app_id);
const char *app_id = view_get_string_prop(self->view, "app_id");
const char *app_id = self->view->app_id;
if (str_equal(app_id, self->view_app_id)) {
return;
}

View file

@ -17,6 +17,7 @@
#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>
#include <wlr/types/wlr_ext_image_capture_source_v1.h>
#include <wlr/types/wlr_ext_image_copy_capture_v1.h>
#include <wlr/types/wlr_fixes.h>
#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
#include <wlr/types/wlr_fractional_scale_v1.h>
#include <wlr/types/wlr_input_method_v2.h>
@ -255,6 +256,7 @@ allow_for_sandbox(const struct wlr_security_context_v1_state *security_state,
"wl_data_device_manager", /* would be great if we could drop this one */
"wl_seat",
"xdg_wm_base",
"wl_fixes",
/* enhanced */
"wl_output",
"wl_drm",
@ -276,6 +278,7 @@ allow_for_sandbox(const struct wlr_security_context_v1_state *security_state,
"zxdg_importer_v1",
"zxdg_importer_v2",
"xdg_toplevel_icon_manager_v1",
"xdg_dialog_v1",
/* plus */
"wp_alpha_modifier_v1",
"wp_linux_drm_syncobj_manager_v1",
@ -434,6 +437,8 @@ server_init(struct server *server)
server->wl_event_loop = wl_display_get_event_loop(server->wl_display);
wlr_fixes_create(server->wl_display, 1);
/* Catch signals */
server->sighup_source = wl_event_loop_add_signal(
server->wl_event_loop, SIGHUP, handle_sighup, server);

View file

@ -259,8 +259,8 @@ ssd_shadow_update(struct ssd *ssd)
bool maximized = view->maximized == VIEW_AXIS_BOTH;
bool tiled_shadows = false;
if (rc.shadows_on_tiled) {
if (rc.gap >= theme->window[THEME_ACTIVE].shadow_size
&& rc.gap >= theme->window[THEME_INACTIVE].shadow_size) {
if (rc.gap >= theme->window[SSD_ACTIVE].shadow_size
&& rc.gap >= theme->window[SSD_INACTIVE].shadow_size) {
tiled_shadows = true;
} else {
wlr_log(WLR_INFO, "gap size < shadow_size, ignore rc.shadows_ontiled");

View file

@ -440,14 +440,13 @@ ssd_update_title(struct ssd *ssd)
}
struct view *view = ssd->view;
char *title = (char *)view_get_string_prop(view, "title");
if (string_null_or_empty(title)) {
if (string_null_or_empty(view->title)) {
return;
}
struct theme *theme = view->server->theme;
struct ssd_state_title *state = &ssd->state.title;
bool title_unchanged = state->text && !strcmp(title, state->text);
bool title_unchanged = state->text && !strcmp(view->title, state->text);
int offset_left, offset_right;
get_title_offsets(ssd, &offset_left, &offset_right);
@ -473,7 +472,7 @@ ssd_update_title(struct ssd *ssd)
}
const float bg_color[4] = {0, 0, 0, 0}; /* ignored */
scaled_font_buffer_update(subtree->title, title,
scaled_font_buffer_update(subtree->title, view->title,
title_bg_width, font,
text_color, bg_color);
@ -483,10 +482,7 @@ ssd_update_title(struct ssd *ssd)
}
if (!title_unchanged) {
if (state->text) {
free(state->text);
}
state->text = xstrdup(title);
xstrdup_replace(state->text, view->title);
}
ssd_update_title_positions(ssd, offset_left, offset_right);
}

View file

@ -348,7 +348,7 @@ ssd_enable_keybind_inhibit_indicator(struct ssd *ssd, bool enable)
float *color = enable
? rc.theme->window_toggled_keybinds_color
: rc.theme->window[THEME_ACTIVE].border_color;
: rc.theme->window[SSD_ACTIVE].border_color;
wlr_scene_rect_set_color(ssd->border.subtrees[SSD_ACTIVE].top, color);
}

View file

@ -162,7 +162,7 @@ get_button_filename(char *buf, size_t len, const char *name, const char *postfix
}
static void
load_button(struct theme *theme, struct button *b, int active)
load_button(struct theme *theme, struct button *b, enum ssd_active_state active)
{
struct lab_img *(*button_imgs)[LAB_BS_ALL + 1] =
theme->window[active].button_imgs;
@ -374,8 +374,8 @@ load_buttons(struct theme *theme)
for (size_t i = 0; i < ARRAY_SIZE(buttons); ++i) {
struct button *b = &buttons[i];
load_button(theme, b, THEME_INACTIVE);
load_button(theme, b, THEME_ACTIVE);
load_button(theme, b, SSD_INACTIVE);
load_button(theme, b, SSD_ACTIVE);
}
}
@ -537,24 +537,24 @@ theme_builtin(struct theme *theme, struct server *server)
theme->window_titlebar_padding_height = 0;
theme->window_titlebar_padding_width = 0;
parse_hexstr("#aaaaaa", theme->window[THEME_ACTIVE].border_color);
parse_hexstr("#aaaaaa", theme->window[THEME_INACTIVE].border_color);
parse_hexstr("#aaaaaa", theme->window[SSD_ACTIVE].border_color);
parse_hexstr("#aaaaaa", theme->window[SSD_INACTIVE].border_color);
parse_hexstr("#ff0000", theme->window_toggled_keybinds_color);
theme->window[THEME_ACTIVE].title_bg.gradient = LAB_GRADIENT_NONE;
theme->window[THEME_INACTIVE].title_bg.gradient = LAB_GRADIENT_NONE;
parse_hexstr("#e1dedb", theme->window[THEME_ACTIVE].title_bg.color);
parse_hexstr("#f6f5f4", theme->window[THEME_INACTIVE].title_bg.color);
theme->window[THEME_ACTIVE].title_bg.color_split_to[0] = FLT_MIN;
theme->window[THEME_INACTIVE].title_bg.color_split_to[0] = FLT_MIN;
theme->window[THEME_ACTIVE].title_bg.color_to[0] = FLT_MIN;
theme->window[THEME_INACTIVE].title_bg.color_to[0] = FLT_MIN;
theme->window[THEME_ACTIVE].title_bg.color_to_split_to[0] = FLT_MIN;
theme->window[THEME_INACTIVE].title_bg.color_to_split_to[0] = FLT_MIN;
theme->window[SSD_ACTIVE].title_bg.gradient = LAB_GRADIENT_NONE;
theme->window[SSD_INACTIVE].title_bg.gradient = LAB_GRADIENT_NONE;
parse_hexstr("#e1dedb", theme->window[SSD_ACTIVE].title_bg.color);
parse_hexstr("#f6f5f4", theme->window[SSD_INACTIVE].title_bg.color);
theme->window[SSD_ACTIVE].title_bg.color_split_to[0] = FLT_MIN;
theme->window[SSD_INACTIVE].title_bg.color_split_to[0] = FLT_MIN;
theme->window[SSD_ACTIVE].title_bg.color_to[0] = FLT_MIN;
theme->window[SSD_INACTIVE].title_bg.color_to[0] = FLT_MIN;
theme->window[SSD_ACTIVE].title_bg.color_to_split_to[0] = FLT_MIN;
theme->window[SSD_INACTIVE].title_bg.color_to_split_to[0] = FLT_MIN;
parse_hexstr("#000000", theme->window[THEME_ACTIVE].label_text_color);
parse_hexstr("#000000", theme->window[THEME_INACTIVE].label_text_color);
parse_hexstr("#000000", theme->window[SSD_ACTIVE].label_text_color);
parse_hexstr("#000000", theme->window[SSD_INACTIVE].label_text_color);
theme->window_label_text_justify = parse_justification("Center");
theme->window_button_width = 26;
@ -565,15 +565,15 @@ theme_builtin(struct theme *theme, struct server *server)
for (enum lab_node_type type = LAB_NODE_BUTTON_FIRST;
type <= LAB_NODE_BUTTON_LAST; type++) {
parse_hexstr("#000000",
theme->window[THEME_INACTIVE].button_colors[type]);
theme->window[SSD_INACTIVE].button_colors[type]);
parse_hexstr("#000000",
theme->window[THEME_ACTIVE].button_colors[type]);
theme->window[SSD_ACTIVE].button_colors[type]);
}
theme->window[THEME_ACTIVE].shadow_size = 60;
theme->window[THEME_INACTIVE].shadow_size = 40;
parse_hexstr("#00000060", theme->window[THEME_ACTIVE].shadow_color);
parse_hexstr("#00000040", theme->window[THEME_INACTIVE].shadow_color);
theme->window[SSD_ACTIVE].shadow_size = 60;
theme->window[SSD_INACTIVE].shadow_size = 40;
parse_hexstr("#00000060", theme->window[SSD_ACTIVE].shadow_color);
parse_hexstr("#00000040", theme->window[SSD_INACTIVE].shadow_color);
theme->menu_overlap_x = 0;
theme->menu_overlap_y = 0;
@ -604,6 +604,8 @@ theme_builtin(struct theme *theme, struct server *server)
theme->osd_window_switcher_classic.item_padding_x = 10;
theme->osd_window_switcher_classic.item_padding_y = 1;
theme->osd_window_switcher_classic.item_active_border_width = 2;
theme->osd_window_switcher_classic.item_active_border_color[0] = FLT_MIN;
theme->osd_window_switcher_classic.item_active_bg_color[0] = FLT_MIN;
theme->osd_window_switcher_classic.item_icon_size = -1;
theme->osd_window_switcher_thumbnail.max_width = 80;
@ -613,8 +615,8 @@ theme_builtin(struct theme *theme, struct server *server)
theme->osd_window_switcher_thumbnail.item_height = 250;
theme->osd_window_switcher_thumbnail.item_padding = 10;
theme->osd_window_switcher_thumbnail.item_active_border_width = 2;
parse_color("#589bda", theme->osd_window_switcher_thumbnail.item_active_border_color);
parse_color("#c7e2fc", theme->osd_window_switcher_thumbnail.item_active_bg_color);
theme->osd_window_switcher_thumbnail.item_active_border_color[0] = FLT_MIN;
theme->osd_window_switcher_thumbnail.item_active_bg_color[0] = FLT_MIN;
theme->osd_window_switcher_thumbnail.item_icon_size = 60;
/* inherit settings in post_processing() if not set elsewhere */
@ -709,15 +711,15 @@ entry(struct theme *theme, const char *key, const char *value)
}
if (match_glob(key, "window.active.border.color")) {
parse_color(value, theme->window[THEME_ACTIVE].border_color);
parse_color(value, theme->window[SSD_ACTIVE].border_color);
}
if (match_glob(key, "window.inactive.border.color")) {
parse_color(value, theme->window[THEME_INACTIVE].border_color);
parse_color(value, theme->window[SSD_INACTIVE].border_color);
}
/* border.color is obsolete, but handled for backward compatibility */
if (match_glob(key, "border.color")) {
parse_color(value, theme->window[THEME_ACTIVE].border_color);
parse_color(value, theme->window[THEME_INACTIVE].border_color);
parse_color(value, theme->window[SSD_ACTIVE].border_color);
parse_color(value, theme->window[SSD_INACTIVE].border_color);
}
if (match_glob(key, "window.active.indicator.toggled-keybind.color")) {
@ -725,41 +727,41 @@ entry(struct theme *theme, const char *key, const char *value)
}
if (match_glob(key, "window.active.title.bg")) {
theme->window[THEME_ACTIVE].title_bg.gradient = parse_gradient(value);
theme->window[SSD_ACTIVE].title_bg.gradient = parse_gradient(value);
}
if (match_glob(key, "window.inactive.title.bg")) {
theme->window[THEME_INACTIVE].title_bg.gradient = parse_gradient(value);
theme->window[SSD_INACTIVE].title_bg.gradient = parse_gradient(value);
}
if (match_glob(key, "window.active.title.bg.color")) {
parse_color(value, theme->window[THEME_ACTIVE].title_bg.color);
parse_color(value, theme->window[SSD_ACTIVE].title_bg.color);
}
if (match_glob(key, "window.inactive.title.bg.color")) {
parse_color(value, theme->window[THEME_INACTIVE].title_bg.color);
parse_color(value, theme->window[SSD_INACTIVE].title_bg.color);
}
if (match_glob(key, "window.active.title.bg.color.splitTo")) {
parse_color(value, theme->window[THEME_ACTIVE].title_bg.color_split_to);
parse_color(value, theme->window[SSD_ACTIVE].title_bg.color_split_to);
}
if (match_glob(key, "window.inactive.title.bg.color.splitTo")) {
parse_color(value, theme->window[THEME_INACTIVE].title_bg.color_split_to);
parse_color(value, theme->window[SSD_INACTIVE].title_bg.color_split_to);
}
if (match_glob(key, "window.active.title.bg.colorTo")) {
parse_color(value, theme->window[THEME_ACTIVE].title_bg.color_to);
parse_color(value, theme->window[SSD_ACTIVE].title_bg.color_to);
}
if (match_glob(key, "window.inactive.title.bg.colorTo")) {
parse_color(value, theme->window[THEME_INACTIVE].title_bg.color_to);
parse_color(value, theme->window[SSD_INACTIVE].title_bg.color_to);
}
if (match_glob(key, "window.active.title.bg.colorTo.splitTo")) {
parse_color(value, theme->window[THEME_ACTIVE].title_bg.color_to_split_to);
parse_color(value, theme->window[SSD_ACTIVE].title_bg.color_to_split_to);
}
if (match_glob(key, "window.inactive.title.bg.colorTo.splitTo")) {
parse_color(value, theme->window[THEME_INACTIVE].title_bg.color_to_split_to);
parse_color(value, theme->window[SSD_INACTIVE].title_bg.color_to_split_to);
}
if (match_glob(key, "window.active.label.text.color")) {
parse_color(value, theme->window[THEME_ACTIVE].label_text_color);
parse_color(value, theme->window[SSD_ACTIVE].label_text_color);
}
if (match_glob(key, "window.inactive.label.text.color")) {
parse_color(value, theme->window[THEME_INACTIVE].label_text_color);
parse_color(value, theme->window[SSD_INACTIVE].label_text_color);
}
if (match_glob(key, "window.label.text.justify")) {
theme->window_label_text_justify = parse_justification(value);
@ -795,85 +797,85 @@ entry(struct theme *theme, const char *key, const char *value)
for (enum lab_node_type type = LAB_NODE_BUTTON_FIRST;
type <= LAB_NODE_BUTTON_LAST; type++) {
parse_color(value,
theme->window[THEME_ACTIVE].button_colors[type]);
theme->window[SSD_ACTIVE].button_colors[type]);
}
}
if (match_glob(key, "window.inactive.button.unpressed.image.color")) {
for (enum lab_node_type type = LAB_NODE_BUTTON_FIRST;
type <= LAB_NODE_BUTTON_LAST; type++) {
parse_color(value,
theme->window[THEME_INACTIVE].button_colors[type]);
theme->window[SSD_INACTIVE].button_colors[type]);
}
}
/* individual buttons */
if (match_glob(key, "window.active.button.menu.unpressed.image.color")) {
parse_color(value, theme->window[THEME_ACTIVE]
parse_color(value, theme->window[SSD_ACTIVE]
.button_colors[LAB_NODE_BUTTON_WINDOW_MENU]);
parse_color(value, theme->window[THEME_ACTIVE]
parse_color(value, theme->window[SSD_ACTIVE]
.button_colors[LAB_NODE_BUTTON_WINDOW_ICON]);
}
if (match_glob(key, "window.active.button.iconify.unpressed.image.color")) {
parse_color(value, theme->window[THEME_ACTIVE]
parse_color(value, theme->window[SSD_ACTIVE]
.button_colors[LAB_NODE_BUTTON_ICONIFY]);
}
if (match_glob(key, "window.active.button.max.unpressed.image.color")) {
parse_color(value, theme->window[THEME_ACTIVE]
parse_color(value, theme->window[SSD_ACTIVE]
.button_colors[LAB_NODE_BUTTON_MAXIMIZE]);
}
if (match_glob(key, "window.active.button.shade.unpressed.image.color")) {
parse_color(value, theme->window[THEME_ACTIVE]
parse_color(value, theme->window[SSD_ACTIVE]
.button_colors[LAB_NODE_BUTTON_SHADE]);
}
if (match_glob(key, "window.active.button.desk.unpressed.image.color")) {
parse_color(value, theme->window[THEME_ACTIVE]
parse_color(value, theme->window[SSD_ACTIVE]
.button_colors[LAB_NODE_BUTTON_OMNIPRESENT]);
}
if (match_glob(key, "window.active.button.close.unpressed.image.color")) {
parse_color(value, theme->window[THEME_ACTIVE]
parse_color(value, theme->window[SSD_ACTIVE]
.button_colors[LAB_NODE_BUTTON_CLOSE]);
}
if (match_glob(key, "window.inactive.button.menu.unpressed.image.color")) {
parse_color(value, theme->window[THEME_INACTIVE]
parse_color(value, theme->window[SSD_INACTIVE]
.button_colors[LAB_NODE_BUTTON_WINDOW_MENU]);
parse_color(value, theme->window[THEME_INACTIVE]
parse_color(value, theme->window[SSD_INACTIVE]
.button_colors[LAB_NODE_BUTTON_WINDOW_ICON]);
}
if (match_glob(key, "window.inactive.button.iconify.unpressed.image.color")) {
parse_color(value, theme->window[THEME_INACTIVE]
parse_color(value, theme->window[SSD_INACTIVE]
.button_colors[LAB_NODE_BUTTON_ICONIFY]);
}
if (match_glob(key, "window.inactive.button.max.unpressed.image.color")) {
parse_color(value, theme->window[THEME_INACTIVE]
parse_color(value, theme->window[SSD_INACTIVE]
.button_colors[LAB_NODE_BUTTON_MAXIMIZE]);
}
if (match_glob(key, "window.inactive.button.shade.unpressed.image.color")) {
parse_color(value, theme->window[THEME_INACTIVE]
parse_color(value, theme->window[SSD_INACTIVE]
.button_colors[LAB_NODE_BUTTON_SHADE]);
}
if (match_glob(key, "window.inactive.button.desk.unpressed.image.color")) {
parse_color(value, theme->window[THEME_INACTIVE]
parse_color(value, theme->window[SSD_INACTIVE]
.button_colors[LAB_NODE_BUTTON_OMNIPRESENT]);
}
if (match_glob(key, "window.inactive.button.close.unpressed.image.color")) {
parse_color(value, theme->window[THEME_INACTIVE]
parse_color(value, theme->window[SSD_INACTIVE]
.button_colors[LAB_NODE_BUTTON_CLOSE]);
}
/* window drop-shadows */
if (match_glob(key, "window.active.shadow.size")) {
theme->window[THEME_ACTIVE].shadow_size = get_int_if_positive(
theme->window[SSD_ACTIVE].shadow_size = get_int_if_positive(
value, "window.active.shadow.size");
}
if (match_glob(key, "window.inactive.shadow.size")) {
theme->window[THEME_INACTIVE].shadow_size = get_int_if_positive(
theme->window[SSD_INACTIVE].shadow_size = get_int_if_positive(
value, "window.inactive.shadow.size");
}
if (match_glob(key, "window.active.shadow.color")) {
parse_color(value, theme->window[THEME_ACTIVE].shadow_color);
parse_color(value, theme->window[SSD_ACTIVE].shadow_color);
}
if (match_glob(key, "window.inactive.shadow.color")) {
parse_color(value, theme->window[THEME_INACTIVE].shadow_color);
parse_color(value, theme->window[SSD_INACTIVE].shadow_color);
}
if (match_glob(key, "menu.overlap.x")) {
@ -989,6 +991,12 @@ entry(struct theme *theme, const char *key, const char *value)
get_int_if_positive(value,
"osd.window-switcher.style-classic.item.active.border.width");
}
if (match_glob(key, "osd.window-switcher.style-classic.item.active.border.color")) {
parse_color(value, switcher_classic_theme->item_active_border_color);
}
if (match_glob(key, "osd.window-switcher.style-classic.item.active.bg.color")) {
parse_color(value, switcher_classic_theme->item_active_bg_color);
}
if (match_glob(key, "osd.window-switcher.style-classic.item.icon.size")
|| match_glob(key, "osd.window-switcher.item.icon.size")) {
switcher_classic_theme->item_icon_size =
@ -1388,7 +1396,8 @@ create_titlebar_fill(cairo_pattern_t *pattern, int height)
static void
create_backgrounds(struct theme *theme)
{
for (int active = THEME_INACTIVE; active <= THEME_ACTIVE; active++) {
enum ssd_active_state active;
FOR_EACH_ACTIVE_STATE(active) {
theme->window[active].titlebar_pattern = create_titlebar_pattern(
&theme->window[active].title_bg,
theme->titlebar_height);
@ -1410,7 +1419,8 @@ create_corners(struct theme *theme)
.height = theme->titlebar_height + theme->border_width,
};
for (int active = THEME_INACTIVE; active <= THEME_ACTIVE; active++) {
enum ssd_active_state active;
FOR_EACH_ACTIVE_STATE(active) {
struct rounded_corner_ctx ctx = {
.box = &box,
.radius = rc.corner_radius,
@ -1540,7 +1550,7 @@ shadow_corner_gradient(struct lab_data_buffer *buffer, int visible_size,
}
static void
create_shadow(struct theme *theme, int active)
create_shadow(struct theme *theme, enum ssd_active_state active)
{
/* Size of shadow visible extending beyond the window */
int visible_size = theme->window[active].shadow_size;
@ -1582,8 +1592,8 @@ create_shadow(struct theme *theme, int active)
static void
create_shadows(struct theme *theme)
{
create_shadow(theme, THEME_INACTIVE);
create_shadow(theme, THEME_ACTIVE);
create_shadow(theme, SSD_INACTIVE);
create_shadow(theme, SSD_ACTIVE);
}
static void
@ -1633,6 +1643,31 @@ get_titlebar_height(struct theme *theme)
return h;
}
/* Blend foreground color (with new alpha) with background color */
static void
blend_color_with_bg(float *dst, float *fg, float fg_a, float *bg)
{
/* Guard against zero division */
if (fg[3] <= 0.0f) {
memset(dst, 0, sizeof(float) * 4);
return;
}
/* Redo premultiplication to update foreground alpha */
float new_fg[4] = {
fg[0] / fg[3] * fg_a,
fg[1] / fg[3] * fg_a,
fg[2] / fg[3] * fg_a,
fg_a,
};
/* Blend colors */
dst[0] = new_fg[0] + bg[0] * (1.0f - new_fg[3]);
dst[1] = new_fg[1] + bg[1] * (1.0f - new_fg[3]);
dst[2] = new_fg[2] + bg[2] * (1.0f - new_fg[3]);
dst[3] = new_fg[3] + bg[3] * (1.0f - new_fg[3]);
}
static void
post_processing(struct theme *theme)
{
@ -1643,8 +1678,8 @@ post_processing(struct theme *theme)
theme->titlebar_height = get_titlebar_height(theme);
fill_background_colors(&theme->window[THEME_INACTIVE].title_bg);
fill_background_colors(&theme->window[THEME_ACTIVE].title_bg);
fill_background_colors(&theme->window[SSD_INACTIVE].title_bg);
fill_background_colors(&theme->window[SSD_ACTIVE].title_bg);
theme->menu_item_height = font_height(&rc.font_menuitem)
+ 2 * theme->menu_items_padding_y;
@ -1689,14 +1724,14 @@ post_processing(struct theme *theme)
}
if (theme->menu_border_color[0] == FLT_MIN) {
memcpy(theme->menu_border_color,
theme->window[THEME_ACTIVE].border_color,
theme->window[SSD_ACTIVE].border_color,
sizeof(theme->menu_border_color));
}
/* Inherit OSD settings if not set */
if (theme->osd_bg_color[0] == FLT_MIN) {
memcpy(theme->osd_bg_color,
theme->window[THEME_ACTIVE].title_bg.color,
theme->window[SSD_ACTIVE].title_bg.color,
sizeof(theme->osd_bg_color));
}
if (theme->osd_border_width == INT_MIN) {
@ -1704,7 +1739,7 @@ post_processing(struct theme *theme)
}
if (theme->osd_label_text_color[0] == FLT_MIN) {
memcpy(theme->osd_label_text_color,
theme->window[THEME_ACTIVE].label_text_color,
theme->window[SSD_ACTIVE].label_text_color,
sizeof(theme->osd_label_text_color));
}
if (theme->osd_border_color[0] == FLT_MIN) {
@ -1721,6 +1756,22 @@ post_processing(struct theme *theme)
memcpy(theme->osd_border_color, theme->osd_label_text_color,
sizeof(theme->osd_border_color));
}
if (switcher_classic_theme->item_active_border_color[0] == FLT_MIN) {
blend_color_with_bg(switcher_classic_theme->item_active_border_color,
theme->osd_label_text_color, 0.50, theme->osd_bg_color);
}
if (switcher_classic_theme->item_active_bg_color[0] == FLT_MIN) {
blend_color_with_bg(switcher_classic_theme->item_active_bg_color,
theme->osd_label_text_color, 0.15, theme->osd_bg_color);
}
if (switcher_thumb_theme->item_active_border_color[0] == FLT_MIN) {
blend_color_with_bg(switcher_thumb_theme->item_active_border_color,
theme->osd_label_text_color, 0.50, theme->osd_bg_color);
}
if (switcher_thumb_theme->item_active_bg_color[0] == FLT_MIN) {
blend_color_with_bg(switcher_thumb_theme->item_active_bg_color,
theme->osd_label_text_color, 0.15, theme->osd_bg_color);
}
if (theme->osd_workspace_switcher_boxes_width == 0) {
theme->osd_workspace_switcher_boxes_height = 0;
}
@ -1805,14 +1856,15 @@ theme_finish(struct theme *theme)
type <= LAB_NODE_BUTTON_LAST; type++) {
for (uint8_t state_set = LAB_BS_DEFAULT;
state_set <= LAB_BS_ALL; state_set++) {
destroy_img(&theme->window[THEME_INACTIVE]
destroy_img(&theme->window[SSD_INACTIVE]
.button_imgs[type][state_set]);
destroy_img(&theme->window[THEME_ACTIVE]
destroy_img(&theme->window[SSD_ACTIVE]
.button_imgs[type][state_set]);
}
}
for (int active = THEME_INACTIVE; active <= THEME_ACTIVE; active++) {
enum ssd_active_state active;
FOR_EACH_ACTIVE_STATE(active) {
zfree_pattern(theme->window[active].titlebar_pattern);
zdrop(&theme->window[active].titlebar_fill);
zdrop(&theme->window[active].corner_top_left_normal);

View file

@ -10,8 +10,6 @@ void
view_impl_map(struct view *view)
{
desktop_focus_view(view, /*raise*/ true);
view_update_title(view);
view_update_app_id(view);
if (!view->been_mapped) {
window_rules_apply(view, LAB_WINDOW_RULE_EVENT_ON_FIRST_MAP);
}
@ -36,8 +34,7 @@ view_impl_map(struct view *view)
desktop_update_top_layer_visibility(view->server);
wlr_log(WLR_DEBUG, "[map] identifier=%s, title=%s",
view_get_string_prop(view, "app_id"),
view_get_string_prop(view, "title"));
view->app_id, view->title);
}
void

View file

@ -133,11 +133,11 @@ view_contains_window_type(struct view *view, enum lab_window_type window_type)
bool
view_matches_query(struct view *view, struct view_query *query)
{
if (!query_str_match(query->identifier, view_get_string_prop(view, "app_id"))) {
if (!query_str_match(query->identifier, view->app_id)) {
return false;
}
if (!query_str_match(query->title, view_get_string_prop(view, "title"))) {
if (!query_str_match(query->title, view->title)) {
return false;
}
@ -2161,7 +2161,8 @@ view_snap_to_edge(struct view *view, enum lab_edge edge,
view_set_shade(view, false);
if (lab_edge_is_cardinal(edge) && view->maximized == VIEW_AXIS_NONE) {
if (lab_edge_is_cardinal(edge) && view->maximized == VIEW_AXIS_NONE
&& view->tiled != LAB_EDGE_CENTER) {
enum lab_edge invert_edge = lab_edge_invert(edge);
/* Represents axis of snapping direction */
enum lab_edge parallel_mask = edge | invert_edge;
@ -2384,31 +2385,36 @@ view_has_strut_partial(struct view *view)
view->impl->has_strut_partial(view);
}
/* Note: It is safe to assume that this function never returns NULL */
const char *
view_get_string_prop(struct view *view, const char *prop)
void
view_set_title(struct view *view, const char *title)
{
assert(view);
assert(prop);
if (view->impl->get_string_prop) {
const char *ret = view->impl->get_string_prop(view, prop);
return ret ? ret : "";
}
return "";
if (!title) {
title = "";
}
void
view_update_title(struct view *view)
{
assert(view);
if (!strcmp(view->title, title)) {
return;
}
xstrdup_replace(view->title, title);
ssd_update_title(view->ssd);
wl_signal_emit_mutable(&view->events.new_title, NULL);
}
void
view_update_app_id(struct view *view)
view_set_app_id(struct view *view, const char *app_id)
{
assert(view);
if (!app_id) {
app_id = "";
}
if (!strcmp(view->app_id, app_id)) {
return;
}
xstrdup_replace(view->app_id, app_id);
wl_signal_emit_mutable(&view->events.new_app_id, NULL);
}
@ -2561,6 +2567,9 @@ view_init(struct view *view)
wl_signal_init(&view->events.activated);
wl_signal_init(&view->events.set_icon);
wl_signal_init(&view->events.destroy);
view->title = xstrdup("");
view->app_id = xstrdup("");
}
void
@ -2584,6 +2593,9 @@ view_destroy(struct view *view)
wl_list_remove(&view->set_title.link);
wl_list_remove(&view->destroy.link);
zfree(view->title);
zfree(view->app_id);
if (view->foreign_toplevel) {
foreign_toplevel_destroy(view->foreign_toplevel);
view->foreign_toplevel = NULL;

View file

@ -5,6 +5,7 @@
#include <wlr/types/wlr_fractional_scale_v1.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_xdg_activation_v1.h>
#include <wlr/types/wlr_xdg_dialog_v1.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/types/wlr_xdg_toplevel_icon_v1.h>
#include "buffer.h"
@ -216,7 +217,7 @@ handle_commit(struct wl_listener *listener, void *data)
&& extent.height == view->pending.height) {
wlr_log(WLR_DEBUG, "window geometry for client (%s) "
"appears to be incorrect - ignoring",
view_get_string_prop(view, "app_id"));
view->app_id);
size = extent; /* Use surface extent instead */
}
}
@ -283,9 +284,8 @@ handle_configure_timeout(void *data)
assert(view->pending_configure_serial > 0);
assert(view->pending_configure_timeout);
const char *app_id = view_get_string_prop(view, "app_id");
wlr_log(WLR_INFO, "client (%s) did not respond to configure request "
"in %d ms", app_id, CONFIGURE_TIMEOUT_MS);
"in %d ms", view->app_id, CONFIGURE_TIMEOUT_MS);
wl_event_source_remove(view->pending_configure_timeout);
view->pending_configure_serial = 0;
@ -492,7 +492,9 @@ static void
handle_set_title(struct wl_listener *listener, void *data)
{
struct view *view = wl_container_of(listener, view, set_title);
view_update_title(view);
struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(view);
view_set_title(view, toplevel->title);
}
static void
@ -501,7 +503,9 @@ handle_set_app_id(struct wl_listener *listener, void *data)
struct xdg_toplevel_view *xdg_toplevel_view =
wl_container_of(listener, xdg_toplevel_view, set_app_id);
struct view *view = &xdg_toplevel_view->base;
view_update_app_id(view);
struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(view);
view_set_app_id(view, toplevel->app_id);
}
static void
@ -614,6 +618,18 @@ xdg_toplevel_view_append_children(struct view *self, struct wl_array *children)
}
}
static bool
xdg_toplevel_view_is_modal_dialog(struct view *view)
{
struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(view);
struct wlr_xdg_dialog_v1 *dialog =
wlr_xdg_dialog_v1_try_from_wlr_xdg_toplevel(toplevel);
if (!dialog) {
return false;
}
return dialog->modal;
}
static void
xdg_toplevel_view_set_activated(struct view *view, bool activated)
{
@ -740,31 +756,6 @@ set_initial_position(struct view *view)
view_place_by_policy(view, /* allow_cursor */ true, rc.placement_policy);
}
static const char *
xdg_toplevel_view_get_string_prop(struct view *view, const char *prop)
{
struct xdg_toplevel_view *xdg_view = xdg_toplevel_view_from_view(view);
struct wlr_xdg_toplevel *xdg_toplevel = xdg_view->xdg_surface
? xdg_view->xdg_surface->toplevel
: NULL;
if (!xdg_toplevel) {
/*
* This may happen due to a matchOnce rule when
* a view is destroyed while A-Tab is open. See
* https://github.com/labwc/labwc/issues/1082#issuecomment-1716137180
*/
return "";
}
if (!strcmp(prop, "title")) {
return xdg_toplevel->title ? xdg_toplevel->title : "";
}
if (!strcmp(prop, "app_id")) {
return xdg_toplevel->app_id ? xdg_toplevel->app_id : "";
}
return "";
}
static void
init_foreign_toplevel(struct view *view)
{
@ -884,7 +875,6 @@ xdg_view_get_pid(struct view *view)
static const struct view_impl xdg_toplevel_view_impl = {
.configure = xdg_toplevel_view_configure,
.close = xdg_toplevel_view_close,
.get_string_prop = xdg_toplevel_view_get_string_prop,
.map = xdg_toplevel_view_map,
.set_activated = xdg_toplevel_view_set_activated,
.set_fullscreen = xdg_toplevel_view_set_fullscreen,
@ -894,6 +884,7 @@ static const struct view_impl xdg_toplevel_view_impl = {
.minimize = xdg_toplevel_view_minimize,
.get_root = xdg_toplevel_view_get_root,
.append_children = xdg_toplevel_view_append_children,
.is_modal_dialog = xdg_toplevel_view_is_modal_dialog,
.get_size_hints = xdg_toplevel_view_get_size_hints,
.contains_window_type = xdg_toplevel_view_contains_window_type,
.get_pid = xdg_view_get_pid,
@ -1139,6 +1130,8 @@ xdg_shell_init(struct server *server)
server->xdg_toplevel_icon_set_icon.notify = handle_xdg_toplevel_icon_set_icon;
wl_signal_add(&server->xdg_toplevel_icon_manager->events.set_icon,
&server->xdg_toplevel_icon_set_icon);
wlr_xdg_wm_dialog_v1_create(server->wl_display, 1);
}
void

View file

@ -498,7 +498,8 @@ static void
handle_set_title(struct wl_listener *listener, void *data)
{
struct view *view = wl_container_of(listener, view, set_title);
view_update_title(view);
struct xwayland_view *xwayland_view = xwayland_view_from_view(view);
view_set_title(view, xwayland_view->xwayland_surface->title);
}
static void
@ -507,35 +508,7 @@ handle_set_class(struct wl_listener *listener, void *data)
struct xwayland_view *xwayland_view =
wl_container_of(listener, xwayland_view, set_class);
struct view *view = &xwayland_view->base;
view_update_app_id(view);
}
static void
xwayland_view_close(struct view *view)
{
wlr_xwayland_surface_close(xwayland_surface_from_view(view));
}
static const char *
xwayland_view_get_string_prop(struct view *view, const char *prop)
{
struct xwayland_view *xwayland_view = xwayland_view_from_view(view);
struct wlr_xwayland_surface *xwayland_surface = xwayland_view->xwayland_surface;
if (!xwayland_surface) {
/*
* This may happen due to a matchOnce rule when
* a view is destroyed while A-Tab is open. See
* https://github.com/labwc/labwc/issues/1082#issuecomment-1716137180
*/
return "";
}
if (!strcmp(prop, "title")) {
return xwayland_surface->title ? xwayland_surface->title : "";
}
if (!strcmp(prop, "class")) {
return xwayland_surface->class ? xwayland_surface->class : "";
}
/*
* Use the WM_CLASS 'instance' (1st string) for the app_id. Per
* ICCCM, this is usually "the trailing part of the name used to
@ -545,10 +518,13 @@ xwayland_view_get_string_prop(struct view *view, const char *prop)
* 'instance' except for being capitalized. We want lowercase
* here since we use the app_id for icon lookups.
*/
if (!strcmp(prop, "app_id")) {
return xwayland_surface->instance ? xwayland_surface->instance : "";
view_set_app_id(view, xwayland_view->xwayland_surface->instance);
}
return "";
static void
xwayland_view_close(struct view *view)
{
wlr_xwayland_surface_close(xwayland_surface_from_view(view));
}
static void
@ -1050,7 +1026,6 @@ xwayland_view_get_pid(struct view *view)
static const struct view_impl xwayland_view_impl = {
.configure = xwayland_view_configure,
.close = xwayland_view_close,
.get_string_prop = xwayland_view_get_string_prop,
.map = xwayland_view_map,
.set_activated = xwayland_view_set_activated,
.set_fullscreen = xwayland_view_set_fullscreen,

View file

@ -1,7 +1,7 @@
[wrap-git]
url = https://gitlab.freedesktop.org/wlroots/wlroots.git
revision = 0.19
revision = master
[provide]
dependency_names = wlroots-0.19
wlroots-0.19=wlroots
dependency_names = wlroots-0.20
wlroots-0.20=wlroots