Skip to content

Sunday, 1 January 2023

The New digiKam Documentation Website

Dear digiKam Fans and Users,

A new year brings new goals—and this year, we’re taking on a significant challenge: migrating the old digiKam documentation from the DocBook format to a modern, easier-to-maintain architecture.

After 20 years of using DocBook, we have transitioned to the Sphinx/ReStructuredText framework. This new system is not only more intuitive for documentation writers but also streamlines the entire process of creating and updating content.

Friday, 30 December 2022

Two month ago, in October 2022, KDE's GitLab made me use a two-factor authentication (2FA). Without a second factor, I was no longer able to push code, comment on merge requests, or contribute anything meaningful on KDE Invent.

For a long time, I have known I should use two-factor authentication for my important accounts; especially for those accounts used to commit code other people are executing because they trust me. But I was too lazy to have everything prepared to use the second factor.

Thanks to Ben and the KDE infrastructure team, there were no more excuses and I had to set up a secure login.

Secure login, but don't lock yourself out 

Having a second factor, it is important that I am not locked out, if my second factor is left at home or broken. Here is what I did:

  1. The first step is to choose the second factor. I decided against dedicated hardware like a Yubi key. I am familiar with one-time passwords on my mobile phone, thus, I decided to use Time-based one-time password (TOTP). This means a device or mobile app knows a secret to calculate a six-digit one-time password. The password is only valid for a couple of seconds, then a new one can be generated locally. I use Google Authenticator, but there are alternatives from less controversial vendors. Just scan the QR code and verify the secret by once entering a one-time code.
  2. I installed Keepass to store the recovery codes in case my TOTP device is broken or stolen. As a side effect, I can now use more complex passwords for less frequently used passwords.
  3. I made a backup including the Keepass database.
  4. As a side-effect, I need to use tokens for checkouts and pushes. A great opportunity to have this aspect of secure development activated, too.

Remaining open questions

Some questions remain open for me. Time will tell, how much they bug me and where I need to adjust.

Currently, I only have my personal phone as a TOTP device. Maybe I want to add more phones or my iPad. I am also unsure whether I should install and use a TOTP software on my desktop like KDE Keysmith. It is a weighting of comfort, security, and reliability.

I have not yet made up my mind, how to handel the tokens for checkout and pushes. Having them in an open document in Kate might be more harmful then using my password as in the past. I consider storing them in Keepass, too.

By-product: More security everywhere

Now that I have everything prepared on my side to use TOTP as my two-factor authentication, it was easy to use it for all the other accounts that deserve some extra protection: GitLab.com, GitHub, and independent GitLab instances hosted by FOSS projects similar to KDE Invent. Even my Google account is now protected by a TOTP mechanism and I no longer get text messages to my mobile phone. Further, I stored ten security codes from Google, just in case.

Having a security spree, I also wanted to use TOTP for my banking accounts. Unfortunately, my banks insist on the use of their own app. I think they could benefit from using open and established standards, but they decided to force us costumers to install and use their apps.

Thursday, 29 December 2022

What is a vacation without a nice side project? – My small x-mas project was to get my RISC-V Allwinner D1 Nezha board finally into a state that it shows anything on the connected HDMI-port. Any, “yeah!!!”, it works:

This picture shows qmlscene rendering a rotating yellow rectangle at 2 fps (speed is limited due to yet missing HW acceleration support).

A few words about the board

Compared to other embedded boards that I have, this one is surprisingly well documented; given you find the right webpages (list at the end). The vendor page itself is in a rather typical state, where an online translator occasionally comes handy to translate Chinese into something I understand 😉 But then, there is the really nice sunxi community wiki with has all extra information that you need.

For the device itself, some months ago I decided to get this one mostly because of the reasons price and delivery date. However, it has a big disadvantage for me when I use it for testing the KDE Yocto layers: There is only a 2D GPU on the board (namely a “G2D” unit) for which also no Kernel drivers are present at the moment (except those from the Kernel source blob by the device vendor). However, for all other parts there is impressive community work. Most notably, coming with Kernel 6.2, there is finally the HDMI driver support that made the above picture possible.

The few steps left for me

As said, there is a really good documentation about compiling a close-to-mainline Kernel and u-boot for this board. Moreover, the meta-riscv Yocto layer provides recipes to build a full device image with Kernel 5.16 and a patched/hacked u-boot (using an older boot sequence) with a nice out-of-the-box experience.

So, to get graphics/HDMI working on the board, all that was left for me was following the wiki documentation and:

  • switch to the latest Kernel fork based on 6.1 with all the relevant patches
  • and adapt U-Boot to a more recent fork that can boot this more mainline Kernel fork.

The details are summarized in this PR for the meta-riscv layer. It essentially drops as much non-mainline tweaks as possible and adapts the recipes along the way. Simple as it looks, it was quite a fun ride down the rabbit hole until I got my first booting Kernel 🙂 I really think that this tinkering around with hardware is the best way to learn its concepts. Even though I attended many talks in the past about the steps I did, it is so different when you are doing it yourself.

What comes next

Unfortunately, I do not expect any Plasma or KDE application running on the Nezha board in the sooonish future (at least not accelerated via the G2D chip)… Yet, if you recall, at FOSDEM 2019 Alistair already demonstrated Plasma running on a HiFive Unleashed board, using an expansion board for graphics. Finally, there is right now another very interesting and not too expensive RISC-V board becoming available with on-board 3D GPU and it is already on my shopping list… I am looking forward to 2023 🙂

Collected Links

Tuesday, 27 December 2022

Kraft 1.0 It is a pleasure to announce that Kraft Version 1.0 was released last week.

What is Kraft?

Kraft is free software to create office documents like offers and invoices in an efficient way. It runs on the Linux desktop and suits small businesses of all kinds.

After countless releases with version numbers 0.x, Kraft finally goes with version number 1.0 with this release. It is in production at several companies since many years, and with this release it got many more improvements that make it a really mature product.

Highlights

Lets take a look on the highlights of of version 1.0.

AppImage Support

Installing software properly is still a challenge for many users. AppImage comes to the rescue there: Single file, easy to download and start with all dependencies included makes it easy for users. For the creator, it is just one-fits-all, which makes it easy to maintain.

The new Kraft AppImage was carefully reworked and has now really all dependencies included, has a complete own icon set and was tested thoroughly. It is ready for production now.

Giro Code

Kraft now can print the EPC QR Code on invoices easily. Users just have to configure the bank account data to use, and Kraft creates the QR code automatically to be included in the documentation template.

User Manual

The Kraft user manual got great improvements again. It comes with even more and improved text, with much more screenshots and improved translations.

This is a community contributions that is so important for Kraft.

Many other Improvements

There were many other improvements: New functionality such as the new day counter for the number cycle templates (allows to have a counter in the document number that resets to 1 every day) or new modes for the watermark functionality to complete the generated PDF documents plus much more details (See Changelog).

How it continues

Kraft is up and running - and maintained. The plan is to keep the 1.0 as a kind of LTS in a stable branch, that only gets urgent fixes for stability.

For master, I am planning to do a couple of more intrusive changes that will take a bit longer.

See the roadmap discussion here.

Curious?

I hope you got a bit curious now about Kraft. Feel free to check it out using the AppImage.

If you find it an useful addition to the Linux application ecosystem that enables more users for Linux we’d very much appreciate your contribution!

Some more progress has been made on fixing my newest drawing tablet!

Fixing up the patch

So as described in the original post, I have to patch the uclogic HID driver. Let’s start by going through the process of submitting a patch upstream!

Before we even think about sending this patch upstream, I have to first - fix it. While the patch is mostly fine in it’s original form, there is one big issue I would like to tackle - which is the tablet frame buttons. Not even the original author seemed to figure out this issue, so I’m excited to jump in and figure it out.

First I want to set an end goal, which is to be able to set the tablet buttons through the new KDE Plasma 5.26 interface, like so:

Screenshot of the tablet settings page, screenshot by Nicolas Fella.

Currently if you run my new uclogic patch, for some reason it is not considered a “Pad”:

Screenshot of my tablet settings page, showing that no pads show up1.

Looking through the merge request shows.. nothing of interest. That’s because I actually needed to look at this KWin merge request which actually talks to libinput! Oh yeah, libinput - our old friend is here again. This MR mentions an event called LIBINPUT_EVENT_TABLET_PAD_BUTTON which is handled by evdev-tablet-pad.c in libinput. Huh, whats this?

...
static inline void
pad_button_set_down(struct pad_dispatch *pad,
		    uint32_t button,
		    bool is_down)
{
	struct button_state *state = &pad->button_state;

	if (is_down) {
		set_bit(state->bits, button);
		pad_set_status(pad, PAD_BUTTONS_PRESSED);
	} else {
		clear_bit(state->bits, button);
		pad_set_status(pad, PAD_BUTTONS_RELEASED);
	}
}
...

