mirror of
https://github.com/labwc/labwc.git
synced 2026-04-11 08:21:13 -04:00
config: support rc.yaml
Based on @johanmalm's work. This adds libyaml as an optional dependency.
This commit is contained in:
parent
a0a9f977b4
commit
85b6e25484
13 changed files with 588 additions and 31 deletions
|
|
@ -90,8 +90,8 @@ modification.
|
|||
|
||||
Openbox spec is somewhat of a stable standard considering how long it has
|
||||
remained unchanged for and how wide-spread its adoption is by lightweight
|
||||
distributions such as LXDE, LXQt, BunsenLabs, ArchLabs, Mabox and Raspbian. Some
|
||||
widely used themes (for example Numix and Arc) have built-in support.
|
||||
distributions such as LXDE, LXQt, BunsenLabs, ArchLabs, Mabox and Raspbian.
|
||||
Some widely used themes (for example Numix and Arc) have built-in support.
|
||||
|
||||
We could have invented a whole new syntax, but that's not where we want to
|
||||
spend our effort.
|
||||
|
|
@ -182,8 +182,8 @@ prevent installing the wlroots headers:
|
|||
## 3. Configuration
|
||||
|
||||
User config files are located at `${XDG_CONFIG_HOME:-$HOME/.config/labwc/}`
|
||||
with the following five files being used: [rc.xml], [menu.xml], [autostart], [shutdown],
|
||||
[environment] and [themerc-override].
|
||||
with the following five files being used: [rc.xml] (or [rc.yaml]), [menu.xml],
|
||||
[autostart], [shutdown], [environment] and [themerc-override].
|
||||
|
||||
Run `labwc --reconfigure` to reload configuration and theme.
|
||||
|
||||
|
|
@ -275,6 +275,7 @@ See [integration] for further details.
|
|||
[metacity]: https://github.com/GNOME/metacity
|
||||
|
||||
[rc.xml]: docs/rc.xml.all
|
||||
[rc.yaml]: docs/rc.yaml
|
||||
[menu.xml]: docs/menu.xml
|
||||
[autostart]: docs/autostart
|
||||
[shutdown]: docs/shutdown
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ Config layout for ~/.config/labwc/
|
|||
- environment
|
||||
- menu.xml
|
||||
- rc.xml
|
||||
- rc.yaml
|
||||
- shutdown
|
||||
- themerc-override
|
||||
- xinitrc
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ labwc - configuration files
|
|||
|
||||
Labwc uses openbox-3.6 specification for configuration and theming, but does not
|
||||
support all options. The following files form the basis of the labwc
|
||||
configuration: rc.xml, menu.xml, autostart, shutdown, environment and xinitrc.
|
||||
configuration: rc.xml (or rc.yaml), menu.xml, autostart, shutdown, environment
|
||||
and xinitrc.
|
||||
|
||||
No configuration files are needed to start and run labwc.
|
||||
|
||||
|
|
@ -158,6 +159,77 @@ Note that in this manual, Boolean values are listed as [yes|no] for simplicity,
|
|||
but it's also possible to use [true|false] and\/or [on|off];
|
||||
this is for compatibility with Openbox.
|
||||
|
||||
## YAML SUPPORT
|
||||
|
||||
Labwc also supports YAML language for configuration. When rc.yaml exists
|
||||
instead of rc.xml, labwc internally converts it from YAML into XML and loads
|
||||
configurations from it. For example, "foo: bar" in YAML is converted to
|
||||
"<foo>bar</foo>" in XML. See /usr/share/docs/labwc/rc.yaml for an example
|
||||
configuration in YAML.
|
||||
|
||||
If rc.yaml includes a key-value pair where the value is an array, it is
|
||||
converted to a sequence of <key>array-element</key> in XML.
|
||||
|
||||
For example, a YAML expression:
|
||||
|
||||
```
|
||||
touch:
|
||||
- deviceName: xxxx
|
||||
mapToOutput: eDP-1
|
||||
- deviceName: yyyy
|
||||
mapToOutput: HDMI-1
|
||||
```
|
||||
|
||||
is converted to an XML expression:
|
||||
|
||||
```
|
||||
<touch>
|
||||
<deviceName>xxxx</deviceName>
|
||||
<mapToOutput>eDP-1</mapToOutput>
|
||||
</touch>
|
||||
<touch>
|
||||
<deviceName>yyyy</deviceName>
|
||||
<mapToOutput>eDP-1</mapToOutput>
|
||||
</touch>
|
||||
```
|
||||
|
||||
To avoid unnecessary indentations, some nodes that wrap array elements in XML
|
||||
can be ommitted. This includes:
|
||||
|
||||
- *<windowSwitcher><fields><field>*
|
||||
- *<regions><region>*
|
||||
- *<windowRules><windowRule>*
|
||||
- *<libinput><device>*
|
||||
- *<desktops><names><name>*
|
||||
|
||||
For example, window switcher can be configured like:
|
||||
|
||||
```
|
||||
windowSwitcher:
|
||||
fields:
|
||||
- content: type
|
||||
width: 15%
|
||||
- content: title
|
||||
width: 85%
|
||||
```
|
||||
|
||||
In addition, some specific keys in singular form with a sequence value in YAML
|
||||
are converted to plural form in XML. This includes:
|
||||
|
||||
- *keybinds* (converted to *keybind*)
|
||||
- *mousebinds* (converted to *mousebind*)
|
||||
- *actions* (converted to *action*)
|
||||
- *fonts* (converted to *font*)
|
||||
- *contexts* (converted to *context*)
|
||||
|
||||
For example, keybinds can be configured like:
|
||||
|
||||
```
|
||||
keybinds:
|
||||
- { key: W-s, action: { name: Execute, command: foot } }
|
||||
- { key: W-a, action: { name: Execute, command: fuzzel } }
|
||||
```
|
||||
|
||||
## CORE
|
||||
|
||||
```
|
||||
|
|
@ -573,9 +645,9 @@ extending outward from the snapped edge.
|
|||
Load the default keybinds listed below. This is an addition to the
|
||||
openbox specification and provides a way to keep config files simpler
|
||||
whilst allowing your specific keybinds.
|
||||
Note that if no rc.xml is found, or if no <keyboard><keybind> entries
|
||||
exist, the same default keybinds will be loaded even if the <default />
|
||||
element is not provided.
|
||||
Note that if no rc.xml or rc.yaml is found, or if no <keyboard><keybind>
|
||||
entries exist, the same default keybinds will be loaded even if the
|
||||
<default /> element is not provided.
|
||||
|
||||
```
|
||||
A-Tab - next window
|
||||
|
|
@ -664,9 +736,10 @@ extending outward from the snapped edge.
|
|||
*<mouse><default />*
|
||||
Load default mousebinds. This is an addition to the openbox
|
||||
specification and provides a way to keep config files simpler whilst
|
||||
allowing user specific binds. Note that if no rc.xml is found, or if no
|
||||
<mouse><mousebind> entries exist, the same default mousebinds will be
|
||||
loaded even if the <default /> element is not provided.
|
||||
allowing user specific binds. Note that if no rc.xml or rc.yaml is
|
||||
found, or if no <mouse><mousebind> entries exist, the same default
|
||||
mousebinds will be loaded even if the <default /> element is not
|
||||
provided.
|
||||
|
||||
## TOUCH
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ install_data(
|
|||
'shutdown',
|
||||
'themerc',
|
||||
'rc.xml',
|
||||
'rc.xml.all'
|
||||
'rc.xml.all',
|
||||
'rc.yaml',
|
||||
],
|
||||
install_dir: get_option('datadir') / 'doc' / meson.project_name()
|
||||
)
|
||||
|
|
|
|||
89
docs/rc.yaml
Normal file
89
docs/rc.yaml
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
# An example configuration file in YAML. To see all the configurations and
|
||||
# their descriptions, see docs/rc.xml.all or labwc-config(5).
|
||||
core:
|
||||
xwaylandPersistence: yes
|
||||
placement:
|
||||
policy: cascade
|
||||
theme:
|
||||
dropShadows: yes
|
||||
fonts:
|
||||
- place: ActiveWindow
|
||||
weight: bold
|
||||
- place: InactiveWindow
|
||||
weight: normal
|
||||
desktops:
|
||||
number: 2
|
||||
windowSwitcher:
|
||||
fields:
|
||||
- content: type
|
||||
width: 15%
|
||||
- content: title
|
||||
width: 85%
|
||||
resize:
|
||||
drawContents: no
|
||||
focus:
|
||||
followMouse: yes
|
||||
followMouseRequiresMovement: yes
|
||||
raiseOnFocus: no
|
||||
snapping:
|
||||
range: 20
|
||||
overlay:
|
||||
delay:
|
||||
inner: 100
|
||||
outer: 0
|
||||
regions:
|
||||
- { name: top-left, x: 0%, y: 0%, width: 50%, height: 50% }
|
||||
- { name: top-right, x: 50%, y: 0%, width: 50%, height: 50% }
|
||||
- { name: bottom-left, x: 0%, y: 50%, width: 50%, height: 50% }
|
||||
- { name: bottom-right, x: 50%, y: 50%, width: 50%, height: 50% }
|
||||
keyboard:
|
||||
repeatRate: 25
|
||||
repeatDelay: 600
|
||||
keybinds:
|
||||
- { key: W-bracketRight, action: { name: ZoomIn } }
|
||||
- { key: W-bracketLeft, action: { name: ZoomOut } }
|
||||
- { key: W-d, action: { name: Debug } }
|
||||
- { key: W-1, action: { name: GoToDesktop, to: Workspace 1 } }
|
||||
- { key: W-2, action: { name: GoToDesktop, to: Workspace 2 } }
|
||||
- { key: W-m, action: { name: ToggleKeybinds } }
|
||||
- { key: W-q, action: { name: Close } }
|
||||
- { key: A-F4, action: { name: Close } }
|
||||
- { key: A-Tab, action: { name: NextWindow } }
|
||||
- { key: W-e, action: { name: Exit } }
|
||||
- { key: W-v, action: { name: Execute, command: sh -c 'cliphist list | fuzzel --dmenu | cliphist decode | wl-copy' } }
|
||||
- { key: W-Tab, action: { name: ToggleMaximize } }
|
||||
- { key: W-s, action: { name: Execute, command: foot } }
|
||||
- { key: W-a, action: { name: Execute, command: fuzzel } }
|
||||
- { key: XF86_AudioLowerVolume, action: { name: Execute, command: pactl set-sink-volume @DEFAULT_SINK@ -2dB } }
|
||||
- { key: XF86_AudioRaiseVolume, action: { name: Execute, command: pactl set-sink-volume @DEFAULT_SINK@ +2dB } }
|
||||
- { key: XF86_AudioMute, action: { name: Execute, command: pactl set-sink-mute @DEFAULT_SINK@ toggle } }
|
||||
- { key: XF86_AudioMicMute, action: { name: Execute, command: pactl set-source-mute @DEFAULT_SOURCE@ toggle } }
|
||||
- { key: Print, action: { name: Execute, command: grim } }
|
||||
mouse:
|
||||
doubleClickTime: 200
|
||||
default:
|
||||
contexts:
|
||||
- name: Frame
|
||||
mousebinds:
|
||||
- { button: W-Left, action: Press, actions: [ { name: Raise }, { name: Move } ] }
|
||||
- { button: W-Right, action: Drag, action: { name: Raise } }
|
||||
- { button: A-Left, action: Press }
|
||||
- { button: A-Left, action: Drag }
|
||||
- { button: A-Right, action: Press }
|
||||
- { button: A-Right, action: Drag }
|
||||
touch:
|
||||
deviceName: ELAN2514:00 04F3:2AF1
|
||||
mapToOutput: eDP-1
|
||||
tablet:
|
||||
mapToOutput: eDP-1
|
||||
libinput:
|
||||
- category: non-touch
|
||||
pointerSpeed: -0.4
|
||||
- category: touchpad
|
||||
naturalScroll: yes
|
||||
tapAndDrag: no
|
||||
windowRules:
|
||||
- identifier: ristretto
|
||||
serverDecoration: no
|
||||
- identifier: code-url-handler
|
||||
action: { name: SetDecorations, decorations: border, forceSSD: yes }
|
||||
9
include/common/yaml2xml.h
Normal file
9
include/common/yaml2xml.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
#ifndef LABWC_YAML2XML_H
|
||||
#define LABWC_YAML2XML_H
|
||||
#include <common/buf.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct buf yaml_to_xml(FILE *stream, const char *toplevel_name);
|
||||
|
||||
#endif /* LABWC_YAML2XML_H */
|
||||
13
meson.build
13
meson.build
|
|
@ -72,6 +72,7 @@ pixman = dependency('pixman-1')
|
|||
math = cc.find_library('m')
|
||||
png = dependency('libpng')
|
||||
svg = dependency('librsvg-2.0', version: '>=2.46', required: false)
|
||||
yaml = dependency('yaml-0.1', required: false)
|
||||
|
||||
if get_option('xwayland').enabled() and not wlroots_has_xwayland
|
||||
error('no wlroots Xwayland support')
|
||||
|
|
@ -87,6 +88,13 @@ else
|
|||
endif
|
||||
conf_data.set10('HAVE_RSVG', have_rsvg)
|
||||
|
||||
if get_option('yaml').disabled()
|
||||
have_libyaml = false
|
||||
else
|
||||
have_libyaml = yaml.found()
|
||||
endif
|
||||
conf_data.set10('HAVE_LIBYAML', have_libyaml)
|
||||
|
||||
if get_option('static_analyzer').enabled()
|
||||
add_project_arguments(['-fanalyzer'], language: 'c')
|
||||
endif
|
||||
|
|
@ -125,6 +133,11 @@ if have_rsvg
|
|||
svg,
|
||||
]
|
||||
endif
|
||||
if have_libyaml
|
||||
labwc_deps += [
|
||||
yaml,
|
||||
]
|
||||
endif
|
||||
|
||||
subdir('include')
|
||||
subdir('src')
|
||||
|
|
|
|||
|
|
@ -4,3 +4,4 @@ option('svg', type: 'feature', value: 'enabled', description: 'Enable svg window
|
|||
option('nls', type: 'feature', value: 'auto', description: 'Enable native language support')
|
||||
option('static_analyzer', type: 'feature', value: 'disabled', description: 'Run gcc static analyzer')
|
||||
option('test', type: 'feature', value: 'disabled', description: 'Run tests')
|
||||
option('yaml', type: 'feature', value: 'enabled', description: 'Enable configuration in YAML')
|
||||
|
|
|
|||
|
|
@ -19,3 +19,9 @@ labwc_sources += files(
|
|||
'spawn.c',
|
||||
'string-helpers.c',
|
||||
)
|
||||
|
||||
if have_libyaml
|
||||
labwc_sources += files(
|
||||
'yaml2xml.c',
|
||||
)
|
||||
endif
|
||||
|
|
|
|||
194
src/common/yaml2xml.c
Normal file
194
src/common/yaml2xml.c
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#include <assert.h>
|
||||
#include <yaml.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "common/buf.h"
|
||||
#include "common/yaml2xml.h"
|
||||
|
||||
struct lab_yaml_event {
|
||||
yaml_event_type_t type;
|
||||
char scalar[256];
|
||||
};
|
||||
|
||||
static bool process_mapping(yaml_parser_t *parser, struct buf *buf);
|
||||
static bool process_sequence(yaml_parser_t *parser, struct buf *buf, char *key_name);
|
||||
|
||||
static bool
|
||||
parse(yaml_parser_t *parser, struct lab_yaml_event *event)
|
||||
{
|
||||
yaml_event_t yaml_event;
|
||||
if (!yaml_parser_parse(parser, &yaml_event)) {
|
||||
wlr_log(WLR_ERROR,
|
||||
"Conversion from YAML to XML failed at %ld:%ld: %s",
|
||||
parser->problem_mark.line + 1,
|
||||
parser->problem_mark.column,
|
||||
parser->problem);
|
||||
return false;
|
||||
}
|
||||
event->type = yaml_event.type;
|
||||
if (yaml_event.type == YAML_SCALAR_EVENT) {
|
||||
snprintf(event->scalar, sizeof(event->scalar), "%s",
|
||||
(char *)yaml_event.data.scalar.value);
|
||||
}
|
||||
yaml_event_delete(&yaml_event);
|
||||
return event;
|
||||
}
|
||||
|
||||
static bool
|
||||
process_value(yaml_parser_t *parser, struct buf *b,
|
||||
struct lab_yaml_event *event, char *key_name)
|
||||
{
|
||||
const char *parent_name = NULL;
|
||||
|
||||
switch (event->type) {
|
||||
case YAML_MAPPING_START_EVENT:
|
||||
buf_add_fmt(b, "<%s>", key_name);
|
||||
if (!process_mapping(parser, b)) {
|
||||
return false;
|
||||
}
|
||||
buf_add_fmt(b, "</%s>", key_name);
|
||||
break;
|
||||
case YAML_SEQUENCE_START_EVENT:
|
||||
/*
|
||||
* YAML XML
|
||||
* fields: [x,y] -> <fields><field>x<field><field>y<field<fields>
|
||||
*/
|
||||
if (!strcasecmp(key_name, "fields")) {
|
||||
parent_name = "fields";
|
||||
key_name = "field";
|
||||
} else if (!strcasecmp(key_name, "regions")) {
|
||||
parent_name = "regions";
|
||||
key_name = "region";
|
||||
} else if (!strcasecmp(key_name, "windowRules")) {
|
||||
parent_name = "windowRules";
|
||||
key_name = "windowRule";
|
||||
} else if (!strcasecmp(key_name, "libinput")) {
|
||||
parent_name = "libinput";
|
||||
key_name = "device";
|
||||
} else if (!strcasecmp(key_name, "names")) {
|
||||
parent_name = "names";
|
||||
key_name = "name";
|
||||
} else if (!strcasecmp(key_name, "keybinds")) {
|
||||
key_name = "keybind";
|
||||
} else if (!strcasecmp(key_name, "mousebinds")) {
|
||||
key_name = "mousebind";
|
||||
} else if (!strcasecmp(key_name, "actions")) {
|
||||
key_name = "action";
|
||||
} else if (!strcasecmp(key_name, "fonts")) {
|
||||
key_name = "font";
|
||||
} else if (!strcasecmp(key_name, "contexts")) {
|
||||
key_name = "context";
|
||||
}
|
||||
if (parent_name) {
|
||||
buf_add_fmt(b, "<%s>", parent_name);
|
||||
}
|
||||
if (!process_sequence(parser, b, key_name)) {
|
||||
return false;
|
||||
}
|
||||
if (parent_name) {
|
||||
buf_add_fmt(b, "</%s>", parent_name);
|
||||
}
|
||||
break;
|
||||
case YAML_SCALAR_EVENT:
|
||||
/*
|
||||
* To avoid duplicated keys, mousebind: {event: Press} is
|
||||
* mapped to <mousebind action="Press">.
|
||||
*/
|
||||
if (!strcasecmp(key_name, "event")) {
|
||||
key_name = "action";
|
||||
}
|
||||
buf_add_fmt(b, "<%s>%s</%s>", key_name, event->scalar, key_name);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
process_sequence(yaml_parser_t *parser, struct buf *b, char *key_name)
|
||||
{
|
||||
while (1) {
|
||||
struct lab_yaml_event event;
|
||||
if (!parse(parser, &event)) {
|
||||
return false;
|
||||
}
|
||||
if (event.type == YAML_SEQUENCE_END_EVENT) {
|
||||
return true;
|
||||
}
|
||||
if (!process_value(parser, b, &event, key_name)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
process_mapping(yaml_parser_t *parser, struct buf *buf)
|
||||
{
|
||||
while (1) {
|
||||
struct lab_yaml_event key_event, value_event;
|
||||
|
||||
if (!parse(parser, &key_event)) {
|
||||
return false;
|
||||
}
|
||||
if (key_event.type == YAML_MAPPING_END_EVENT) {
|
||||
return true;
|
||||
}
|
||||
if (key_event.type != YAML_SCALAR_EVENT) {
|
||||
wlr_log(WLR_ERROR, "key must be scalar");
|
||||
return false;
|
||||
}
|
||||
if (!parse(parser, &value_event)) {
|
||||
return false;
|
||||
}
|
||||
if (!process_value(parser, buf, &value_event, key_event.scalar)) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
process_root(yaml_parser_t *parser, struct buf *b, const char *toplevel_name)
|
||||
{
|
||||
struct lab_yaml_event event;
|
||||
if (!parse(parser, &event) || event.type != YAML_STREAM_START_EVENT) {
|
||||
return false;
|
||||
}
|
||||
if (!parse(parser, &event) || event.type != YAML_DOCUMENT_START_EVENT) {
|
||||
return false;
|
||||
}
|
||||
if (!parse(parser, &event) || event.type != YAML_MAPPING_START_EVENT) {
|
||||
wlr_log(WLR_ERROR, "mapping is expected for toplevel node");
|
||||
return false;
|
||||
}
|
||||
|
||||
buf_add_fmt(b, "<%s>", toplevel_name);
|
||||
if (!process_mapping(parser, b)) {
|
||||
return false;
|
||||
}
|
||||
buf_add_fmt(b, "</%s>", toplevel_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct buf
|
||||
yaml_to_xml(FILE *stream, const char *toplevel_name)
|
||||
{
|
||||
struct buf b = BUF_INIT;
|
||||
yaml_parser_t parser;
|
||||
yaml_parser_initialize(&parser);
|
||||
yaml_parser_set_input_file(&parser, stream);
|
||||
|
||||
bool success = process_root(&parser, &b, toplevel_name);
|
||||
if (success) {
|
||||
wlr_log(WLR_DEBUG, "XML converted from YAML: %s", b.data);
|
||||
} else {
|
||||
buf_reset(&b);
|
||||
}
|
||||
yaml_parser_delete(&parser);
|
||||
return b;
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <glib.h>
|
||||
#include <libxml/parser.h>
|
||||
|
|
@ -15,6 +16,7 @@
|
|||
#include <wlr/util/box.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "action.h"
|
||||
#include "common/buf.h"
|
||||
#include "common/dir.h"
|
||||
#include "common/list.h"
|
||||
#include "common/macros.h"
|
||||
|
|
@ -36,6 +38,10 @@
|
|||
#include "window-rules.h"
|
||||
#include "workspaces.h"
|
||||
|
||||
#if HAVE_LIBYAML
|
||||
#include "common/yaml2xml.h"
|
||||
#endif
|
||||
|
||||
static bool in_regions;
|
||||
static bool in_usable_area_override;
|
||||
static bool in_keybind;
|
||||
|
|
@ -1743,6 +1749,37 @@ validate(void)
|
|||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
file_is_xml(const char *filename)
|
||||
{
|
||||
if (str_endswith(filename, ".xml")) {
|
||||
return true;
|
||||
} else if (str_endswith(filename, ".yaml")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the extension is not .xml or .yaml, judge by whther the content
|
||||
* starts with '<'.
|
||||
*/
|
||||
FILE *stream = fopen(filename, "r");
|
||||
if (!stream) {
|
||||
return true;
|
||||
}
|
||||
char c;
|
||||
while ((c = fgetc(stream))) {
|
||||
if (!isspace(c)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(stream);
|
||||
if (c == '<') {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rcxml_read(const char *filename)
|
||||
{
|
||||
|
|
@ -1758,9 +1795,14 @@ rcxml_read(const char *filename)
|
|||
wl_list_append(&paths, &path->link);
|
||||
} else {
|
||||
paths_config_create(&paths, "rc.xml");
|
||||
#ifdef HAVE_LIBYAML
|
||||
struct wl_list paths_yaml;
|
||||
wl_list_init(&paths_yaml);
|
||||
paths_config_create(&paths_yaml, "rc.yaml");
|
||||
wl_list_insert_list(paths.prev, &paths_yaml);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Reading file into buffer before parsing - better for unit tests */
|
||||
bool should_merge_config = rc.merge_config;
|
||||
struct wl_list *(*iter)(struct wl_list *list);
|
||||
iter = should_merge_config ? paths_get_prev : paths_get_next;
|
||||
|
|
@ -1782,21 +1824,30 @@ rcxml_read(const char *filename)
|
|||
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';
|
||||
if (HAVE_LIBYAML && !file_is_xml(path->string)) {
|
||||
#if HAVE_LIBYAML
|
||||
wlr_log(WLR_INFO, "read yaml config file %s", path->string);
|
||||
b = yaml_to_xml(stream, "labwc_config");
|
||||
#endif
|
||||
} else {
|
||||
wlr_log(WLR_INFO, "read xml config file %s", path->string);
|
||||
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);
|
||||
}
|
||||
buf_add(&b, line);
|
||||
zfree(line);
|
||||
}
|
||||
zfree(line);
|
||||
|
||||
fclose(stream);
|
||||
rcxml_parse_xml(&b);
|
||||
if (b.len > 0) {
|
||||
rcxml_parse_xml(&b);
|
||||
}
|
||||
buf_reset(&b);
|
||||
if (!should_merge_config) {
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,18 +1,37 @@
|
|||
test_lib = static_library(
|
||||
'test_lib',
|
||||
sources: files(
|
||||
test_lib_sources = files(
|
||||
'../src/common/buf.c',
|
||||
'../src/common/mem.c',
|
||||
'../src/common/string-helpers.c'
|
||||
),
|
||||
include_directories: [labwc_inc],
|
||||
dependencies: [dep_cmocka],
|
||||
'../src/common/string-helpers.c',
|
||||
)
|
||||
|
||||
test_deps = [
|
||||
dep_cmocka,
|
||||
]
|
||||
|
||||
tests = [
|
||||
'buf-simple',
|
||||
]
|
||||
|
||||
if have_libyaml
|
||||
test_lib_sources += [
|
||||
'../src/common/yaml2xml.c',
|
||||
]
|
||||
test_deps += [
|
||||
wlroots,
|
||||
yaml,
|
||||
]
|
||||
tests += [
|
||||
'yaml2xml',
|
||||
]
|
||||
endif
|
||||
|
||||
test_lib = static_library(
|
||||
'test_lib',
|
||||
sources: test_lib_sources,
|
||||
include_directories: [labwc_inc],
|
||||
dependencies: test_deps
|
||||
)
|
||||
|
||||
foreach t : tests
|
||||
test(
|
||||
'test_@0@'.format(t),
|
||||
|
|
|
|||
99
t/yaml2xml.c
Normal file
99
t/yaml2xml.c
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <setjmp.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <cmocka.h>
|
||||
#include "common/buf.h"
|
||||
#include "common/macros.h"
|
||||
#include "common/yaml2xml.h"
|
||||
|
||||
struct test_set {
|
||||
const char *name, *yaml, *xml;
|
||||
};
|
||||
|
||||
const struct test_set test_sets[] = {
|
||||
{
|
||||
.name = "key-scalar",
|
||||
.yaml = "xxx: yyy",
|
||||
.xml = "<test><xxx>yyy</xxx></test>",
|
||||
},
|
||||
{
|
||||
.name = "key-sequence",
|
||||
.yaml = "xxx: [yyy, zzz]",
|
||||
.xml = "<test><xxx>yyy</xxx><xxx>zzz</xxx></test>",
|
||||
},
|
||||
{
|
||||
.name = "key-mapping",
|
||||
.yaml = "xxx: {yyy: zzz}",
|
||||
.xml = "<test><xxx><yyy>zzz</yyy></xxx></test>",
|
||||
},
|
||||
{
|
||||
.name = "window-switcher-fields",
|
||||
.yaml = "windowSwitcher: {fields: [xxx, yyy]}",
|
||||
.xml =
|
||||
"<test><windowSwitcher><fields>"
|
||||
"<field>xxx</field>"
|
||||
"<field>yyy</field>"
|
||||
"</fields></windowSwitcher></test>",
|
||||
},
|
||||
{
|
||||
.name = "theme-fonts",
|
||||
.yaml = "theme: {fonts: [xxx, yyy]}",
|
||||
.xml =
|
||||
"<test><theme>"
|
||||
"<font>xxx</font>"
|
||||
"<font>yyy</font>"
|
||||
"</theme></test>",
|
||||
},
|
||||
{
|
||||
.name = "mousebinds",
|
||||
.yaml =
|
||||
"mousebinds:\n"
|
||||
" - { button: W-Left, action: Press, actions: [ { name: Raise }, { name: Move } ] }\n"
|
||||
" - { button: W-Right, action: Drag, action: { name: Resize} }\n",
|
||||
.xml =
|
||||
"<test>"
|
||||
"<mousebind>"
|
||||
"<button>W-Left</button>"
|
||||
"<action>Press</action>"
|
||||
"<action><name>Raise</name></action>"
|
||||
"<action><name>Move</name></action>"
|
||||
"</mousebind>"
|
||||
"<mousebind>"
|
||||
"<button>W-Right</button>"
|
||||
"<action>Drag</action>"
|
||||
"<action><name>Resize</name></action>"
|
||||
"</mousebind>"
|
||||
"</test>",
|
||||
},
|
||||
};
|
||||
|
||||
static void
|
||||
test_yaml_to_xml(void **state)
|
||||
{
|
||||
(void)state;
|
||||
for (int i = 0; i < (int)ARRAY_SIZE(test_sets); i++) {
|
||||
const struct test_set *set = &test_sets[i];
|
||||
|
||||
char buf[1024];
|
||||
FILE *stream = fmemopen(buf, sizeof(buf), "w+");
|
||||
fwrite(set->yaml, strlen(set->yaml), 1, stream);
|
||||
fseek(stream, 0, SEEK_SET);
|
||||
|
||||
struct buf b = yaml_to_xml(stream, "test");
|
||||
fclose(stream);
|
||||
assert_string_equal(b.data, set->xml);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test(test_yaml_to_xml),
|
||||
};
|
||||
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue