add config dialog

This commit is contained in:
elviosak 2026-05-09 09:33:42 -03:00
parent ff2f243eb1
commit c8889c7fb7
12 changed files with 230 additions and 59 deletions

View file

@ -50,6 +50,7 @@ enum lab_node_type {
LAB_NODE_CYCLE_OSD_ITEM,
LAB_NODE_LAYER_SURFACE,
LAB_NODE_UNMANAGED,
LAB_NODE_CONFIG_DIALOG,
LAB_NODE_ALL,
/* translated to LAB_NODE_CLIENT by get_cursor_context() */

23
include/config/dialog.h Normal file
View file

@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef LAB_DIALOG_H
#define LAB_DIALOG_H
struct wlr_scene_node;
/**
* dialog_create_callback - creates the config dialog,
* used with wl_event_loop_add_idle.
*
* @data: Ignored.
*/
void dialog_create_callback(void *data);
/**
* dialog_destroy - destroys a dialog that is a child of
* server.dialog_tree, destroys all children if NULL is provided.
*
* @dialog: Dialog to be destroyed
*/
void dialog_destroy(struct wlr_scene_node *dialog);
#endif /* LAB_DIALOG_H */

View file

@ -17,6 +17,28 @@
/* max of one button of each type (no repeats) */
#define TITLE_BUTTONS_MAX ((LAB_NODE_BUTTON_LAST + 1) - LAB_NODE_BUTTON_FIRST)
struct log_item {
char *text;
struct wl_list link; /* struct rcxml.error_logs */
};
/**
* Calls wlr_log(verb, fmt, ...) and also saves it in
* rc.error_logs for displaying it in a dialog later.
* Only sets rc.has_error when WLR_ERROR is used to log
* other relevant info like the config file.
*/
#define lab_wlr_log(verb, fmt, ...) \
do { \
wlr_log(verb, fmt, ##__VA_ARGS__); \
if (verb == WLR_ERROR) { \
rc.has_error = true; \
} \
struct log_item *log_item = znew(*log_item); \
log_item->text = strdup_printf(fmt, ##__VA_ARGS__); \
wl_list_append(&rc.error_logs, &log_item->link); \
} while (0)
enum adaptive_sync_mode {
LAB_ADAPTIVE_SYNC_DISABLED,
LAB_ADAPTIVE_SYNC_ENABLED,
@ -213,6 +235,10 @@ struct rcxml {
float mag_scale;
float mag_increment;
bool mag_filter;
/* Error log */
bool has_error;
struct wl_list error_logs; /* struct log_item.link */
};
/* defined in main.c */

View file

@ -230,6 +230,9 @@ struct server {
/* Tree for all non-layer xdg/xwayland-shell surfaces */
struct wlr_scene_tree *workspace_tree;
/* Tree for error dialog */
struct wlr_scene_tree *dialog_tree;
/*
* Popups need to be rendered above always-on-top views, so we reparent
* them to this dedicated tree

View file

@ -318,7 +318,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
|| action->type == ACTION_TYPE_SNAP_TO_EDGE);
enum lab_edge edge = lab_edge_parse(content, tiled, /*any*/ false);
if (edge == LAB_EDGE_NONE) {
wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
lab_wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
action_names[action->type], argument, content);
} else {
action_arg_add_int(action, argument, edge);
@ -347,7 +347,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
} else if (!strcasecmp(content, "current")) {
action_arg_add_int(action, argument, CYCLE_WORKSPACE_CURRENT);
} else {
wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
lab_wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
action_names[action->type], argument, content);
}
goto cleanup;
@ -360,7 +360,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
} else if (!strcasecmp(content, "focused")) {
action_arg_add_int(action, argument, CYCLE_OUTPUT_FOCUSED);
} else {
wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
lab_wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
action_names[action->type], argument, content);
}
goto cleanup;
@ -371,7 +371,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
} else if (!strcasecmp(content, "current")) {
action_arg_add_int(action, argument, CYCLE_APP_ID_CURRENT);
} else {
wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
lab_wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
action_names[action->type], argument, content);
}
goto cleanup;
@ -401,7 +401,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
if (!strcmp(argument, "direction")) {
enum view_axis axis = view_axis_parse(content);
if (axis == VIEW_AXIS_NONE || axis == VIEW_AXIS_INVALID) {
wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
lab_wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
action_names[action->type], argument, content);
} else {
action_arg_add_int(action, argument, axis);
@ -415,7 +415,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
if (mode != LAB_SSD_MODE_INVALID) {
action_arg_add_int(action, argument, mode);
} else {
wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
lab_wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
action_names[action->type], argument, content);
}
goto cleanup;
@ -430,7 +430,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
enum lab_edge edge = lab_edge_parse(content,
/*tiled*/ true, /*any*/ false);
if (edge == LAB_EDGE_NONE || edge == LAB_EDGE_CENTER) {
wlr_log(WLR_ERROR,
lab_wlr_log(WLR_ERROR,
"Invalid argument for action %s: '%s' (%s)",
action_names[action->type], argument, content);
} else {
@ -497,7 +497,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
enum lab_edge edge = lab_edge_parse(content,
/*tiled*/ false, /*any*/ false);
if (edge == LAB_EDGE_NONE) {
wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
lab_wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
action_names[action->type], argument, content);
} else {
action_arg_add_int(action, argument, edge);
@ -521,7 +521,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
enum lab_placement_policy policy =
view_placement_parse(content);
if (policy == LAB_PLACE_INVALID) {
wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
lab_wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
action_names[action->type], argument, content);
} else {
action_arg_add_int(action, argument, policy);
@ -542,7 +542,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
goto cleanup;
}
wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s'",
lab_wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s'",
action_names[action->type], argument);
cleanup:
@ -557,7 +557,7 @@ action_type_from_str(const char *action_name)
return i;
}
}
wlr_log(WLR_ERROR, "Invalid action: %s", action_name);
lab_wlr_log(WLR_ERROR, "Invalid action: %s", action_name);
return ACTION_TYPE_INVALID;
}
@ -664,7 +664,7 @@ action_is_valid(struct action *action)
return true;
}
wlr_log(WLR_ERROR, "Missing required argument for %s: %s",
lab_wlr_log(WLR_ERROR, "Missing required argument for %s: %s",
action_names[action->type], arg_name);
return false;
}