So this file seems to be handling “tablet pad” devices, which I didn’t even know were considered a separate device until now. Looking through the code reveals logic that libinput uses to decide what buttons reported by the device are meant for tablet pad usage:

...

static void
pad_init_buttons_from_kernel(struct pad_dispatch *pad,
			       struct evdev_device *device)
{
	unsigned int code;
	int map = 0;

	/* we match wacom_report_numbered_buttons() from the kernel */
	for (code = BTN_0; code < BTN_0 + 10; code++) {
		if (libevdev_has_event_code(device->evdev, EV_KEY, code))
			map_set_button_map(pad->button_map[code], map++);
	}

	for (code = BTN_BASE; code < BTN_BASE + 2; code++) {
		if (libevdev_has_event_code(device->evdev, EV_KEY, code))
			map_set_button_map(pad->button_map[code], map++);
	}

	for (code = BTN_A; code < BTN_A + 6; code++) {
		if (libevdev_has_event_code(device->evdev, EV_KEY, code))
			map_set_button_map(pad->button_map[code], map++);
	}

	for (code = BTN_LEFT; code < BTN_LEFT + 7; code++) {
		if (libevdev_has_event_code(device->evdev, EV_KEY, code))
			map_set_button_map(pad->button_map[code], map++);
	}

	pad->nbuttons = map;
}
...

So I created a list of valid pad inputs and used those to remap all of the pad buttons to valid inputs. This shouldn’t affect existing tablets (especially considering I don’t think anyone has used a tablet with this driver with more than a couple buttons).

...
/* Buttons considered valid tablet pad inputs. */
const unsigned int uclogic_extra_input_mapping[] = {
	BTN_0,
	BTN_1,
	BTN_2,
	BTN_3,
	BTN_4,
	BTN_5,
	BTN_6,
	BTN_7,
	BTN_8,
	BTN_RIGHT,
	BTN_MIDDLE,
	BTN_SIDE,
	BTN_EXTRA,
	BTN_FORWARD,
	BTN_BACK,
	BTN_B,
	BTN_A,
	BTN_BASE,
	BTN_BASE2,
	BTN_X
};
...
/*
* Remap input buttons to sensible ones that are not invalid.
* This only affects previous behavior for devices with more than ten or so buttons.
*/
const int key = (usage->hid & HID_USAGE) - 1;

if (key > 0 && key < ARRAY_SIZE(uclogic_extra_input_mapping)) {
        hid_map_usage(hi,
                        usage,
                        bit,
                        max,
                        EV_KEY,
                        uclogic_extra_input_mapping[key]);
        return 1;
}

And that’s pretty much the only major change I did to the original patch! You can see the patch in linux-input here.

However that isn’t the end of the story, as there’s one issue - libinput doesn’t think my tablet pad is a… tablet pad?

# libinput record /dev/input/event12
...
  udev:
    properties:
    - ID_INPUT=1
    - ID_INPUT_MOUSE=1
    - LIBINPUT_DEVICE_GROUP=3/28bd/91b:usb-0000:2f:00.3-4.1
...

Okay, that’s interesting - why is this a mouse? Uh oh, don’t tell me…

Fixing udev

So unfortunately before my tablet is fully functional, we have to touch another piece of critical Linux infrastructure - great. I want to dive into how libinput even decides device types, and I promise you it’s harder than it should be.

If you never touched the Linux input stack before (like I did before I started this project) you might think, “whats the big deal? the mouse says it’s a mouse, a keyboard says it’s a keyboard, whatever.” except it’s never that simple. If you’ve been reading my blog posts, you might think that’s just information evdev will hand over, which you’re wrong there too. All evdev tells userspace is what events the device will emit - that’s it2. So who figures out what is what?

Unfortunately, it’s a guessing game. No you heard me right, udev guesses what devices are. So how do we tell what devices are which? Luckily systemd ships with a hardware database, which details stuff like id vendors and manual fixes for esoteric devices. This is actually what the original patch author did in order to fix udev classifying it as a mouse:

id-input:*:input:b0003v28BDp091Be0100*
 ID_INPUT_MOUSE=0
 ID_INPUT_TABLET=1
 ID_INPUT_TABLET_PAD=1
...

However I didn’t like this solution, and it would be a pain to find the modalias for every single tablet that ever existed, that udev mistakenly categorizes. Is there a better solution? Instead I changed the logic in src/udev/udev-builtin-input_id.c. Let’s take a look at the original code path:

...
is_pointing_stick = test_bit(INPUT_PROP_POINTING_STICK, bitmask_props);
has_stylus = test_bit(BTN_STYLUS, bitmask_key);
has_pen = test_bit(BTN_TOOL_PEN, bitmask_key);
finger_but_no_pen = test_bit(BTN_TOOL_FINGER, bitmask_key) && !test_bit(BTN_TOOL_PEN, bitmask_key);
for (int button = BTN_MOUSE; button < BTN_JOYSTICK && !has_mouse_button; button++)
        has_mouse_button = test_bit(button, bitmask_key);
has_rel_coordinates = test_bit(EV_REL, bitmask_ev) && test_bit(REL_X, bitmask_rel) && test_bit(REL_Y, bitmask_rel);
has_mt_coordinates = test_bit(ABS_MT_POSITION_X, bitmask_abs) && test_bit(ABS_MT_POSITION_Y, bitmask_abs);

/* unset has_mt_coordinates if devices claims to have all abs axis */
if (has_mt_coordinates && test_bit(ABS_MT_SLOT, bitmask_abs) && test_bit(ABS_MT_SLOT - 1, bitmask_abs))
        has_mt_coordinates = false;
is_direct = test_bit(INPUT_PROP_DIRECT, bitmask_props);
has_touch = test_bit(BTN_TOUCH, bitmask_key);
has_pad_buttons = test_bit(BTN_0, bitmask_key) && has_stylus && !has_pen;
...
if (has_mt_coordinates) {
        if (has_stylus || has_pen)
                is_tablet = true;
        else if (finger_but_no_pen && !is_direct)
                is_touchpad = true;
        else if (has_touch || is_direct)
                is_touchscreen = true;
}

if (is_tablet && has_pad_buttons)
        is_tablet_pad = true;

if (!is_tablet && !is_touchpad && !is_joystick &&
        has_mouse_button &&
        (has_rel_coordinates ||
        !has_abs_coordinates)) /* mouse buttons and no axis */
        is_mouse = true;

To explain a little of context, udev has several “built-in” commands to decide what devices are what, and if any quirks or workarounds need to be applied. Notably, the input_id builtin handles classifying input types for devices if they are not already in the hardware database. If you turn on udev debug logging, you can actually see the built-ins it runs whenever you connect, say a usb device to your system. As you can see, evdev gives udev the supported event types of the device, and then udev has to figure out what the device actually is. Now let’s look at the supported event types by the tablet pad:

# libinput record /dev/input/event12
...
devices:
- node: /dev/input/event12
  evdev:
    # Name: UGTABLET 21.5 inch PenDisplay Pad
    # ID: bus 0x3 vendor 0x28bd product 0x91b version 0x100
    # Supported Events:
    # Event type 0 (EV_SYN)
    # Event type 1 (EV_KEY)
    #   Event code 256 (BTN_0)
    #   Event code 257 (BTN_1)
    #   Event code 258 (BTN_2)
    #   Event code 259 (BTN_3)
    #   Event code 260 (BTN_4)
    #   Event code 261 (BTN_5)
    #   Event code 262 (BTN_6)
    #   Event code 263 (BTN_7)
    #   Event code 264 (BTN_8)
    #   Event code 265 (BTN_9)
    #   Event code 266 ((null))
    #   Event code 267 ((null))
    #   Event code 268 ((null))
    #   Event code 269 ((null))
    #   Event code 270 ((null))
    #   Event code 271 ((null))
    #   Event code 272 (BTN_LEFT)
    #   Event code 273 (BTN_RIGHT)
    #   Event code 274 (BTN_MIDDLE)
    #   Event code 275 (BTN_SIDE)
    # Event type 2 (EV_REL)
    #   Event code 6 (REL_HWHEEL)
    #   Event code 8 (REL_WHEEL)
    #   Event code 11 (REL_WHEEL_HI_RES)
    #   Event code 12 (REL_HWHEEL_HI_RES)
    # Event type 4 (EV_MSC)
    #   Event code 4 (MSC_SCAN)
...

I encourage you to read through the original systemd code, as the bug (I think) is hilariously obvious. The tablet pad never emits any BTN_STYLUS events. In fact, my tablet is not even configured on a firmware level to ever emit any BTN_STYLUS events, because the attached log above is without my patch! Obviously this is bad, as that means possibly other tablets are wrongly accused of being mice, and we should fix that logic. That’s also what I didn’t want to simply shove a new entry into the hardware database, as this turns out to be a udev bug.

