From c8889c7fb7db15cd6dce820f028447290d3cdee9 Mon Sep 17 00:00:00 2001 From: elviosak <33790211+elviosak@users.noreply.github.com> Date: Sat, 9 May 2026 09:33:42 -0300 Subject: [PATCH] add config dialog --- include/common/node-type.h | 1 + include/config/dialog.h | 23 +++++++++ include/config/rcxml.h | 26 ++++++++++ include/labwc.h | 3 ++ src/action.c | 24 ++++----- src/config/dialog.c | 88 +++++++++++++++++++++++++++++++ src/config/meson.build | 1 + src/config/rcxml.c | 103 ++++++++++++++++++++----------------- src/desktop.c | 4 ++ src/input/cursor.c | 6 ++- src/main.c | 5 ++ src/server.c | 5 ++ 12 files changed, 230 insertions(+), 59 deletions(-) create mode 100644 include/config/dialog.h create mode 100644 src/config/dialog.c diff --git a/include/common/node-type.h b/include/common/node-type.h index 52fff3b0..86477e30 100644 --- a/include/common/node-type.h +++ b/include/common/node-type.h @@ -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() */ diff --git a/include/config/dialog.h b/include/config/dialog.h new file mode 100644 index 00000000..b9899d5c --- /dev/null +++ b/include/config/dialog.h @@ -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 */ diff --git a/include/config/rcxml.h b/include/config/rcxml.h index 3ef7bd67..13cdf52c 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -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 */ diff --git a/include/labwc.h b/include/labwc.h index dc4c1311..9f2265ac 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -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 diff --git a/src/action.c b/src/action.c index daf8fb30..a1be9653 100644 --- a/src/action.c +++ b/src/action.c @@ -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; } diff --git a/src/config/dialog.c b/src/config/dialog.c new file mode 100644 index 00000000..d300bd9b --- /dev/null +++ b/src/config/dialog.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0-only +#define _POSIX_C_SOURCE 200809L +#include "config/dialog.h" +#include +#include +#include +#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; + } + } +} diff --git a/src/config/meson.build b/src/config/meson.build index 70b1baa3..8a34c12a 100644 --- a/src/config/meson.build +++ b/src/config/meson.build @@ -1,4 +1,5 @@ labwc_sources += files( + 'dialog.c', 'rcxml.c', 'keybind.c', 'session.c', diff --git a/src/config/rcxml.c b/src/config/rcxml.c index d0bb76db..07877f90 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -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, " must contain one colon"); + lab_wlr_log(WLR_ERROR, " 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(®ion->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 " "<%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, " is only" + lab_wlr_log(WLR_ERROR, " 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, " is deprecated. " + lab_wlr_log(WLR_ERROR, " is deprecated. " "Use 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, " is deprecated." + lab_wlr_log(WLR_ERROR, " is deprecated." " Use "); } 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, " is deprecated." + lab_wlr_log(WLR_ERROR, " is deprecated." " Use "); } 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 : '%s'", content); } else { rc.window_switcher.workspace_filter = ret ? CYCLE_WORKSPACE_ALL : CYCLE_WORKSPACE_CURRENT; } - wlr_log(WLR_ERROR, " is deprecated." + lab_wlr_log(WLR_ERROR, " is deprecated." " Use 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, " should not be child of "); + lab_wlr_log(WLR_ERROR, " should not be child of "); /* 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, " is deprecated." + lab_wlr_log(WLR_ERROR, " is deprecated." " Use "); } else if (!strcasecmp(nodename, "cycleViewPreview.core")) { set_bool(content, &rc.window_switcher.preview); - wlr_log(WLR_ERROR, " is deprecated." + lab_wlr_log(WLR_ERROR, " is deprecated." " Use "); } else if (!strcasecmp(nodename, "cycleViewOutlines.core")) { set_bool(content, &rc.window_switcher.outlines); - wlr_log(WLR_ERROR, " is deprecated." + lab_wlr_log(WLR_ERROR, " is deprecated." " Use "); } 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 "); + lab_wlr_log(WLR_ERROR, "Invalid value for "); } } 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 " ""); } } @@ -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, " is deprecated" + lab_wlr_log(WLR_ERROR, " is deprecated" " and overwrites ." " Use only ."); 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(®ion->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(); diff --git a/src/desktop.c b/src/desktop.c index 57ef9e3c..e105ae79 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -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 */ diff --git a/src/input/cursor.c b/src/input/cursor.c index e8ac7d96..f7380713 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -17,6 +17,7 @@ #include #include #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; } diff --git a/src/main.c b/src/main.c index 373f4480..0d2a6390 100644 --- a/src/main.c +++ b/src/main.c @@ -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, diff --git a/src/server.c b/src/server.c index c792d8b4..403ad6c4 100644 --- a/src/server.c +++ b/src/server.c @@ -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