88
src/config/dialog.c Normal file
View file

@ -0,0 +1,88 @@
// SPDX-License-Identifier: GPL-2.0-only
#define _POSIX_C_SOURCE 200809L
#include "config/dialog.h"
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/util/box.h>
#include "common/lab-scene-rect.h"
#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 "theme.h"
static void
dialog_create(void)
{
struct theme *theme = rc.theme;
float *text_color = theme->osd_label_text_color;
float *bg_color = theme->osd_bg_color;
struct output *output = output_nearest_to_cursor();
struct wlr_box output_box = output_usable_area_in_layout_coords(output);
int border = theme->osd_border_width;
int padding = rc.font_osd.size / 2; /* used for padding and line gap */
int x = output_box.x + output_box.width / 4;
int y = output_box.y + output_box.height / 10;
int width = output_box.width * 0.5;
int height = output_box.height * 0.8;
int inner_w = width - 2 * border;
int inner_h = height - 2 * border;
int dx = border + padding;
int dy = border + padding;
struct wlr_scene_tree *dialog = lab_wlr_scene_tree_create(server.dialog_tree);
node_descriptor_create(&dialog->node, LAB_NODE_CONFIG_DIALOG, NULL, NULL);
wlr_scene_node_set_position(&dialog->node, x, y);
struct wlr_scene_rect *border_rect = lab_wlr_scene_rect_create(dialog, width, height,
theme->osd_border_color);
wlr_scene_node_set_position(&border_rect->node, 0, 0);
struct wlr_scene_tree *dialog_tree = lab_wlr_scene_tree_create(dialog);
wlr_scene_node_set_position(&dialog_tree->node, border, border);
struct wlr_scene_rect *dialog_rect = lab_wlr_scene_rect_create(dialog_tree,
inner_w, inner_h, bg_color);
wlr_scene_node_set_position(&dialog_rect->node, 0, 0);
struct scaled_font_buffer *font_buf = scaled_font_buffer_create(dialog_tree);
struct font title_font = rc.font_osd;
title_font.size *= 1.15;
scaled_font_buffer_update(font_buf, "Config log (Click to dismiss)", inner_w,
&title_font, text_color, bg_color);
wlr_scene_node_set_position(&font_buf->scene_buffer->node, dx, dy);
dy += font_buf->height + padding;
struct log_item *item;
wl_list_for_each(item, &rc.error_logs, link) {
font_buf = scaled_font_buffer_create(dialog_tree);
scaled_font_buffer_update(font_buf, item->text, inner_w, &rc.font_osd,
text_color, bg_color);
wlr_scene_node_set_position(&font_buf->scene_buffer->node, dx, dy);
dy += font_buf->height + padding;
}
wlr_scene_node_set_enabled(&server.dialog_tree->node, true);
}
void
dialog_create_callback(void *data)
{
dialog_create();
}
void
dialog_destroy(struct wlr_scene_node *dialog)
{
struct wlr_scene_node *child, *tmp;
wl_list_for_each_safe(child, tmp, &server.dialog_tree->children, link) {
if (!dialog) {
wlr_scene_node_destroy(child);
continue;
}
if (dialog == child) {
wlr_scene_node_destroy(child);
break;
}
}
}