So I made a systemd PR fixing the logic, maybe that might fix your tablet too!

Tilt support

One more thing I wanted to tackle was tilt support for the pen, which the driver says it supported but I haven’t tested yet. Simple, right? (you already know the answer to that question)

The first thing to do is just evdev:

# evtest /dev/input/event11
...
Event: time 1672152523.926503, -------------- SYN_REPORT ------------
Event: time 1672152523.938496, type 3 (EV_ABS), code 0 (ABS_X), value 37529
Event: time 1672152523.938496, type 3 (EV_ABS), code 1 (ABS_Y), value 13166
Event: time 1672152523.938496, type 3 (EV_ABS), code 27 (ABS_TILT_Y), value 12
Event: time 1672152523.938496, -------------- SYN_REPORT ------------
Event: time 1672152523.952501, type 3 (EV_ABS), code 27 (ABS_TILT_Y), value 13
Event: time 1672152523.952501, -------------- SYN_REPORT ------------
Event: time 1672152523.956501, type 3 (EV_ABS), code 0 (ABS_X), value 37534
Event: time 1672152523.956501, type 3 (EV_ABS), code 1 (ABS_Y), value 13138
Event: time 1672152523.956501, type 3 (EV_ABS), code 27 (ABS_TILT_Y), value 14
Event: time 1672152523.956501, -------------- SYN_REPORT ------------
Event: time 1672152523.962497, type 3 (EV_ABS), code 27 (ABS_TILT_Y), value 15
...

So it looks like it’s reporting tilt events, that’s good. Let’s go one layer up, and check with libinput. Actually, instead of using debug-events or record, libinput actually has a handy tablet testing function called debug-tablet:

#  libinput debug-tablet
Device: UGTABLET 21.5 inch PenDisplay Pen (event11)
Tool: pen serial 0, id 0
libinput:
tip: up
  x:                 299.38 [-------------------------------------------------|-----------------------------]
  y:                 140.64 [-----------------------------------------|-------------------------------------]
  tilt x:            -54.53 [---------------|---------------------------------------------------------------]
  tilt y:             41.21 [---------------------------------------------------------|---------------------]
  dist:                0.00 [|------------------------------------------------------------------------------]
  pressure:            0.00 [|------------------------------------------------------------------------------]
  rotation:            0.00 [|------------------------------------------------------------------------------]
  slider:              0.00 [---------------------------------------|---------------------------------------]
  buttons:
evdev:
  ABS_X:           29952.00 [-------------------------------------------------|-----------------------------]
  ABS_Y:           14077.00 [-----------------------------------------|-------------------------------------]
  ABS_Z:               0.00 [|------------------------------------------------------------------------------]
  ABS_TILT_X:        -55.00 [----|--------------------------------------------------------------------------]
  ABS_TILT_Y:         41.00 [-----------------------------------------------------------------|-------------]
  ABS_DISTANCE:        0.00 [|------------------------------------------------------------------------------]
  ABS_PRESSURE:        0.00 [|------------------------------------------------------------------------------]
  buttons: BTN_TOOL_PEN

Alright that’s good, it looks like libinput is recording the tilt too. How about inside of Krita, which has tilt support? Uhhh… let’s check the tablet tester:

Screenshot of the Krita tablet tester

Wait, there’s no way to check for tilt here! That was a really weird omission, so I fixed that in Krita 5.2. Then I thought there was no way that no developer in Krita somehow didn’t have tilt information debugged somewhere, so after a bit searching I found a tool for Tablet Event Logging activated by CTRL+SHIFT+T. Using that, we can find if Krita has picked up the tilt from our pen:

$ krita
...
krita.tabletlog: "[BLOCKED 2:] MouseMove        btn: 0 btns: 0 pos:  828, 515 gpos:  859,2778 hires:  858.956, 2777.95 Source:2"
krita.tabletlog: "[       ] TabletMove       btn: 0 btns: 0 pos:  828, 516 gpos:  859,2779 hires:  859.281,    2779 prs: 0.000000 Stylus Pen id: 0 xTilt: 0 yTilt: 0 rot: 0 z: 0 tp: 0 "
krita.tabletlog: "[BLOCKED 2:] MouseMove        btn: 0 btns: 0 pos:  828, 516 gpos:  859,2779 hires:  859.281,    2779 Source:2"
krita.tabletlog: "[       ] TabletMove       btn: 0 btns: 0 pos:  828, 517 gpos:  859,2780 hires:  859.441, 2779.72 prs: 0.000000 Stylus Pen id: 0 xTilt: 0 yTilt: 0 rot: 0 z: 0 tp: 0 "
krita.tabletlog: "[BLOCKED 2:] MouseMove        btn: 0 btns: 0 pos:  828, 517 gpos:  859,2780 hires:  859.441, 2779.72 Source:2"
krita.tabletlog: "[       ] TabletMove       btn: 0 btns: 0 pos:  829, 517 gpos:  860,2780 hires:  859.562, 2780.25 prs: 0.000000 Stylus Pen id: 0 xTilt: 0 yTilt: 0 rot: 0 z: 0 tp: 0 "
...

Huh? No tilt information at all? That’s slightly worrying. However, there is a layer in-between Krita and libinput, that being the compositor - in my case KWin. I actually didn’t know until now, but KWin has a handy debug tool available by searching KWin in KRunner:

Screenshot of the KWin Debugging Tool when searched in KRunner.

Using that, we can check for the input events that it records:

Screenshot of the KWin Debug Window

Well that’s a nice consolation, it looks like KWin is picking up the tilt information from libinput too, so at least it’s not throwing it out. But why does Krita still not receive anything? For some reason, Krita receives tilt information on X11.

To explain, Krita does not interface with KWin directly. Instead, it uses standard protocols - either X or Wayland to communicate with the server or compositor respectively. So why does tilt work when Krita is running under X11 and not when running under Wayland? The trick is that it doesn’t matter, currently Krita is always running under X since it disables Wayland support for some reason. However, XWayland is still technically a Wayland client, so naturally the next place to look is how KWin sends Wayland events. That takes us to the class KWaylandServer

The protocol we care about here is the unstable “tablet” Wayland protocol, currently on version 2. It supports tilt events, so what’s the problem here? In reality, KWin was never sending the events in the first place:

...
const quint32 MAX_VAL = 65535;
tool->sendPressure(MAX_VAL * event->pressure());
tool->sendFrame(event->timestamp());
return true;
...

I actually came across this function when searching through relevant KWin merge requests, notably !3231 which has the unrealistic goal of complete tablet support in just one merge request! I cleaned up the relevant tilt support part, and submitted a new merge request which means pen tilt/rotation is supported under KDE Wayland now! Yay!

Dials

All is well and good now, right? That’s what I thought, until I realized my dials don’t work! I was so entrenched in everything else, that I completely neglected these little do-dads. To explain, these seem to be XP-PEN specific and are just glorified scrollwheels:

Picture of a XP-PEN dial.

However, these are not Wacom rings. Rings on a Wacom tablet (to my knowledge) are actual rings, which report absolute positioning like a wheel. These dials are not those, but are sending relative “scroll” events. I had considered remapping this to buttons on the evdev layer, which was a bad idea since that meant that complicated things further up. Another option was to instead, emulate a Wacom ring by disguising the relative events and turning those into EV_ABS. I also threw that idea away, because that also seems to hide the problem.

If you’re curious, the dials actually worked without my patches - because remember that udev was classifying the pad as a mouse - so libinput thought no further. But now that it’s considered a real tablet pad, it rejects scroll wheel events.

Honestly the best way to solve this issue is to - unfortunately - touch every single bit of the input stack to accommodate these devices. Again.

  • The Wayland tablet protocol must be changed to add a few new properties for dials on pads. Tracking via !122.
  • Libinput must be modified to expose these dials and correctly map them. Tracking via !845.
  • KWin must be modified to support sending dial events in support of the tablet protocol changes. Currently not tracking.
  • These dials (and we should also include rings and strips too) should be exposed in the Tablet Settings KCM too. Currently not tracking either.

As you can see, it’s going to be a complex process to support these stupid little circles, but I’m going all in. Luckily, with the rest of my patches going through - everything except for dials on the XP-PEN Artist 22R Pro device should be well supported now. In the coming months, I hope to make headway in supporting dials in KDE.


  1. The settings page is bugged when you have no pads, in 5.27 the combo box will be disabled and say “None” like it should. ↩︎

  2. Technically, there is HID device types defined but as far as I can tell that’s only used internally in the kernel. ↩︎

Saturday, 24 December 2022

So I bought an art tablet this year, the XP-Pen Artist 22R Pro which comes with a pen. This pen does not work under Linux, sometimes.

My dirty, dusty 22R tablet.

