Skip to content

Commit

Permalink
database: support $XDG_CONFIG_HOME/libwacom as additional path
Browse files Browse the repository at this point in the history
This completes the traditional triplet of $XDG_CONFIG_HOME, /etc, and
/usr/share for configuration files.

Having custom .tablet files in $XDG_CONFIG_HOME makes it easier for
immutable systems and also for backups that only back up the user home
directory.
  • Loading branch information
whot committed Oct 29, 2024
1 parent 656012f commit 99f7c0e
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 21 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ jobs:
- sudo mv /usr/share/libwacom/wacom-intuos*.tablet /etc/libwacom
- sudo mv /usr/share/libwacom/*.tablet /etc/libwacom
- sudo mv /usr/share/libwacom/*.stylus /etc/libwacom
- sudo mv /usr/share/libwacom/*.tablet $HOME/.config/libwacom
- sudo mv /usr/share/libwacom/*.stylus $HOME/.config/libwacom
- sudo mv /usr/share/libwacom/wacom-*.tablet $HOME/.config/libwacom && sudo mv /usr/share/libwacom/huion-*.tablet /etc/libwacom
# split the wacom.stylus file into to two files to check for
# accumlated loading
- sudo csplit data/wacom.stylus '/^\[0x822\]/' && sudo mv xx00 /etc/libwacom/first.stylus && sudo mv xx01 /usr/share/libwacom/wacom.stylus
Expand All @@ -141,6 +144,7 @@ jobs:
- name: list devices with database in /usr
run: libwacom-list-devices --format=datafile > devicelist.default.txt
- run: sudo mkdir /etc/libwacom
- run: sudo mkdir $HOME/.config/libwacom
- name: split the databases between /usr/share and /etc
run: ${{matrix.command}}
- name: list devices with database in /etc and /usr
Expand Down
17 changes: 16 additions & 1 deletion libwacom/libwacom-database.c
Original file line number Diff line number Diff line change
Expand Up @@ -1192,13 +1192,28 @@ libwacom_database_new_for_path (const char *datadir)
LIBWACOM_EXPORT WacomDeviceDatabase *
libwacom_database_new (void)
{
WacomDeviceDatabase *db;
char *xdgdir = NULL;
char *xdg_config_home = g_strdup(g_getenv("XDG_CONFIG_HOME"));

if (!xdg_config_home)
xdg_config_home = g_strdup_printf("%s/.config/", g_get_home_dir());

xdgdir = g_strdup_printf("%s/libwacom", xdg_config_home);

char *datadir[] = {
xdgdir,
ETCDIR,
DATADIR,
NULL,
};

return database_new_for_paths(datadir);
db = database_new_for_paths(datadir);

free(xdgdir);
free(xdg_config_home);

return db;
}

LIBWACOM_EXPORT void
Expand Down
21 changes: 21 additions & 0 deletions test/test_libwacom.py
Original file line number Diff line number Diff line change
Expand Up @@ -600,3 +600,24 @@ def test_nonwacom_stylus_ids(tmp_path):
assert sum(s.vendor_id == 0x1234 and s.tool_id == 0x9876 for s in styli) == 1
assert sum(s.vendor_id == 0 and s.tool_id == 0xFFFFE for s in styli) == 1
assert sum(s.vendor_id == 0 and s.tool_id == 0xFFFFF for s in styli) == 1


def test_load_xdg_config_home(monkeypatch, tmp_path, custom_datadir):
monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path.absolute()))

xdg = tmp_path / "libwacom"
xdg.mkdir()

usbid = (0x1234, 0x5678)
matches = [f"usb|{usbid[0]:04x}|{usbid[1]:04x}"]
TabletFile(name="XDGTablet", matches=matches).write_to(xdg / "uniq.tablet")

StylusFile.default().write_to_dir(xdg)

# This should load from system *and* XDG. system files could
# interfere with our test or may not exist but unfortunately we can't
# chroot for the test. it should be good enough this way anyway.
db = WacomDatabase()
builder = WacomBuilder.create(usbid=usbid)
device = db.new_from_builder(builder)
assert device is not None and device.name == "XDGTablet"
66 changes: 47 additions & 19 deletions tools/libwacom-update-db.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,17 @@

import argparse
import configparser
import os
import sys
import subprocess
import tempfile
from pathlib import Path


def xdg_dir():
return Path(os.getenv("XDG_CONFIG_HOME", Path.home() / ".config")) / "libwacom"


class Tablet(object):
def __init__(self, name, bus, vid, pid):
self.name = name
Expand Down Expand Up @@ -154,19 +160,20 @@ def _load(self, path):

# Guess the udev directory based on path. For the case of /usr/share, the
# udev directory is probably in /usr/lib so let's fallback to that.
def find_udev_base_dir(path):
for parent in path.parents:
d = Path(parent / "udev" / "rules.d")
if d.exists():
return d.parent
def find_udev_base_dir(paths):
for path in paths:
for parent in path.parents:
d = Path(parent / "udev" / "rules.d")
if d.exists():
return d.parent

# /usr/share but also any custom prefixes
for parent in path.parents:
d = Path(parent / "lib" / "udev" / "rules.d")
if d.exists():
return d.parent
# /usr/share but also any custom prefixes
for parent in path.parents:
d = Path(parent / "lib" / "udev" / "rules.d")
if d.exists():
return d.parent

raise FileNotFoundError(path)
raise FileNotFoundError(paths)


# udev's behaviour is that where a file X exists in two locations,
Expand All @@ -190,8 +197,8 @@ def guess_hwdb_filename(basedir):
"path",
nargs="?",
type=Path,
default="/etc/libwacom",
help="Directory to load .tablet files from",
default=None,
help="Directory to load .tablet files from. Default: $XDG_CONFIG_HOME/libwacom and /etc/libwacom",
)
# buildsystem-mode is what we use from meson, it changes the
# the behavior to just generate the file and print it
Expand All @@ -215,7 +222,14 @@ def guess_hwdb_filename(basedir):
)
ns = parser.parse_args()

db = TabletDatabase(ns.path)
if ns.path is None:
paths = [xdg_dir(), Path("/etc/libwacom")]
else:
paths = [ns.path]

# Reverse the list so the most important one is last and takes precedence
paths.reverse()
dbs = [TabletDatabase(p) for p in paths]

hwdb = HWDBFile()
# Bamboo and Intuos devices connected to the system via Wacom's
Expand All @@ -230,20 +244,32 @@ def guess_hwdb_filename(basedir):
wwak.has_touch = True
hwdb.tablets.append(wwak)

hwdb.tablets.extend(db.tablets)
for db in dbs:
hwdb.tablets.extend(db.tablets)

if ns.buildsystem_mode:
hwdb.print()
else:
if os.geteuid() == 0:
print(
"WARNING: Running this command as root will not pick up .tablet files in $XDG_CONFIG_HOME/libwacom"
)

try:
udevdir = ns.udev_base_dir or find_udev_base_dir(ns.path)
udevdir = ns.udev_base_dir or find_udev_base_dir(paths)
hwdbfile = guess_hwdb_filename(udevdir)
with open(hwdbfile, "w") as fd:

with tempfile.NamedTemporaryFile(
mode="w+", prefix=f"{hwdbfile.name}-XXXXXX", encoding="utf-8"
) as fd:
hwdb.print(fd)
print(f"New hwdb file: {hwdbfile}")
print(f"Using sudo to copy hwdb file to {hwdbfile}")
subprocess.run(["sudo", "cp", f"{fd.name}", hwdbfile.absolute()])

if not ns.skip_systemd_hwdb_update:
print("Using sudo to run systemd-hwdb update")
subprocess.run(
["systemd-hwdb", "update"],
["sudo", "systemd-hwdb", "update"],
capture_output=True,
check=True,
text=True,
Expand All @@ -255,3 +281,5 @@ def guess_hwdb_filename(basedir):
print(f"Unable to find udev base directory: {e}")
except subprocess.CalledProcessError as e:
print(f"hwdb update failed: {e.stderr}")
except KeyboardInterrupt:
pass
7 changes: 6 additions & 1 deletion tools/show-stylus.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import argparse
import configparser
import os
import sys
from pathlib import Path

Expand All @@ -37,6 +38,10 @@
sys.exit(1)


def xdg_dir():
return Path(os.getenv("XDG_CONFIG_HOME", Path.home() / ".config")) / "libwacom"


class Ansi:
clearline = "\x1b[K"

Expand Down Expand Up @@ -133,7 +138,7 @@ def record_events(ns):
def load_data_files():
lookup_paths = (
("./data/",),
("@DATADIR@", "@ETCDIR@"),
("@DATADIR@", "@ETCDIR@", xdg_dir()),
("/usr/share/libwacom/", "/etc/libwacom/"),
)
stylusfiles = []
Expand Down

0 comments on commit 99f7c0e

Please sign in to comment.