Basic config-file support

This commit is contained in:
Keith Bowes 2022-02-01 20:26:50 -05:00
parent 82ddb3f59f
commit 8153a24863
10 changed files with 312 additions and 48 deletions

View file

@ -13,6 +13,7 @@ contributing.](https://github.com/wizbright/waybox/blob/master/CONTRIBUTING.md)
* [Meson](https://mesonbuild.com/)
* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots/)
* [libxml2](http://xmlsoft.org/)
* [Wayland](https://wayland.freedesktop.org/)
* [xkbcommon](https://xkbcommon.org/)

View file

@ -25,6 +25,7 @@
#include <locale.h>
#define _ gettext
#include "config.h"
#include "waybox/cursor.h"
#include "decoration.h"
#include "layer_shell.h"
@ -40,6 +41,9 @@ struct wb_server {
struct wlr_output_layout *output_layout;
struct wlr_renderer *renderer;
struct wb_config *config;
char *config_file;
struct wb_cursor *cursor;
struct wb_seat *seat;

View file

@ -25,6 +25,7 @@ inc_dir = include_directories('include')
# Due to the planned refactor of xdg_shell in wlroots 0.16.0, I doubt this will
# build when it's released
libxml2 = dependency('libxml-2.0')
wlroots = dependency('wlroots', version: ['>=0.15.0', '<0.16.0'])
wayland_server = dependency('wayland-server', version: '>=1.15')
wayland_protos = dependency('wayland-protocols', version: '>=1.17')

185
waybox/config.c Normal file
View file

@ -0,0 +1,185 @@
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include "config.h"
static char *parse_xpath_expr(char *expr, xmlXPathContextPtr ctxt) {
xmlXPathObjectPtr object = xmlXPathEvalExpression((xmlChar *) expr, ctxt);
if (object == NULL) {
wlr_log(WLR_INFO, "%s", _("Unable to evaluate expression"));
xmlXPathFreeContext(ctxt);
return(NULL);
}
if (!object->nodesetval) {
wlr_log(WLR_INFO, "%s", _("No nodesetval"));
return NULL;
}
xmlChar *ret = NULL;
if (object->nodesetval->nodeNr > 0)
ret = xmlNodeGetContent(object->nodesetval->nodeTab[0]);
xmlXPathFreeObject(object);
return (char *) ret;
}
static bool parse_key_bindings(struct wb_config *config, xmlXPathContextPtr ctxt)
{
/* Get the key bindings */
wl_list_init(&config->key_bindings);
xmlXPathObjectPtr object = xmlXPathEvalExpression((xmlChar *) "//ob:keyboard/ob:keybind", ctxt);
if (object == NULL) {
wlr_log(WLR_INFO, "%s", _("Unable to evaluate expression"));
return(false);
}
if (!object->nodesetval) {
wlr_log(WLR_INFO, "%s", _("No nodeset"));
return false;
}
/* Iterate through the nodes to get the information */
if (object->nodesetval->nodeNr > 0) {
int i;
for (i = 0; i < object->nodesetval->nodeNr; i++) {
if (object->nodesetval->nodeTab[i]) {
/* First get the key combinations */
xmlAttr *keycomb = object->nodesetval->nodeTab[i]->properties;
while (strcmp((char *) keycomb->name, "key") != 0)
{
keycomb = keycomb->next;
}
char *sym;
uint32_t modifiers = 0;
sym = (char *) keycomb->children->content;
char *s;
struct wb_key_binding *key_bind = calloc(1, sizeof(struct wb_key_binding));
key_bind->sym = 0;
key_bind->modifiers = 0;
while ((s = strtok(sym, "-")) != NULL)
{
if (strcmp(s, "A") == 0 || strcmp(s, "Alt") == 0)
modifiers |= WLR_MODIFIER_ALT;
else if (strcmp(s, "Caps") == 0)
modifiers |= WLR_MODIFIER_CAPS;
else if (strcmp(s, "C") == 0 || strcmp(s, "Ctrl") == 0)
modifiers |= WLR_MODIFIER_CTRL;
else if (strcmp(s, "Mod2") == 0)
modifiers |= WLR_MODIFIER_MOD2;
else if (strcmp(s, "Mod3") == 0)
modifiers |= WLR_MODIFIER_MOD3;
else if (strcmp(s, "Mod5") == 0)
modifiers |= WLR_MODIFIER_MOD5;
else if (strcmp(s, "S") == 0 || strcmp(s, "Shift") == 0)
modifiers |= WLR_MODIFIER_SHIFT;
else if (strcmp(s, "W") == 0 || strcmp(s, "Logo") == 0)
modifiers |= WLR_MODIFIER_LOGO;
else
key_bind->sym = xkb_keysym_from_name(s, 0);
key_bind->modifiers = modifiers;
sym = NULL;
}
/* Now get the actions */
xmlNode *cur_node;
xmlNode *new_node = object->nodesetval->nodeTab[i]->children;
xmlAttr *attr;
for (cur_node = new_node; cur_node; cur_node = cur_node->next)
{
if (strcmp((char *) cur_node->name, "action") == 0)
{
attr = cur_node->properties;
while (strcmp((char *) attr->name, "name") != 0)
{
attr = attr->next;
}
key_bind->action = malloc(sizeof (char) * 64);
strcpy(key_bind->action, (char *) attr->children->content);
wlr_log(WLR_INFO, "Registering action %s", (char *) key_bind->action);
if (strcmp((char *) key_bind->action, "Execute") != 0)
break;
cur_node = cur_node->children;
}
if (strcmp((char *) cur_node->name, "execute") == 0)
{
/* Bad things can happen if the command is greater than 1024 characters */
key_bind->cmd = malloc(sizeof(char) * 1024);
strncpy(key_bind->cmd, (char *) cur_node->children->content, 1023);
key_bind->cmd[strlen(key_bind->cmd)] = '\0';
if (key_bind->action)
break;
}
}
wl_list_insert(&config->key_bindings, &key_bind->link);
}
}
}
xmlXPathFreeObject(object);
return true;
}
bool init_config(struct wb_server *server)
{
xmlDocPtr doc;
if (server->config_file == NULL) {
char *xdg_config = getenv("XDG_CONFIG_HOME");
if (!xdg_config)
xdg_config = "~/.config";
char *rc_file = malloc(strlen(xdg_config) + 14);
strcpy(rc_file, xdg_config);
rc_file = strcat(rc_file, "/waybox/rc.xml");
doc = xmlParseFile(rc_file);
wlr_log(WLR_INFO, "Using config file %s", rc_file);
free(rc_file);
} else {
wlr_log(WLR_INFO, "Using config file %s", server->config_file);
doc = xmlParseFile(server->config_file);
}
if (doc == NULL) {
wlr_log(WLR_INFO, "%s", _("Unable to parse XML file"));
return false;
}
xmlXPathContextPtr ctxt = xmlXPathNewContext(doc);
if (ctxt == NULL) {
wlr_log(WLR_INFO, "%s", _("Couldn't create new context!"));
xmlFreeDoc(doc);
return(false);
}
if (xmlXPathRegisterNs(ctxt, (const xmlChar *) "ob", (const xmlChar *) "http://openbox.org/3.4/rc") != 0) {
wlr_log(WLR_INFO, "%s", _("Couldn't register the namespace"));
}
struct wb_config *config = calloc(1, sizeof(struct wb_config));
config->keyboard_layout.layout = parse_xpath_expr("//ob:keyboard//ob:layout//ob:layout", ctxt);
config->keyboard_layout.model = parse_xpath_expr("//ob:keyboard//ob:layout//ob:model", ctxt);
config->keyboard_layout.options = parse_xpath_expr("//ob:keyboard//ob:layout//ob:options", ctxt);
config->keyboard_layout.rules = parse_xpath_expr("//ob:keyboard//ob:layout//ob:rules", ctxt);
config->keyboard_layout.variant = parse_xpath_expr("//ob:keyboard//ob:layout//ob:variant", ctxt);
if (!parse_key_bindings(config, ctxt))
{
xmlFreeDoc(doc);
return false;
}
server->config = config;
xmlXPathFreeContext(ctxt);
xmlFreeDoc(doc);
xmlCleanupParser();
return true;
}
void deinit_config(struct wb_config *config)
{
/* Free everything allocated in init_config */
struct wb_key_binding *key_binding;
wl_list_for_each(key_binding, &config->key_bindings, link) {
free(key_binding->action);
free(key_binding->cmd);
free(key_binding);
}
wl_list_remove(&config->key_bindings);
free(config);
}

30
waybox/config.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef _WB_CONFIG_H
#define _WB_CONFIG_H
#include "waybox/server.h"
struct wb_config {
struct wb_server *server;
struct {
char *layout;
char *model;
char *options;
char *rules;
char *variant;
} keyboard_layout;
struct wl_list applications;
struct wl_list key_bindings;
};
struct wb_key_binding {
xkb_keysym_t sym;
uint32_t modifiers;
char *action;
char *cmd;
struct wl_list link;
};
bool init_config(struct wb_server *server);
void deinit_config(struct wb_config *config);
#endif

View file

@ -13,7 +13,6 @@ struct wb_decoration {
struct wl_listener toplevel_decoration_destroy;
struct wl_listener request_mode;
struct wl_listener mode_destroy;
int yes;
};
void init_xdg_decoration(struct wb_server *server);

View file

@ -26,6 +26,7 @@ int main(int argc, char **argv) {
textdomain(GETTEXT_PACKAGE);
char *startup_cmd = NULL;
struct wb_server server = {0};
enum wlr_log_importance debuglevel = WLR_ERROR;
if (argc > 1) {
int i;
@ -44,8 +45,13 @@ int main(int argc, char **argv) {
} else if (!strcmp("--help", argv[i]) || !strcmp("-h", argv[i])) {
show_help(argv[0]);
return 0;
} else if (!strcmp("--config-file", argv[i]) ||
!strcmp("--sm-disable", argv[i])) {
} else if (strcmp("--config-file", argv[i]) == 0) {
if (i < argc - 1) {
server.config_file = argv[i + 1];
} else {
fprintf(stderr, _("%s requires an argument\n"), argv[i]);
}
} else if (!strcmp("--sm-disable", argv[i])) {
fprintf(stderr, _("%s hasn't been implemented yet.\n"), argv[i]);
if (i == argc - 1) {
fprintf(stderr, _("%s requires an argument\n"), argv[i]);
@ -58,7 +64,6 @@ int main(int argc, char **argv) {
}
wlr_log_init(debuglevel, NULL);
struct wb_server server = {0};
if (wb_create_backend(&server)) {
wlr_log(WLR_INFO, "%s", _("Successfully created backend"));

View file

@ -1,4 +1,5 @@
wb_src = files(
'config.c',
'cursor.c',
'decoration.c',
'layer_shell.c',
@ -10,6 +11,7 @@ wb_src = files(
)
wb_dep = [
libxml2,
wayland_server,
wlroots,
xkbcommon,

View file

@ -8,29 +8,20 @@ static bool handle_keybinding(struct wb_server *server, xkb_keysym_t sym, uint32
* Here we handle compositor keybindings. This is when the compositor is
* processing keys, rather than passing them on to the client for its own
* processing.
*
* Returns true if the keybinding is handled, false to send it to the
* client.
*/
if (modifiers & WLR_MODIFIER_CTRL && sym == XKB_KEY_Escape) {
wl_display_terminate(server->wl_display);
} else if (modifiers & WLR_MODIFIER_ALT && sym == XKB_KEY_Tab) {
struct wb_key_binding *key_binding;
wl_list_for_each(key_binding, &server->config->key_bindings, link) {
if (sym == key_binding->sym && modifiers == key_binding->modifiers)
{
if ((strcmp("NextWindow", key_binding->action) == 0)) {
/* Cycle to the next view */
if (wl_list_length(&server->views) < 2) {
return false;
}
struct wb_view *current_view = wl_container_of(
server->views.next, current_view, link);
struct wb_view *next_view = wl_container_of(
current_view->link.next, next_view, link);
focus_view(next_view, next_view->xdg_surface->surface);
/* Move the previous view to the end of the list */
wl_list_remove(&current_view->link);
wl_list_insert(server->views.prev, &current_view->link);
} else if ((modifiers & (WLR_MODIFIER_ALT|WLR_MODIFIER_SHIFT))
&& sym == XKB_KEY_ISO_Left_Tab) {
/* Cycle to the previous view */
if (wl_list_length(&server->views) < 2) {
return false;
}
struct wb_view *current_view = wl_container_of(
server->views.prev, current_view, link);
struct wb_view *prev_view = wl_container_of(
@ -39,15 +30,42 @@ static bool handle_keybinding(struct wb_server *server, xkb_keysym_t sym, uint32
/* Move the current view to the beginning of the list */
wl_list_remove(&current_view->link);
wl_list_insert(&server->views, &current_view->link);
} else if (modifiers & WLR_MODIFIER_ALT && sym == XKB_KEY_F2) {
if (fork() == 0) {
execl("/bin/sh", "/bin/sh", "-c", "(obrun || bemenu-run || synapse || gmrun || gnome-do || dmenu_run) 2>/dev/null", (char *) NULL);
return true;
}
} else {
else if ((strcmp("PreviousWindow", key_binding->action) == 0)) {
/* Cycle to the previous view */
if (wl_list_length(&server->views) < 2) {
return false;
}
struct wb_view *current_view = wl_container_of(
server->views.next, current_view, link);
struct wb_view *next_view = wl_container_of(
current_view->link.next, next_view, link);
focus_view(next_view, next_view->xdg_surface->surface);
/* Move the previous view to the end of the list */
wl_list_remove(&current_view->link);
wl_list_insert(server->views.prev, &current_view->link);
return true;
}
else if ((strcmp("Close", key_binding->action) == 0)) {
struct wb_view *current_view = wl_container_of(
server->views.next, current_view, link);
wlr_xdg_toplevel_send_close(current_view->xdg_surface);
}
else if ((strcmp("Execute", key_binding->action) == 0)) {
if (fork() == 0) {
execl("/bin/sh", "/bin/sh", "-c", key_binding->cmd, (char *) NULL);
}
return true;
}
else if ((strcmp("Exit", key_binding->action) == 0)) {
wl_display_terminate(server->wl_display);
return true;
}
}
}
return false;
}
static void keyboard_handle_modifiers(
struct wl_listener *listener, void *data) {
@ -107,21 +125,38 @@ static void handle_new_keyboard(struct wb_server *server,
keyboard->device = device;
/* We need to prepare an XKB keymap and assign it to the keyboard. */
struct xkb_rule_names rules = {
.rules = getenv("XKB_DEFAULT_RULES"),
.model = getenv("XKB_DEFAULT_MODEL"),
.layout = getenv("XKB_DEFAULT_LAYOUT"),
.variant = getenv("XKB_DEFAULT_VARIANT"),
.options = getenv("XKB_DEFAULT_OPTIONS"),
};
struct xkb_rule_names rules = {0};
if (server->config->keyboard_layout.layout)
rules.layout = server->config->keyboard_layout.layout;
else
rules.layout = getenv("XKB_DEFAULT_LAYOUT");
if (server->config->keyboard_layout.model)
rules.model = server->config->keyboard_layout.model;
else
rules.model = getenv("XKB_DEFAULT_MODEL");
if (server->config->keyboard_layout.options)
rules.options = server->config->keyboard_layout.options;
else
rules.options = getenv("XKB_DEFAULT_OPTIONS");
if (server->config->keyboard_layout.rules)
rules.rules = server->config->keyboard_layout.rules;
else
rules.variant = getenv("XKB_DEFAULT_RULES");
if (server->config->keyboard_layout.variant)
rules.variant = server->config->keyboard_layout.variant;
else
rules.variant = getenv("XKB_DEFAULT_VARIANT");
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
XKB_KEYMAP_COMPILE_NO_FLAGS);
if (keymap != NULL) {
wlr_keyboard_set_keymap(device->keyboard, keymap);
wlr_keyboard_set_repeat_info(device->keyboard, 25, 600);
}
xkb_keymap_unref(keymap);
xkb_context_unref(context);
wlr_keyboard_set_repeat_info(device->keyboard, 25, 600);
/* Here we set up listeners for keyboard events. */
keyboard->modifiers.notify = keyboard_handle_modifiers;

View file

@ -42,6 +42,7 @@ bool wb_create_backend(struct wb_server* server) {
}
bool wb_start_server(struct wb_server* server) {
init_config(server);
wl_list_init(&server->outputs);
server->new_output.notify = new_output_notify;
@ -80,6 +81,7 @@ bool wb_terminate(struct wb_server* server) {
wb_cursor_destroy(server->cursor);
wl_list_remove(&server->new_xdg_decoration.link); /* wb_decoration_destroy */
wb_seat_destroy(server->seat);
deinit_config(server->config);
wl_display_destroy_clients(server->wl_display);
wl_display_destroy(server->wl_display);
wlr_output_layout_destroy(server->output_layout);