While almost every part of the tablet works, only one of the two stylus buttons is functional. One button is middle mouse click, and the other one is right mouse click. For some reason, only the first stylus button works! This is obviously troublesome, so where do we begin to fix this issue? Let me take you on a journey, if you have no experience with how input works on Linux - well that makes two of us!

Figuring Out The Problem

I work in Krita, so the first place to check is Krita’s tablet testing tool first. Looking at the log yields us something like this:

The Krita Tablet Tester
...
Stylus move X=120.37 Y=129.63 B=4 P=0.0% S=0.0 (DRAW)
Stylus release X=120.37 Y=129.63 B=0 P=0.0% S=0.0
...

According to the legend, we only care about the section named B which tells us which button is pressed. On my pen it’s:

  • 0 is released (not pressing on the screen)
  • 1 is drawing (pressed on the screen, it doesn’t count hovering)
  • 4 is the “middle mouse” button
  • The nonfunctional button does not show up at all of course.

So that at least means Krita isn’t the root cause, also testing it on the desktop or any other application yields the same result - the button is non-functional. In some cases, I’ve found the button to actually turn off the stylus entirely, but I’m not sure why yet.

How does tablet input on Wayland actually function? Let’s move down a layer and look at KWin, the Wayland compositor for KDE Plasma.

On Wayland, input is typically handled using libinput, and now even recent X versions have a libinput driver. Digging through the libinput backend on KWin doesn’t reveal anything particularly exciting, so we’ll assume KWin isn’t at fault either for now. Luckily, libinput has a built-on logging utility we can use to check what it sees!

First we want to check what devices libinput sees:

# libinput list-devices
...
Device:           UGTABLET 21.5 inch PenDisplay Mouse
Kernel:           /dev/input/event11
Group:            5
Seat:             seat0, default
Capabilities:     pointer
Tap-to-click:     n/a
Tap-and-drag:     n/a
Tap drag lock:    n/a
Left-handed:      disabled
Nat.scrolling:    disabled
Middle emulation: disabled
Calibration:      identity matrix
Scroll methods:   button
Click methods:    none
Disable-w-typing: n/a
Disable-w-trackpointing: n/a
Accel profiles:   flat *adaptive
Rotation:         n/a

Device:           UGTABLET 21.5 inch PenDisplay Keyboard
Kernel:           /dev/input/event12
Group:            5
Seat:             seat0, default
Capabilities:     keyboard
Tap-to-click:     n/a
Tap-and-drag:     n/a
Tap drag lock:    n/a
Left-handed:      n/a
Nat.scrolling:    n/a
Middle emulation: n/a
Calibration:      n/a
Scroll methods:   none
Click methods:    none
Disable-w-typing: n/a
Disable-w-trackpointing: n/a
Accel profiles:   n/a
Rotation:         n/a

Device:           UGTABLET 21.5 inch PenDisplay
Kernel:           /dev/input/event13
Group:            5
Seat:             seat0, default
Size:             477x268mm
Capabilities:     tablet
Tap-to-click:     n/a
Tap-and-drag:     n/a
Tap drag lock:    n/a
Left-handed:      n/a
Nat.scrolling:    n/a
Middle emulation: n/a
Calibration:      identity matrix
Scroll methods:   none
Click methods:    none
Disable-w-typing: n/a
Disable-w-trackpointing: n/a
Accel profiles:   none
Rotation:         n/a

So it looks like the device we’re interested in is/dev/input/event13. We can now view the debug event log:

# libinput debug-events --verbose
...
 event2   POINTER_MOTION          +25.044s       -1.66/  0.00 ( -1.00/ +0.00)
 event2   POINTER_MOTION          +25.045s       -1.66/  0.00 ( -1.00/ +0.00)
 event2   POINTER_MOTION          +25.046s       -1.87/  1.87 ( -1.00/ +1.00)
 event2   POINTER_MOTION          +25.047s       -1.87/  0.00 ( -1.00/ +0.00)
 event2   POINTER_MOTION          +25.049s       -1.38/  0.00 ( -1.00/ +0.00)
 event2   POINTER_MOTION          +25.050s       -1.38/  0.00 ( -1.00/ +0.00)
 event2   POINTER_MOTION          +25.051s       -1.66/  0.00 ( -1.00/ +0.00)
 event2   POINTER_MOTION          +25.053s       -1.38/  0.00 ( -1.00/ +0.00)
 event2   POINTER_MOTION          +25.055s       -1.11/  0.00 ( -1.00/ +0.00)
 event2   POINTER_MOTION          +25.057s       -1.11/  0.00 ( -1.00/ +0.00)
...

Oh right, there’s a lot of junk in there. We can slim it down to the only device we care about though:

# libinput debug-events --verbose --device /dev/input/event13
...
event13  TABLET_TOOL_AXIS        +9.455s               371.93*/88.01*  tilt: 8.06/15.12        pressure: 0.00
event13  TABLET_TOOL_AXIS        +9.545s               371.67*/88.07*  tilt: 8.06/15.12        pressure: 0.00
event13  TABLET_TOOL_BUTTON      +9.549s       331 (BTN_STYLUS) pressed, seat count: 1
event13  TABLET_TOOL_AXIS        +9.607s               371.38*/88.37*  tilt: 8.06/15.12        pressure: 0.00
event13  TABLET_TOOL_AXIS        +9.627s               371.38/88.56*   tilt: 8.19*/15.12       pressure: 0.00
...

As you can see, whenever I press the first stylus button it reports BTN_STYLUS, but when I press the second one it instead reports itself as BTN_TOUCH (not shown in the snippet).

Now before we go further, I just wanted to confirm whether or not the pen actually worked at all so I booted up Windows real quick to test. And it does work as expected, darn.

Going Deeper

Now it’s easy to assume libinput is the culprit and the one to be fixed, like I did. At first, I wrote up a quick patch to remap the wrong BTN_TOUCH inputs and inject proper BTN_STYLUS2 events but honestly that’s dirty. So what is beneath libinput? evdev!

So now that we know the (possible) culprit, we need to dig deeper into libinput. Like a good software project, libinput has developer documentation for us to look at.

Let’s check the event device that Linux gives us, using the handy evtest:

# evtest /dev/input/event13
...
Event: time 1671823416.485402, -------------- SYN_REPORT ------------
Event: time 1671823416.517402, type 4 (EV_MSC), code 4 (MSC_SCAN), value d0045
Event: time 1671823416.517402, type 1 (EV_KEY), code 330 (BTN_TOUCH), value 0
Event: time 1671823416.517402, type 1 (EV_KEY), code 320 (BTN_TOOL_PEN), value 0
Event: time 1671823416.517402, type 3 (EV_ABS), code 26 (ABS_TILT_X), value 0
Event: time 1671823416.517402, -------------- SYN_REPORT ------------
...
Event: time 1671823416.825414, -------------- SYN_REPORT ------------
Event: time 1671823416.835640, type 4 (EV_MSC), code 4 (MSC_SCAN), value d0045
Event: time 1671823416.835640, type 1 (EV_KEY), code 330 (BTN_TOUCH), value 1
Event: time 1671823416.835640, type 3 (EV_ABS), code 26 (ABS_TILT_X), value 29
Event: time 1671823416.835640, -------------- SYN_REPORT ------------

Uh oh, the same bug appears here too. So it’s time to sigh look deeper into the stack. Unfortunately, going deeper means stepping into the kernel. What is actually handling the input?

# lsusb -t
...
    |__ Port 4: Dev 5, If 0, Class=Hub, Driver=hub/2p, 480M
        |__ Port 1: Dev 8, If 0, Class=Hub, Driver=hub/4p, 480M
            |__ Port 3: Dev 9, If 0, Class=Human Interface Device, Driver=usbhid, 12M
            |__ Port 3: Dev 9, If 1, Class=Human Interface Device, Driver=usbhid, 12M
            |__ Port 3: Dev 9, If 2, Class=Human Interface Device, Driver=usbhid, 12M
...

It looks like the “usbhid” driver which makes sense, this is a USB HID device after all. However at this point is where I got stuck, because as far as I know there is no way to extract the specific HID driver in use (unless you know the actual module name). This is annoying, so I went digging inside of the kernel source tree for any mention of my device ID (0x091b). Nope. So in turn we can assume it’s using the generic HID driver, which is actually kind of impressive.

So what is the proper HID driver for this device? Well after some research it turns out to be uclogic which handles a bunch of UGTablet, Huion and other misc Asian tablet devices. Unfortunately, they haven’t added any support for this model but someone has.

Digimend Project

Luckily there is a project dedicated to upstreaming device drivers for these tablets, and Aren Villanuvea (also known as “kurikaesu”) known for userspace-tablet-driver-daemon has reverse engineered the tablet already. Better yet, he even implemented most of the work in uclogic already! You can see the PR here.