View file

@ -1,4 +1,5 @@
labwc_sources += files(
'dialog.c',
'rcxml.c',
'keybind.c',
'session.c',

View file

@ -23,6 +23,7 @@
#include "common/string-helpers.h"
#include "common/xml.h"
#include "config/default-bindings.h"
#include "config/dialog.h"
#include "config/keybind.h"
#include "config/libinput.h"
#include "config/mousebind.h"
@ -168,7 +169,7 @@ fill_section(const char *content, enum lab_node_type *buttons, int *count,
#if HAVE_LIBSFDO
type = LAB_NODE_BUTTON_WINDOW_ICON;
#else
wlr_log(WLR_ERROR, "libsfdo is not linked. "
lab_wlr_log(WLR_ERROR, "libsfdo is not linked. "
"Replacing 'icon' in titlebar layout with 'menu'.");
type = LAB_NODE_BUTTON_WINDOW_MENU;
#endif
@ -185,7 +186,7 @@ fill_section(const char *content, enum lab_node_type *buttons, int *count,
} else if (!strcmp(identifier, "desk")) {
type = LAB_NODE_BUTTON_OMNIPRESENT;
} else {
wlr_log(WLR_ERROR, "invalid titleLayout identifier '%s'",
lab_wlr_log(WLR_ERROR, "invalid titleLayout identifier '%s'",
identifier);
continue;
}
@ -194,7 +195,7 @@ fill_section(const char *content, enum lab_node_type *buttons, int *count,
/* We no longer need this check, but let's keep it just in case */
if (*found_buttons & (1 << type)) {
wlr_log(WLR_ERROR, "ignoring duplicated button type '%s'",
lab_wlr_log(WLR_ERROR, "ignoring duplicated button type '%s'",
identifier);
continue;
}
@ -223,7 +224,7 @@ fill_title_layout(const char *content)
gchar **parts = g_strsplit(content, ":", -1);
if (g_strv_length(parts) != 2) {
wlr_log(WLR_ERROR, "<titlebar><layout> must contain one colon");
lab_wlr_log(WLR_ERROR, "<titlebar><layout> must contain one colon");
goto err;
}
@ -259,7 +260,7 @@ fill_usable_area_override(xmlNode *node)
} else if (!strcmp(key, "bottom")) {
usable_area_override->margin.bottom = atoi(content);
} else {
wlr_log(WLR_ERROR, "Unexpected data usable-area-override "
lab_wlr_log(WLR_ERROR, "Unexpected data usable-area-override "
"parser: %s=\"%s\"", key, content);
}
}
@ -324,7 +325,7 @@ fill_window_rule(xmlNode *node)
} else if (!strcasecmp(content, "server")) {
window_rule->icon_prefer_client = LAB_PROP_FALSE;
} else {
wlr_log(WLR_ERROR,
lab_wlr_log(WLR_ERROR,
"Invalid value for window rule property 'iconPriority'");
}
} else if (!strcasecmp(key, "skipTaskbar")) {
@ -409,7 +410,7 @@ fill_region(xmlNode *node)
xstrdup_replace(region->name, content);
} else if (strstr("xywidtheight", key)
&& !strchr(content, '%')) {
wlr_log(WLR_ERROR, "Removing invalid region "
lab_wlr_log(WLR_ERROR, "Removing invalid region "
"'%s': %s='%s' misses a trailing %%",
region->name, key, content);
wl_list_remove(&region->link);
@ -425,7 +426,7 @@ fill_region(xmlNode *node)
} else if (!strcmp(key, "height")) {
region->percentage.height = atoi(content);
} else {
wlr_log(WLR_ERROR, "Unexpected data in region "
lab_wlr_log(WLR_ERROR, "Unexpected data in region "
"parser: %s=\"%s\"", key, content);
}
}
@ -592,7 +593,7 @@ fill_keybind(xmlNode *node)
if (lab_xml_get_string(node, "key", keyname, sizeof(keyname))) {
keybind = keybind_create(keyname);
if (!keybind) {
wlr_log(WLR_ERROR, "Invalid keybind: %s", keyname);
lab_wlr_log(WLR_ERROR, "Invalid keybind: %s", keyname);
}
}
if (!keybind) {
@ -672,7 +673,7 @@ fill_touch(xmlNode *node)
} else if (!strcasecmp(key, "mouseEmulation")) {
set_bool(content, &touch_config->force_mouse_emulation);
} else {
wlr_log(WLR_ERROR, "Unexpected data in touch parser: %s=\"%s\"",
lab_wlr_log(WLR_ERROR, "Unexpected data in touch parser: %s=\"%s\"",
key, content);
}
}
@ -688,14 +689,14 @@ fill_tablet_button_map(xmlNode *node)
if (lab_xml_get_string(node, "button", buf, sizeof(buf))) {
map_from = tablet_button_from_str(buf);
} else {
wlr_log(WLR_ERROR, "Invalid 'button' argument for tablet button mapping");
lab_wlr_log(WLR_ERROR, "Invalid 'button' argument for tablet button mapping");
return;
}
if (lab_xml_get_string(node, "to", buf, sizeof(buf))) {
map_to = mousebind_button_from_str(buf, NULL);
} else {
wlr_log(WLR_ERROR, "Invalid 'to' argument for tablet button mapping");
lab_wlr_log(WLR_ERROR, "Invalid 'to' argument for tablet button mapping");
return;
}
@ -754,7 +755,7 @@ fill_libinput_category(xmlNode *node)
char *key, *content;
LAB_XML_FOR_EACH(node, child, key, content) {
if (string_null_or_empty(content)) {
wlr_log(WLR_ERROR, "Empty string is not allowed for "
lab_wlr_log(WLR_ERROR, "Empty string is not allowed for "
"<libinput><device><%s>. Ignoring.", key);
continue;
}
@ -801,7 +802,7 @@ fill_libinput_category(xmlNode *node)
category->tap_button_map =
LIBINPUT_CONFIG_TAP_MAP_LMR;
} else {
wlr_log(WLR_ERROR, "invalid tapButtonMap");
lab_wlr_log(WLR_ERROR, "invalid tapButtonMap");
}
} else if (!strcasecmp(key, "tapAndDrag")) {
int ret = parse_bool(content, -1);
@ -846,7 +847,7 @@ fill_libinput_category(xmlNode *node)
: LIBINPUT_CONFIG_3FG_DRAG_DISABLED;
}
#else
wlr_log(WLR_ERROR, "<threeFingerDrag> is only"
lab_wlr_log(WLR_ERROR, "<threeFingerDrag> is only"
" supported in libinput >= 1.28");
#endif
} else if (!strcasecmp(key, "accelProfile")) {
@ -879,7 +880,7 @@ fill_libinput_category(xmlNode *node)
category->click_method =
LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
} else {
wlr_log(WLR_ERROR, "invalid clickMethod");
lab_wlr_log(WLR_ERROR, "invalid clickMethod");
}
} else if (!strcasecmp(key, "scrollMethod")) {
if (!strcasecmp(content, "none")) {
@ -895,14 +896,14 @@ fill_libinput_category(xmlNode *node)
category->scroll_method =
LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
} else {
wlr_log(WLR_ERROR, "invalid scrollMethod");
lab_wlr_log(WLR_ERROR, "invalid scrollMethod");
}
} else if (!strcasecmp(key, "scrollButton")) {
int button = atoi(content);
if (button != 0) {
category->scroll_button = button;
} else {
wlr_log(WLR_ERROR, "invalid scrollButton");
lab_wlr_log(WLR_ERROR, "invalid scrollButton");
}
} else if (!strcasecmp(key, "sendEventsMode")) {
category->send_events_mode =
@ -918,7 +919,7 @@ fill_libinput_category(xmlNode *node)
mat[i] = strtof(elements[i], &end_str);
if (errno == ERANGE || *end_str != '\0' || i == 6
|| *elements[i] == '\0') {
wlr_log(WLR_ERROR, "invalid calibration "
lab_wlr_log(WLR_ERROR, "invalid calibration "
"matrix element %s (index %d), "
"expect six floats", elements[i], i);
category->have_calibration_matrix = false;
@ -927,7 +928,7 @@ fill_libinput_category(xmlNode *node)
}
}
if (i != 6 && category->have_calibration_matrix) {
wlr_log(WLR_ERROR, "wrong number of calibration "
lab_wlr_log(WLR_ERROR, "wrong number of calibration "
"matrix elements, expected 6, got %d", i);
category->have_calibration_matrix = false;
}
@ -1128,7 +1129,7 @@ entry(xmlNode *node, char *nodename, char *content)
return true;
} else if (str_space_only(content)) {
wlr_log(WLR_ERROR, "Empty string is not allowed for %s. "
lab_wlr_log(WLR_ERROR, "Empty string is not allowed for %s. "
"Ignoring.", nodename);
/* handle non-empty leaf nodes */
@ -1203,7 +1204,7 @@ entry(xmlNode *node, char *nodename, char *content)
if (doubleclick_time_parsed > 0) {
rc.doubleclick_time = doubleclick_time_parsed;
} else {
wlr_log(WLR_ERROR, "invalid doubleClickTime");
lab_wlr_log(WLR_ERROR, "invalid doubleClickTime");
}
} else if (!strcasecmp(nodename, "scrollFactor.mouse")) {
/* This is deprecated. Show an error message in post_processing() */
@ -1235,7 +1236,7 @@ entry(xmlNode *node, char *nodename, char *content)
} else if (!strcasecmp(nodename, "range.snapping")) {
rc.snap_edge_range_inner = atoi(content);
rc.snap_edge_range_outer = atoi(content);
wlr_log(WLR_ERROR, "<snapping><range> is deprecated. "
lab_wlr_log(WLR_ERROR, "<snapping><range> is deprecated. "
"Use <snapping><range inner=\"\" outer=\"\"> instead.");
} else if (!strcasecmp(nodename, "inner.range.snapping")) {
rc.snap_edge_range_inner = atoi(content);
@ -1261,7 +1262,7 @@ entry(xmlNode *node, char *nodename, char *content)
} else if (!strcasecmp(content, "never")) {
rc.snap_tiling_events_mode = LAB_TILING_EVENTS_NEVER;
} else {
wlr_log(WLR_ERROR, "ignoring invalid value for notifyClient");
lab_wlr_log(WLR_ERROR, "ignoring invalid value for notifyClient");
}
/*
@ -1279,7 +1280,7 @@ entry(xmlNode *node, char *nodename, char *content)
} else if (!strcasecmp(content, "thumbnail")) {
rc.window_switcher.osd.style = CYCLE_OSD_STYLE_THUMBNAIL;
} else {
wlr_log(WLR_ERROR, "Invalid windowSwitcher style '%s': "
lab_wlr_log(WLR_ERROR, "Invalid windowSwitcher style '%s': "
"should be one of classic|thumbnail", content);
}
} else if (!strcasecmp(nodename, "output.osd.windowSwitcher")) {
@ -1290,7 +1291,7 @@ entry(xmlNode *node, char *nodename, char *content)
} else if (!strcasecmp(content, "focused")) {
rc.window_switcher.osd.output_filter = CYCLE_OUTPUT_FOCUSED;
} else {
wlr_log(WLR_ERROR, "Invalid windowSwitcher output '%s': "
lab_wlr_log(WLR_ERROR, "Invalid windowSwitcher output '%s': "
"should be one of all|focused|cursor", content);
}
} else if (!strcasecmp(nodename, "order.windowSwitcher")) {
@ -1299,14 +1300,14 @@ entry(xmlNode *node, char *nodename, char *content)
} else if (!strcasecmp(content, "age")) {
rc.window_switcher.order = WINDOW_SWITCHER_ORDER_AGE;
} else {
wlr_log(WLR_ERROR, "Invalid windowSwitcher order '%s': "
lab_wlr_log(WLR_ERROR, "Invalid windowSwitcher order '%s': "
"should be one of focus|age", content);
}
/* The following two are for backward compatibility only. */
} else if (!strcasecmp(nodename, "show.windowSwitcher")) {
set_bool(content, &rc.window_switcher.osd.show);
wlr_log(WLR_ERROR, "<windowSwitcher show=\"\" /> is deprecated."
lab_wlr_log(WLR_ERROR, "<windowSwitcher show=\"\" /> is deprecated."
" Use <windowSwitcher><osd show=\"\" />");
} else if (!strcasecmp(nodename, "style.windowSwitcher")) {
if (!strcasecmp(content, "classic")) {
@ -1314,7 +1315,7 @@ entry(xmlNode *node, char *nodename, char *content)
} else if (!strcasecmp(content, "thumbnail")) {
rc.window_switcher.osd.style = CYCLE_OSD_STYLE_THUMBNAIL;
}
wlr_log(WLR_ERROR, "<windowSwitcher style=\"\" /> is deprecated."
lab_wlr_log(WLR_ERROR, "<windowSwitcher style=\"\" /> is deprecated."
" Use <windowSwitcher><osd style=\"\" />");
} else if (!strcasecmp(nodename, "preview.windowSwitcher")) {
@ -1324,20 +1325,20 @@ entry(xmlNode *node, char *nodename, char *content)
} else if (!strcasecmp(nodename, "allWorkspaces.windowSwitcher")) {
int ret = parse_bool(content, -1);
if (ret < 0) {
wlr_log(WLR_ERROR, "Invalid value for <windowSwitcher"
lab_wlr_log(WLR_ERROR, "Invalid value for <windowSwitcher"
" allWorkspaces=\"\">: '%s'", content);
} else {
rc.window_switcher.workspace_filter = ret ?
CYCLE_WORKSPACE_ALL : CYCLE_WORKSPACE_CURRENT;
}
wlr_log(WLR_ERROR, "<windowSwitcher allWorkspaces=\"\" /> is deprecated."
lab_wlr_log(WLR_ERROR, "<windowSwitcher allWorkspaces=\"\" /> is deprecated."
" Use <action name=\"NextWindow\" workspace=\"\"> instead.");
} 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")) {
wlr_log(WLR_ERROR, "<windowSwitcher> should not be child of <core>");
lab_wlr_log(WLR_ERROR, "<windowSwitcher> should not be child of <core>");
/* The following three are for backward compatibility only */
} else if (!strcasecmp(nodename, "show.windowSwitcher.core")) {
@ -1350,15 +1351,15 @@ entry(xmlNode *node, char *nodename, char *content)
/* The following three are for backward compatibility only */
} else if (!strcasecmp(nodename, "cycleViewOSD.core")) {
set_bool(content, &rc.window_switcher.osd.show);
wlr_log(WLR_ERROR, "<cycleViewOSD> is deprecated."
lab_wlr_log(WLR_ERROR, "<cycleViewOSD> is deprecated."
" Use <windowSwitcher show=\"\" />");
} else if (!strcasecmp(nodename, "cycleViewPreview.core")) {
set_bool(content, &rc.window_switcher.preview);
wlr_log(WLR_ERROR, "<cycleViewPreview> is deprecated."
lab_wlr_log(WLR_ERROR, "<cycleViewPreview> is deprecated."
" Use <windowSwitcher preview=\"\" />");
} else if (!strcasecmp(nodename, "cycleViewOutlines.core")) {
set_bool(content, &rc.window_switcher.outlines);
wlr_log(WLR_ERROR, "<cycleViewOutlines> is deprecated."
lab_wlr_log(WLR_ERROR, "<cycleViewOutlines> is deprecated."
" Use <windowSwitcher outlines=\"\" />");
} else if (!strcasecmp(nodename, "name.names.desktops")) {
@ -1379,7 +1380,7 @@ entry(xmlNode *node, char *nodename, char *content)
} else if (!strcasecmp(content, "Nonpixel")) {
rc.resize_indicator = LAB_RESIZE_INDICATOR_NON_PIXEL;
} else {
wlr_log(WLR_ERROR, "Invalid value for <resize popupShow />");
lab_wlr_log(WLR_ERROR, "Invalid value for <resize popupShow />");
}
} else if (!strcasecmp(nodename, "drawContents.resize")) {
set_bool(content, &rc.resize_draw_contents);
@ -1435,7 +1436,7 @@ entry(xmlNode *node, char *nodename, char *content)
if (iface_id) {
rc.allowed_interfaces |= iface_id;
} else {
wlr_log(WLR_ERROR, "invalid value for "
lab_wlr_log(WLR_ERROR, "invalid value for "
"<privilegedInterfaces><allow>");
}
}
@ -1464,7 +1465,7 @@ rcxml_parse_xml(struct buf *b)
int options = 0;
xmlDoc *d = xmlReadMemory(b->data, b->len, NULL, NULL, options);
if (!d) {
wlr_log(WLR_ERROR, "error parsing config file");
lab_wlr_log(WLR_ERROR, "error parsing config file");
return;
}
xmlNode *root = xmlDocGetRootElement(d);
@ -1495,6 +1496,7 @@ rcxml_init(void)
wl_list_init(&rc.mousebinds);
wl_list_init(&rc.libinput_categories);
wl_list_init(&rc.workspace_config.workspaces);
wl_list_init(&rc.error_logs);
wl_list_init(&rc.regions);
wl_list_init(&rc.window_switcher.osd.fields);
wl_list_init(&rc.window_rules);
@ -1843,7 +1845,7 @@ post_processing(void)
assert(l && libinput_category_get_default() == l);
}
if (mouse_scroll_factor >= 0) {
wlr_log(WLR_ERROR, "<mouse><scrollFactor> is deprecated"
lab_wlr_log(WLR_ERROR, "<mouse><scrollFactor> is deprecated"
" and overwrites <libinput><scrollFactor>."
" Use only <libinput><scrollFactor>.");
struct libinput_category *l;
@ -1903,7 +1905,7 @@ validate_actions(void)
if (!action_is_valid(action)) {
wl_list_remove(&action->link);
action_free(action);
wlr_log(WLR_ERROR, "Removed invalid keybind action");
lab_wlr_log(WLR_ERROR, "Removed invalid keybind action");
}
}
}
@ -1914,7 +1916,7 @@ validate_actions(void)
if (!action_is_valid(action)) {
wl_list_remove(&action->link);
action_free(action);
wlr_log(WLR_ERROR, "Removed invalid mousebind action");
lab_wlr_log(WLR_ERROR, "Removed invalid mousebind action");
}
}
}
@ -1925,7 +1927,7 @@ validate_actions(void)
if (!action_is_valid(action)) {
wl_list_remove(&action->link);
action_free(action);
wlr_log(WLR_ERROR, "Removed invalid window rule action");
lab_wlr_log(WLR_ERROR, "Removed invalid window rule action");
}
}
}
@ -1944,7 +1946,7 @@ validate(void)
|| box.width <= 0 || box.width > 100
|| box.height <= 0 || box.height > 100;
if (invalid) {
wlr_log(WLR_ERROR,
lab_wlr_log(WLR_ERROR,
"Removing invalid region '%s': %d%% x %d%% @ %d%%,%d%%",
region->name, box.width, box.height, box.x, box.y);
wl_list_remove(&region->link);
@ -1958,7 +1960,7 @@ validate(void)
wl_list_for_each_safe(rule, rule_tmp, &rc.window_rules, link) {
if (!rule->identifier && !rule->title && rule->window_type < 0
&& !rule->sandbox_engine && !rule->sandbox_app_id) {
wlr_log(WLR_ERROR, "Deleting rule %p as it has no criteria", rule);
lab_wlr_log(WLR_ERROR, "Deleting rule %p as it has no criteria", rule);
rule_destroy(rule);
}
}
@ -1971,7 +1973,7 @@ validate(void)
wl_list_for_each_safe(field, field_tmp, &rc.window_switcher.osd.fields, link) {
field_width_sum += field->width;
if (!cycle_osd_field_is_valid(field) || field_width_sum > 100) {
wlr_log(WLR_ERROR, "Deleting invalid window switcher field %p", field);
lab_wlr_log(WLR_ERROR, "Deleting invalid window switcher field %p", field);
wl_list_remove(&field->link);
cycle_osd_field_free(field);
}
@ -2017,7 +2019,7 @@ rcxml_read(const char *filename)
continue;
}
wlr_log(WLR_INFO, "read config file %s", path->string);
lab_wlr_log(WLR_INFO, "read config file %s", path->string);
rcxml_parse_xml(&b);
buf_reset(&b);
@ -2092,6 +2094,15 @@ rcxml_finish(void)
zfree(w);
}
struct log_item *log_item, *log_tmp;
wl_list_for_each_safe(log_item, log_tmp, &rc.error_logs, link) {
wl_list_remove(&log_item->link);
zfree(log_item->text);
zfree(log_item);
}
rc.has_error = false;
dialog_destroy(NULL);
regions_destroy(NULL, &rc.regions);
clear_window_switcher_fields();

View file

@ -426,6 +426,10 @@ get_cursor_context(void)
ret.type = desc->type;
}
return ret;
case LAB_NODE_CONFIG_DIALOG:
ret.type = LAB_NODE_CONFIG_DIALOG;
ret.node = node;
return ret;
default:
/* Other node types are not attached a scene node */

View file

@ -17,6 +17,7 @@
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/region.h>
#include "action.h"
#include "config/dialog.h"
#include "common/macros.h"
#include "common/mem.h"
#include "config/mousebind.h"
@ -1236,7 +1237,10 @@ cursor_process_button_release(struct seat *seat, uint32_t button,
}
return notify;
}
if (ctx.type == LAB_NODE_CONFIG_DIALOG) {
dialog_destroy(ctx.node);
return notify;
}
if (server.input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
return notify;
}

View file

@ -7,6 +7,7 @@
#include "common/fd-util.h"
#include "common/font.h"
#include "common/spawn.h"
#include "config/dialog.h"
#include "config/rcxml.h"
#include "config/session.h"
#include "labwc.h"
@ -263,6 +264,10 @@ main(int argc, char *argv[])
menu_init();
if (rc.has_error) {
wl_event_loop_add_idle(server.wl_event_loop, dialog_create_callback, NULL);
}
/* Delay startup of applications until the event loop is ready */
struct idle_ctx idle_ctx = {
.primary_client = primary_client,

View file

@ -54,6 +54,7 @@
#include "common/macros.h"
#include "common/mem.h"
#include "common/scene-helpers.h"
#include "config/dialog.h"
#include "config/rcxml.h"
#include "config/session.h"
#include "decorations.h"
@ -118,6 +119,9 @@ reload_config_and_theme(void)
resize_indicator_reconfigure();
kde_server_decoration_update_default();
workspaces_reconfigure();
if (rc.has_error) {
wl_event_loop_add_idle(server.wl_event_loop, dialog_create_callback, NULL);
}
}
static int
@ -616,6 +620,7 @@ server_init(void)
server.wl_display, 1, server.renderer);
server.workspace_tree = lab_wlr_scene_tree_create(&server.scene->tree);
server.dialog_tree = lab_wlr_scene_tree_create(&server.scene->tree);
server.xdg_popup_tree = lab_wlr_scene_tree_create(&server.scene->tree);
#if HAVE_XWAYLAND
// Creating/setting this is harmless when xwayland support is built-in