Now that’s where the story ends, yay - except that my device still doesn’t work. That’s because the PR never made it into Digimend, and it was closed by the author without explanation. And because it never made it into Digimend, it never landed upstream. It looks like Aren has decided it was a better use of his time to implement it into userspace instead.

The daemon is nice and all, but I would prefer kernel support for this device for numerous reasons. And what’s worse is that it’s so close to being complete that it sucks that the work hasn’t continued. Oh well…

Kernel Hacking

So now I’m trying to upstream the uclogic patch! Oh how I wish it was a bug in a higher layer… I’ll be taking over Aren’s work and trying to clean up the patch and reduce it down to just supporting the 22R Pro, since that’s the only tablet I own. Here’s what works:

  • Both stylus buttons work!
  • Pen pressure works.
  • Both dials work, although the right one is registered as horizontal scroll which is odd - that might have to be re-mappable on a higher layer.
  • The tablet buttons “work” but some of them are not registered properly as there’s more buttons than HID definitions for buttons. I might have to split this up into two devices, or something.

Now here’s my to-do list:

  • Hopefully receive Aren’s blessing to release the patches with his original ownership.
  • Figure out a fix with the tablet buttons, and contact Nicolas Fella (who is working on the re-mappable tablet buttons in 5.27) to see what needs to be done so they’re re-mappable in the new GUI.
  • Choose whether or not to contribute it directly to upstream (slow?) or back to Digimend.

If anyone is familiar with kernel contributions in the HID subsystem, or know the right people (like the ones mentioned in the article, hi!) I would love to get in contact!

Sunday, 18 December 2022

I’m happy to announce the release of Tokodon 22.11.2 (and 22.11.1 who I released earlier this month and forgot to properly announce). These releases contain mostly bug fixes but also some welcome interface improvements.

First this adds an account switcher (similar to the one Tobias Fella implemented in NeoChat). Very usefully when you need to manage multiple accounts and want to quickly switch between them.

Tokodon
Tokodon

This also change the image preview from appearing in a separate full screen window to be contained inside the window. This follow the similar change from from James Graham in NeoChat.

Preview full window mode
Preview full window mode

Joshua Goins improved the loading of media attachment and made it possible to now hide sensitive image by default using the blurhash effect. This is also using the already existing implementation of blurhash from Tobias in NeoChat and you might start to see a pattern in this release. ;)

Blurhash post
Blurhash post

Finally I added support for custom emojis in many places inside the UI. Perfect if you want to show you true verified checkmark in your profile :)

Aside from the nice new improvements, I improved the spacing in the app and while not perfect yet, I hope this makes Tokodon more enjoyable to use. Joshua Goins has also made various improvements to our internal networking code and this should offer better reliability and less prone to crash code. And I fixed an important crash on start-up that was affecting a lot of users

Finally I started adding unit tests in Tokodon and added the infrastructure to mock a Mastodon server. We now have reached 12% unit tests coverage and I hope this number will grow after each release.

And for those who prefer a full changelog, here it is:
  • Remember selected account
  • Update metadata
  • Fix rebasing issue
  • Attempt to fix Qt complaining about incomplete Post type
  • Add parents to replies made by Account::get and similar requests
  • More fixes
  • Move away from shared_ptr for Post
  • Fix double-free bug when viewing certain timeline pages
  • Add qtkeychain to .kde-ci.yml
  • Fix hide image icon missing on Android
  • View your own profile via account switcher
  • Add emoji support to page headings and profile bios
  • Fix translation extraction
  • Fix replying from the notification timeline
  • Fix notification list
  • Fix fetching the timeline twice
  • Release 22.11.2
  • Fix showing view-sensitive button too often
  • Don't have text autocomplete on login form
  • Add missing release info
  • Release 21.11.1
  • Remove usage of anchors in layout
  • Use blur hash for loading images and sensitive media
  • Improve hover effect on the card
  • Fix qt6 build
  • Fix dependency in the ci
  • Put accessible description at the bottom
  • Improve the look of cards
  • Use Kirigami.ActionToolBar
  • Allow download images
  • Full screen image like neochat
  • Add m_original_post_id for use in timeline fetch
  • Propertly reset pageStack when switching account
  • Polish NotificationPage
  • Improve layout of follow notification
  • Fix crash when switching account in the notification view
  • Fix translation catalog loading
  • Post: Fix memory leak
  • Fix off by one error in notification model
  • Posibly fix crash (second try)
  • Remove debug leftover
  • Posibly fix crash at startup
  • Improve account switcher
  • Make tap and text selection work together in PostDelegate
  • Fix wrong header url
  • Fix handling of empty displayName
  • Improve layout of PostDelegate
  • Add InteractionButton component for likes, boosts and replies
  • Add Qt 6 CI for FreeBSD and Android
  • Fix custom emoji in account lists
  • Port LoginPage to mobile form
  • Add runtime dependency for org.kde.sonnet
  • More cleanup and add autotests
  • Properly use getter and use displayNameHtml in more places
  • Implement custom emojis
  • Fix coverage badge
  • Add a refresh button for desktop devices
  • Reset message handler properly to prevent threads overwriting each other
  • Fix setInstanceUri early exit, preventing client id from being fetched
  • Add coverage badge
  • Fix reuse
  • Add a qDebug filter to remove confidential data
  • Add model tests
  • Add basic test
  • Split account in an abstract class without networking
  • Remot stray qDebug leaking token

Packager section

You can find the package on download.kde.org and it has been signed with my GPG key.

Tuesday, 11 October 2022

This year, I had the amazing opportunity to attend KDE Akademy in person for the first time! The host city was Barcelona. It is my second time visiting the city but it was my first time to attend KDE Akademy. Actually it was my first KDE event.

For KDE friends who don't know me, I mainly contribute to openSUSE, GNOME, Nextcloud, ownCloud and GNU Health. I have fewer contributions to Fedora, Ubuntu and ONLYOFFICE and a few here and there to FOSS projects.

Question. Why did you attend KDE Akademy? Two were the reasons. The first and main reason was to see the organization of the conference from the inside, since my University will host the next KDE Akademy. The second reason was to "introduce" myself to the KDE community, since I contribute to other projects. Actually, I know a person from the KDE board but community is not only one person.

The only familiar person I could meet was openSUSE's community manager. Unfortunately he couldn't attend, so he asked me to represent openSUSE. The duties were to have a booth and present something openSUSE related for 3 minutes. I had an idea to propose my friend George to do his first presentation to an open source conference and start his open source journey. He was very excited and he did it.

Day 0

There was a welcome event on Friday for us, where attendees got to know each other. Unfortunately, my flight was delayed and I arrived too late to attend the event. So I stayed at the hotel and tried to rest for my first Akademy day. I felt like going to school.

Day 1

The first thing we had to do was set up our booth. Well, the only promo material we had was stickers. I think all geeks like stickers so it was the best gift for everyone. I love stickers, not only from openSUSE but from other projects as well.
Stathis at openSUSE booth
During setting up the booth, I met the rest of the guys from the sponsors like Ubuntu, Fedora, Qt and Slim Book.

I attended quite a few interesting talks:Food at the coference wasn't the best for my taste. Maybe it's me. But the most interesting part of the conference was the fact that I had the chance to meet realy important people, developers that changed my point of view on softare developement.

You can see the first day, Room 1 here:

Day 2

After having fun the first day, I was excited for the second day. The first reason was that George and I (actually only George) will have the sponsor talk and the second reason was that the fact that the organizers would announce the place of next year's Akademy. Of cource that place is Thessaloniki and my University.

I attended quite a few interesting talks:You can see the second day, Room 1 here:
Unfortunately I didn't have any team to join the next BoFs days. I had a small hope that we could setup the working environment for the next Akademy but that didn't happen.

We didn't join the trip to the mountain. We went to see the city. It was my second time and I skipped some sites.

I really loved my first KDE Akademy. I would like to thank KDE ev that sponsored my trip to attend the Akademy.

I have a lot of stuff to work here with the organizing committee.
We are working to host you all next year.

Wednesday, 17 August 2022

KDE Akademy 2022
Happy traveller is back. Happy open source conference guy is ready for another trip. This time my destination is KDE Akademy and Barcelona. It's my first time attending to Akademy and I am soooooo excited. It's also my second time in Barcelona. Thanks to my highschool, I have been to Barcelona participating in the Erasmus + mobility program (article in Greek). According to the legent, maybe me kissing weird things in Girona worked just fine (click to see the picture).

You don't need to be a "KDE expert" to join, I know I am not. If you're interested in KDE you should really attend if you can (in person if possible), and not only the weekend of talks, but the whole week! And you should register today!

For those of you who know me, I used to attend conferences alone. This time we are 3 people from my university, the University of Macedonia. We have a newly formed Open Source Team and I would like to bring more people with me, to join global communities.

I will keep this short. More to come soon.

I would like to thank KDE and the community for the opportunity to join such a big conference. I am so happy that I will meet you in person after those 2 years of COVID-19 era.

Monday, 23 March 2020

This blog post was not easy to write as it started as a very simple thing intended for developers, but later, when I was digging around, it turned out that there is no good single resource online on copyright statements. So I decided to take this stab at writing one.

I tried to strike a good balance between 1) keeping it short and to the point for developers who just want to know what to do, and 2) FOSS compliance officers and legal geeks who want to understand not just best practices, but also the reasons behind them.

If you are extremely short on time, the TL;DR should give you the bare minimal instructions, but if you have just 2 minutes I would advise you to read the actual HowTo a bit lower below.

Of course, if you have about 20 minutes of time, the best way is always to start reading at the beginning and finish at the end.

Where else to find this article & updates

A copy of this blog is available also on Liferay Blog.
Haksung Jang (장학성) was awesome enough to publish a Korean translation.

2021-03-09 update: better wording; more info on how to handle anonymous authors and when copyright is held by employer, © and ASCII, multiple authors; DCO; easier REUSE instructions

2022-10-23 update: more FAQ entries

2023-03-28 update: a few more FAQ entries following feedback at FOSDEM and from Mastodon

TL;DR

Use the following format:

SPDX-FileCopyrightText: © {$year_of_file_creation} {$name_of_copyright_holder} <{$contact}>

SPDX-License-Identifier: {$SPDX_license_name}

… put that in every source code file and go check out (and follow) REUSE.software best practices, published by the FSFE.

E.g. for a file that I created today and I released under the BSD-3-Clause license, I would use put the following as a comment at the top of the source code file:

SPDX-FileCopyrightText: © 2020 Matija Šuklje <matija@suklje.name>

SPDX-License-Identifier: BSD-3-Clause

Introduction and copyright basics

Copyright is automatic (since the Berne convention) and any work of authorship is automatically protected by it – essentially giving the copyright holder1 exclusive power over their work. In order for your downstream to have the rights to use any of your work – be that code, text, images or other media – you need to give them a license to your work.

So in order for you to copy, implement, modify etc. the code from others, you need to be given the needed rights – i.e. a license2 –, or make use of a statutory limitation or exception3. And if that license has some obligations attached, you need to meet them as well.

In any case, you have to meet the basic requirements of copyright law as well. At the very least you need to have the following two in place:

  • attribution – list the copyright holders and/or authors – especially in jurisdictions which recognise moral rights (e.g. most of EU) it is important to keep the names of authors, if they are listed;
  • license(s) – since a license is the only thing that gives anybody other than the copyright holder themself the right to use the code, you are very well advised to have a notice of the the license and its full text present – this goes for both for your outbound licenses and the inbound licenses you received from others by using 3rd party works, such as copied code or libraries.

Inbound vs. outbound licenses

The license you give to your downstream is called an outbound license, because it handles the rights in the code that flow out of you. In turn that same license in the same work would then be perceived by your downstream as their inbound license, as it handles the rights in the code that flows into them.

In short, licenses describing rights flowing in are called inbound licenses, and the licenses describing rights flowing out are called outbound licenses.

The good news is that attribution is a discretionary right that can be exercised by the author should they choose to. And you are obliged to keep the attribution notices only insofar as the author(s) made use of that right. Which means that if the author has not listed themselves, you do not have to hunt them down yourself.

Why have the copyright statement?

Which brings us to the question of whether you need to write your own copyright statement4.

First, some very brief history …

The urge to absolutely have to write copyright statements stems from the inertia in the USA, as it only joined the Berne convention in 1989, well after computer programs were a thing. Which means that before then the US copyright law still required an explicit copyright statement in order for a work to be protected.

Copyright statements are useful

The copyright statement is not required by law, but in practice very useful as proof, at best, and indicator, more likely, of what the copyright situation of that work is. This can be very useful for compliance reasons, traceability of the code etc.

Attribution is practically unavoidable, because a) most licenses explicitly call for it, and if that fails b) copyright laws of most jurisdictions require it anyway.

And if that is not enough, then there is also c) sometimes you will want to reach the original author(s) of the code for legal or technical reasons.

So storing both the name and contact information makes sense for when things go wrong. Finding the original upstream of a runaway file you found in your codebase – if there are no names or links in it – is a huge pain and often includes (currently still) expensive specialised software. I would suspect the onus on a FOSS project to be much lower than on a corporation in this case, but still better to put a little effort upfront than having to do some serious archæology later.

How to write a good copyright statement and license notice

Finally we come to the main part of this article!

A good copyright statement should consist of the following information:

  • start with the © sign;
  • the year of the first publication – a good date would be the year in which you created the file and then do not touch that date anymore;
  • the name of the copyright holder – typically the author, but depending on the circumstances might be their employer or if there is a CLA in place the legal entity or person they transferred their rights to;
  • a valid contact to the copyright owner

As an example, this is what I would put on something I wrote today:

© 2020 Matija Šuklje <matija@suklje.name>

While you are at it, it would make a lot of sense to also notify everyone which license you are releasing your code under as well. Using an SPDX ID is a great way to unambiguously state the license of your code. (See note mentioned below for an example of how things can go wrong otherwise.)

And if you have already come so far, it is just a small step towards following the best practices as described by REUSE.software by using SPDX tags to make your copyright statement (marked with SPDX-FileCopyrightText) and license notice (marked with SPDX-License-Identifier and followed by an SPDX ID).

Here is now an example of a copyright statement and license notice that check all the above boxes and also complies with both the SPDX and the REUSE.software specifications:

SPDX-FileCopyrightText: © 2020 Matija Šuklje <matija@suklje.name>

SPDX-License-Identifier: BSD-3-Clause

Now make sure you have these in comments of all your source code files.

Q&A

Over the years, I have heard many questions on this topic – both from developers and lawyers.

I will try to address them below in no particular order.

If you have a question that is not addressed here, do let me know and I will try to include it in an update.

Why keep the year?

Some might argue that for the sake of simplicity it would be much easier to maintain copyright statements if we just skip the years. In fact, that is a policy at Microsoft/GitHub at the time of this writing.

While I agree that not updating the year simplifies things enormously, I do think that keeping a date helps preserve at least a vague timeline in the codebase. As the question is when the work was first expressed in a medium, the earliest date provable is the time when that file was first created.

In addition, having an easy way to find the earliest date of a piece of code, might prove useful also in figuring out when an invention was first expressed to the general public. Something that might become useful for patent defense.

This is also why e.g. in Liferay our new policy is to write the year of the file creation, and then not change the year any more.

Innocent infringement excursion for legal geeks

17 U.S. Code § 401.(d) states that if a work carries a copyright notice in the form that the law prescribes, in a copyright infringement case the defendant cannot rely on the innocent infringement defense, except if they had reason to believe their use was covered fair use. And even then, the innocent infringer would have to be e.g. a non-profit broadcaster or archive to be still eligible to such defence.

So, if you are concerned with copyright violations (at least in USA), you may actually want to make sure your copyright statements include both the copyright sign and year of publication.

See also note in Why the © sign for how a copyright notice following the US copyright act looks like.

Why not bump the year on change?

I am sure you have seen something like this before:
Copyright (C) 1992, 1995, 2000, 2001, 2003 CompanyX Inc.

The presumption behind this is that whenever you add a new year in the copyright statement, the copyright term would start anew, and therefore prolong the time that file would be protected by copyright.

Adding a new year on every change – or, even worse, simply every 1st January – is a practice still too wide-spread even today. Unfortunately, doing this is useless at best, and misleading at worst. Needless to say, if you do this as part of your build process, this is extra wrong. For the origin of this myth see the short history above.

A big problem with this approach is that not every contribution is original or substantial enough to be copyrightable – even the popular 5 (or 10, or X) SLOC rule of thumb5 is legally-speaking very debatable.

So, in order to keep your copyright statement true, you would need to make a judgement call every time whether the change was substantial and original enough to be granted copyright protection by the law and therefore if the year should be bumped. And that is a substantial test for every time you change a file.

On the other hand copyright lasts at least 50 (and usually 70) years6 after the death of the author; or if the copyright holder is a legal entity (e.g. CompanyX Inc.), since publication. So the risk of your own copyright expiring under your feet is very very low.

Worst case thought experiment

Let us imagine the worst possible scenario now:

1) you never bump the year in a copyright statement in a file and 2) 50+ years after its initial release, someone copies your code as if it were in public domain. Now, if you would have issue with that and go to court, and 3) the court would (very unlikely) take only the copyright statements in that file into account as the only proof and based on that 4) rule that the code in that file would have fallen under public domain and therefore the FOSS license would not apply to it any more.

The end result would simply be that (in one jurisdiction) that file would fall into public domain and be up for grabs by anyone for anything, no copyright, no copyleft, 50+ years from the file’s creation (instead of e.g. 5, maybe 20 years later).

But, honestly, how likely is it that 50 years from now the same (unaltered) code would still be (commercially) interesting?

… and if it turns out you do need to bump the year eventually, you still have, at worst, 50 years to sort it out – so, ample opportunity to mitigate the risk.

In addition to that, as typically a single source code file is just one of the many cogs in a bigger piece of software, what you are more concerned with is the software product/project as a whole. As the software grows, you will keep adding new files, and those will obviously have newer years in them. So the codebase as a whole work will already include copyright statements with newer years in it anyway.

Keep the Git/VCS history clean

Also, bumping the year in all the files every year messes with the usefulness of the Git/VCS history, and makes the log unnecessarily long(er) and the repository consumes more space.

It makes all the files seem equally old (in years), which makes it hard to identify stale code if you are looking for it.

Another issue might be that your year-bumping script can be too trigger-happy and bump the years also in the files that do not even belong to you. Furthering misinformation both in your VCS and the files’ copyright notices.

Do not bump the year during build time

Bumping the year manually is bad, but automating year bumping during build time is taking it to another level!

One could argue – and I suspect this is where it originates from – that since compiling is translation and as such an exclusive right of the copyright holder. But while translation from one programming language to another clearly can take a lot of mental effort and might require different ways how to express something, a machine-compilation from human-readable source code to machine-readable object/binary code per se is extremely unlikely to have added a new copyrightable component into the mix. That would be like saying an old song would gain new copyright just because it was released in a new audio format without any other changes.

Bumping the year during build time also messes up reproducible builds.

Why not use a year range?

Similar to the previous question, the year span (e.g. 1990-2013) is basically just a lazy version of bumping the year. So all of the above-mentioned applies.

A special case is when people use a range like {$year}-present. This has almost all of the above-mentioned issues7, plus it adds another dimension of confusion, because what constitutes the “present” is an open – and potentially philosophical – question. Does it mean:

  • the time when the file was last modified?
  • the time it was released as a package?
  • the time you downloaded it (maybe for the first time)?
  • the time you ran it the last time?
  • or perhaps even the ever eluding “right now”?

As you can see, this does not help much at all. Quite the opposite!

But doesn’t Git/Mercurial keep a better track?

Not reliably.

Git (and other VCS) are good at storing metadata, but you should be careful about it.

Git does have an Author field, which is separate from the Committer field. But even if we were to assume – and that is a big assumption8 – Git’s Author was the actual author of the code committed, they may not be the copyright holder.

Furthermore, the way git blame and git diff currently work, is line-by-line and using the last change as the final author, making Git suboptimal for finding out who actually wrote what.

Token-based blame information

For a more fine-grained tool to see who to blame for which piece of code, check out cregit.

And ultimately – and most importantly – as soon as the file(s) leave the repository, the metadata is lost. Whether it is released as a tarball, the repository is forked and/or rebased, or a single file is simply copied into a new codebase, the trace is lost.

All of these issues are addressed by simply including the copyright statement and license information in every file. REUSE.software best practices handle this very well.

Why the © sign?

Some might argue that the English word “Copyright” is so common nowadays that everyone understands it, but if you actually read the copyright laws out there, you will find that using © (i.e. the copyright sign) is the only way to write a copyright statement that is common in copyright laws around the world9.

Using the © sign makes sense, as it is the the common global denominator.

Comparison between US and Slovenian copyright statements

As an EU example, the Slovenian ZASP §175.(1) simply states that holders of exclusive author’s rights may mark their works with a (c)/© sign in front of their name or firm and year of first publication, which can be simply put as:

© {$year_of_first_publication} {$name_of_author_or_other_copyright_holder}

On the other side of the pond, in the USA, 17 U.S. Code § 401.(b) uses more words to give a more varied approach, and relevant for this question in §401(b)(1) prescribes the use of

the symbol © (the letter C in a circle), or the word “Copyright”, or the abbreviation “Copr.”;

The rest you can go read yourself, but can be summarised as:

(©|Copyright|Copr.) {$year_of_first_publication} {$name_or_abreviation_of_copyright_holder}

See also the note in Why keep the year for why this can matter in front of USA courts.

While the © sign is a pet peeve of mine, from the practical point of view, this is the least important point here. As we established in the introduction, copyright is automatic, so the actual risk of not following the law by its letter is pretty low if you write e.g. “Copyright” instead.

© sign and ASCII

While Unicode (UTF-8, UTF-16, …) is pretty much ubiquitous nowadays, there are places and reasons for when the encoding of source code will have to be limited to a much simpler one, such as ASCII. This could be e.g. in case when the code is written to be put into small embedded devices where every bit counts.

The © character was introduced in 8-bit extended ASCII, but the original 7-bit ASCII does not have it.

So if this is the situation you are in, it is fine to either ommit the copyright sign or replace it with e.g. (C) or Copyright.

Why leave a contact?

A contact is in no way required by copyright law, but from practical reasons can be extremely useful.

It can happen that you need to access the author and/or copyright holder of the code for legal or technical question. Perhaps you need to ask how the code works, or have a fix you want to send their way. Perhaps you found a licensing issue and want to help them fix it (or ask for a separate license). In all of these cases, having a contact helps a lot.

As pretty much all of internet still hinges on the e-mail10, the copyright holder’s e-mail address should be the first option. But anything really goes, as long as that contact is easily accessible and actually in use long-term.

Avoiding orphan works

For the legal geeks out there, a contact to the copyright holder mitigates the issue of orphan works.

There will be cases where the authorship will be very dispersed or lie with a legal entity instead. In those cases, it might be more sense to provide a URL to either the project’s or legal entity’s homepage and provide useful information there. If a project lists copyright holders in a file such as AUTHORS or CONTRIBUTORS.markdown a permalink to that file (in the master) of the publicly available repository could also be a good URL option.

How to handle multitudes of authors?

Here are two examples of what you can write in case the project (e.g. Project X) has many authors and does not have a CAA or exclusive CLA in place to aggregate the copyright in a single entity:

© 2010 The Project X Authors <https://projectx.example/about/authors>

© 1998 Contributors to the Project X <https://git.projectx.example/ProjectX/blob/master/CONTRIBUTORS.markdown>

An an example of when the project is handled by a non-profit NGO legal entity.

© 2020 BestProjectNGO <https://bestprojectngo.example>

Bot to automate contributions

A really interesting project is All Contributors, which specifies how to manage contributions to all – even non-code – contributions to a project. It also includes a CLI tool and offers a GitHub bot to automate this process.

The major downside is that the prescribed format is an HTML table embedded in MarkDown. So not very easy to read or parse in source form.

What if I added code to an existing project?

A major benefit of FOSS is that people collaborate on the same project, so it is inevitable that several people will be touching the same file. If that file already includes a copyright statement, this is a good question.

If there are only a handful of people who wrote that file, it would be fine to just add a new line with your copyright statement, as such:

SPDX-FileCopyrightText: © 2018 Matija Šuklje <matija@suklje.name>
SPDX-FileCopyrightText: © 2021 Master Hacker <mh@example.org>

But if there are many authors that would need to be added that way, to avoid clutter, it would make sense to instead create an AUTHORS.* or CONTRIBUTORS.* file as described in the question above.

What about public domain?

Public domain is tricky.

In general the public domain are works to which the copyright term has expired11.

While in some jurisdictions (e.g. USA, UK) you can actually waive your copyright and dedicate your work to public domain, in most jurisdiction (e.g. most of EU member countries) that is not possible.

Which means that depending on the applicable jurisdiction, it may be that although an author wrote that they dedicate their work into public domain this does not meet the legal standard for it to actually happen – they retain the copyright in their own work.

Unsurprisingly, FOSS compliance officers and other people/projects who take copyright and licensing seriously are typically very wary of statements like “this is public domain”.

This can be mitigated in two ways:

  • instead of some generic wording, when you want to dedicate something to public domain use a tried and tested public copyright waiver / public domain dedication with a very permissive license, such as 0BSD for code or CC0-1.0 for non-code; and
  • include your name and contact if you are the author in the SPDX-FileCopyrightText: field – 1) because in doubt that will associate you with your dedication to the public domain, and 2) in case anything is unclear, people have a contact to you.

This makes sense to do even for files that you deem are not copyrightable, such as config files – if you mark them as above, everyone will know that you will not exercise your author’s rights (if they existed) in those files.

It may seem a bit of a hassle for something you just released to the public to use however they see fit, without people having to ask you for permission. I get that, I truly do! But do consider that if you already put so much effort into making this wonderful stuff you and donating it to the general humanity, it would be a huge pity that, for (silly) legal details, in the end people would not (be able to) use it at all.

What about minified JS?

Modern code minifiers/uglifiers tend to have an optional flag to preserve copyright and licensing info, even when they rip out all the other comments.

The copyright does not simply go away if you minify/uglify the code, so do make sure that you use a minifier that preserves both the copyright statement as well as the license (at least its SPDX Identifier) – or better yet, the whole REUSE-compliant header.

Transformations of code

Translations between different languages, compilations and other transformations are all exclusive rights of the copyright owner. So you need a valid license even for compiling and minifying.

What is wrong with “All rights reserved”?

Often you will see “all rights reserved” in copyright statements even in a FOSS project.

The cause of this, I suspect, lies again from a copycat behaviour where people tend to simply copy what they so often found on a (music) CD or in a book. Again, the copyright law does not ask for this, even if you want to follow the fullest formal copyright statement rules.

But what it does bring, is confusion.

The statement “all rights reserved” obviously contradicts the FOSS license the same file is released under. The latter gives everyone the rights to use, study, share and improve the code, while the former states that all of these rights the author reserves to themself.

So, as those three words cause a contradiction, and do not bring anything useful to the table in the first place, you should not write them in vain.

Practical example

Imagine12 a FOSS project that has a copy of the MIT license stored in its LICENSE file and (only) the following comment at the top of all its source code files:

# This file is Copyright (C) 1997 Master Hacker, all rights reserved.

Now imagine that someone simply copies one file from that repository/archive into their own work, which is under the AGPL-3.0-only license, and this is also what it says in the LICENSE file in the root of its own repository. And you, in turn, are using this second person’s codebase.

According to the information you have at hand:

  • the copyright in the copied file is held by Master Hacker;
  • apparently, Mr Hacker reserves all the rights they have under copyright law;
  • if you felt like taking a risk, you could assume that the copied file is under the AGPL-3.0-or-later license – which is false, and could lead to copyright violation13;
  • if you wanted to play it safe, you could assume that you have no valid license to this file, so you decide to remove it and work around it – again false and much more work, but safe;
  • you could wait until 2067 and hope this actually falls under public domain by then – but who has time for that.

This example highlights both how problematic the wording of “all rights reserved” can be even if there is a license text somewhere in the codebase.

This can be avoided by using a sane copyright statement (as described in this blog post) and including an unambiguous license ID. REUSE.software ties both of these together in an easy to follow specification.

What if I work for a company, NGO, university?

In many jurisdictions if you are in an employment relationship (at least full employment), your employer would be the one holding the relevant rights.

If the revelant jurisdiction is Slovenian (as an EU example), ZASP §101 (unofficial English translation) says the following:

(1) When copyright work is created by an employee in the execution of his duties or following the instructions given by his employer (copyright work created in the course of employment), it shall be deemed that the economic rights and other rights of the author to such work are exclusively assigned to the employer for the period of ten years from the completion of the work, unless otherwise provided by contract.

(2) On the expiration of the term mentioned in the foregoing paragraph, the rights mentioned in the foregoing paragraph revert to the employee, however, the employer can claim a new exclusive assignment of these rights, for adequate remuneration.

If the relevant jurisdiction is USA this would fall under “work for hire” and the employer would be the copyright holder of any work their employee makes that are within the scope of their employment. There are also other cases where “work for hire” kicks in, but the sloppy rule of thumb is that if the closer the work’s creation was controlled by the employer/hiring party, the more likely it would be the copyright holder.

In any case, if your contract says you are transferring the rights to your employer or the other party, then they would be the copyright holder (e.g. in USA) or at least the exclusive rights holder (e.g. most of EU).

On a similar note, an author / copyright holder / exclusive right holder can transfer the rights they have to another person by written agreement.

What if I want to stay anonymous?

Whether you want to sign your work with your legal name, a pseudonym14 or even not at all is your own decision as author.

But do take into consideration that if you want to stay anonymous, you will have a much harder time proving you are the author of that piece of code later. For this reason, it would make sense to release your anonymous code under a “public-domain-like” license such as CC0-1.0 or Unlicense.

In any case, unless you have good reasons not to (e.g. for your personal safety), it would be really useful to use the copyright tag to at least include a contact. In case you want to just use a pseudonym, that should not be much of an issue. But in the case you want to stay anonymous, the contact could be simply the URL to the project’s homepage and instead of your name you could state the name of the project, or leave it empty.

Anonymity and Git

If you are concerned about anonymity, do take into consideration also that Git stores both author and committer data for each commit. Look into how to keep those records in a way that they cannot be linked to you.

My project uses DCO. Does this conflict with it?

Not at all. Quite the opposite!

When signing the DCO 1.1, you state that you are contributing under the license as stated in the file. If the file you contributed (to), includes an SPDX license tag, that supports the DCO.

While signing the DCO typically requires you to use git commit --signoff when you commit, so it stores your agreement with DCO in the repository history, if a file is copied outside that git repository that information, along with your authorship information is lost. So it makes sense to include your copyright statement and contact in each file even if you sign a DCO.

How do I find out the date of file creation?

If you are creating a new file, this is trivial, as you just need to enter the current year (e.g. with date +Y).

But if you are adding your copyright statements to your existing software, that might indeed be a bit more tricky.

Luckily, if your project uses a VCS and all its history is tracked in it, you can find the date of the first commit for each file. If using Git, the following command will output you the year the file was first authored:

git log --follow --format=%as {$path} | tail -n1 | cut -c-4

Failing that, you could check with your filesystem (e.g. for EXT4), but this can be of very questionable quality, if you know the file landed on your disk at a later date, you changed disks etc.

If even that is not a viable possibility, just use your best judgement.

That is a tricky question, and probably depends on the jurisdiction in question.

This analysis tries to answer those questions from the Slovenian jurisdiction.

What about if I merge or split a file or just use a snippet?

In case you copy just a part of a file (assuming that part is copyrightable) into another file, you can put retain/copy its licensing metadata by wrapping its SPDX/REUSE tags between an SPDX-SnippetBegin and an SPDX-SnippetEnd tag. For more details see the Snippet tags format annex of the SPDX specification.

An example would se as follows:

# SPDX-SnippetBegin

# SPDX-FileCopyrightText: © 2020 Matija Šuklje <matija@suklje.name>
# SPDX-License-Identifier: BSD-3-Clause

...
import sense
lots_of_cool_code()
...

# SPDX-SnippetEnd

You can use this also when e.g. concattenating different JS files into one.

In any case, unless you are the copyright holder, do not remove or alter other people’s copyright statements. You can always add a new one, if it is needed.

hook out → hat tip to the TODO Group for giving me the push to finally finish this article and Carmen Bianca Bakker, Robbie Morrison, as well as the Legal Network for some very welcome feedback


  1. This is presumed to be the author at least initially. But depending on circumstances can be also some other person, a legal entity, a group of people etc. See also this FAQ entry for more info. 

  2. A license is by definition “[t]he permission granted by competent authority to exercise a certain privilege that, without such authorization, would constitute an illegal act, a trespass or a tort.” 

  3. Limitations and exceptions (or fair use/dealings in USA/Canada/UK) in copyright are extremely limited when it comes to software compared to more traditional media. Do not rely on them. 

  4. In USA, the copyright statement is often called a copyright notice. The two terms are used intercheangably. 

  5. E.g. the 5 SLOC rule of thumb means that any contribution that is 5 lines or shorter, is (likely) too short to be deemed copyrightable, and therefore can be treated as un-copyrightable or as in public domain; and on the flip-side anything longer than 5 lines of code needs to be treated as copyrightable. This rule can pop up when a project has a relatively strict contribution agreement (a CLA or even CAA), but wants some leeway to accept short fix patches from drive-by contributors. The obvious problem with this is that on one hand someone can be very original even in 5 lines (think haiku), while one can also have pages and pages of absolute fluff or just plain raw factual numbers. 

  6. This depends from jurisdiction to jurisdiction. The Berne convention stipulates at least 50 years after death of the author as the baseline. There are very few non-signatory states that have shorter terms, but the majority of countries have life + 70 years. The current longest copyright term is life + 100 years, in Mexico. 

  7. The only improvement is that it avoids messing up the Git/VCS history. 

  8. In practice what the Author field in a Git repository actually includes varies quite a bit and depends on how the committer set up and used Git. 

  9. Of course, I did not go through all of the copyright laws out there, but I checked a handful of them in different languages I understand, and this is the pattern I identified. If anyone has a more thorough analysis at hand, please reach out and I will happily include it. 

  10. Just think about it, pretty much every time you create a new account somewhere online, you are asked for your e-mail address, and in general people rarely change their e-mail address. 

  11. As stated before, in most jurisdictions that is 70 years after the death of the author. 

  12. I suspect many of the readers not only can imagine one, but have seen many such projects before ;)

  13. Granted, MIT code embedded into AGPL-3.0-or-later code is less risky than vice versa. But simply imagine what it would be the other way around … or with an even odder combination of licenses. 

  14. A(n identifiable) pseudonym, under copyright law, has basically the same power as a legal name. Think of all the musicians, actors and writers that we know under their pseudonym or stage name.