HIKARI(1) 2.3.3 | hikari - Wayland compositor


hikari - Wayland Compositor


hikari [-vh] [-a <executable>] [-c <config>]


hikari is a stacking Wayland compositor with additional tiling capabilities, it is heavily inspired by the Calm Window manager (cwm(1)). Its core concepts are views, workspaces, sheets and groups.

The following options are available:

-a <executable> Specify autostart executable.

-c <config> Specify a configuration file.

-h Show this message and quit.

-v Show version and quit.



Views are basically the windows of a Wayland client. Each view belongs to at most one sheet and can also belong to at most one group. A view can be in several states.


A workspace is the set of views that are currently visible. Unlike in most other Wayland compositors, hikari only has a single workspace for each output and we only manipulate its content using actions. While this seems like a superficial distinction it is important to keep in mind for some actions to make sense. When switching to a sheet this sheet becomes the workspace sheet. On startup a workspace sheet is 1. Views on a workspace are stacked from bottom to top, where the next view is higher up the stack and the previous view is one below. This order can be changed by raising or lowering views individually via actions. Selecting a view via cycling actions automatically raises this view to the top of the stacking order.

hikari provides multiple ways to cycle the views on a workspace. Cycling is a way to navigate to a view using key bindings.


A sheet is a collection of views, each view can only be a member of a single sheet. Switching between sheets will replace the current content of the workspace with all the views that are a member of the selected sheet, this sheet will also become the current sheet. hikari has 9 general purpose sheets that correspond to the numbers 1 to 9 and a special purpose sheet 0. Views that are a member of sheet 0 will always be visible but stacked below the views of the selected sheet. A sheet that contains views is called inhabited.

When switching to a different sheet the current current sheet becomes the alternate sheet.


Groups are a bit more fine grained than sheets. Like sheets, groups are a collection of views. Unlike sheets you can have a arbitrary number of groups and each group can have an arbitrary name. Views from one group can be spread among all available sheets. Some actions act on entire groups rather than individual views.


Each sheet can have at most one layout for tiling views. Applying a layout to a sheet introduces an additional ordering for views which is not altered by raising or lowering, which can be used to traverse the layout in the expected order. Each layout can be stored in one of the layout register a to z.

View indicators

View indicators show information about the current view as well as views belonging to the same group. They outline the border of the current view as well as all view borders belonging to the same group (obscured view borders will shine through the obscuring view). The focused view will also display so called indicator bars. Each bar holds some information, like title, sheet information, group and its mark (if one has been set for the view).


Marks can be used to “speed dial” views, even if they are on a sheet other than the current sheet (note: such views will become borrowed in the process). Marks are represented by characters from a to z. When jumping to a mark the view will be brought forward and focused. If no view is bound to a mark the user can specify a command that is executed instead. This can be used to start an application that binds itself to this mark.


hikari is a modal Wayland compositor and therefore offers several modes for actions like changing a views group, mark or sheet as well a jumping to marks or grabbing input events and layout selection.


hikari is configured using libucl(3) as a configuration file format. The configuration is located under $XDG_CONFIG_HOME/hikari/hikari.conf. If this file is not found hikari is going to try hikari.conf from the install etc directory.

The default configuration is going to use $TERMINAL as your standard terminal application.

On startup hikari attempts to execute ~/.config/hikari/autostart to autostart applications.

Environment Variables

hikari supports the use of environment variables in its string values. Occurrences of ${VARIABLE} or $VARIABLE inside a string will be substituted with the value of an environment variable named VARIABLE. If no such environment variable exists, no substituation takes place.

You can use double dollar signs to escape variables: $${VARIABLE} will result in ${VARIABLE}, $$VARIABLE will result in $VARIABLE.


General actions

Group actions

Layout actions

Mark actions

Mode actions

Sheet actions

View actions

VT actions

Workspace actions


Actions can also be user defined, this is done in the actions section of the configuration file. A user defined action consists of a name and a command that should be run when the action has been issued.

To define an action action-terminal that launches sakura(1) one needs to defined the following.

terminal = sakura

Now we can bind the newly defined action-terminal to a key combination in the bindings section.


Actions can be bound to keys and mouse buttons. The bindings section in the configuration file is used for this matter. Keys can be specified by using either key symbols or codes. A key combination starts with a string identifying the modifiers for the bindings. There are 5 valid modifiers. A valid modifier string is a combination of the following modifiers.

If we want to omit the modifier for a key binding we signal this by using “0” instead of a modifier string.

Following the modifier string a key symbol or code can be stated. If we are using a key symbol to identify a key combination we are using “+” followed by the symbol in the case of a key code we are using “-” followed by the numerical key code. Key symbols and codes can be determined using wev(1).

Once a key combination has been identified it can be bound to an action.

"LS+a" = action1 # symbol binding
"LS-38" = action2 # code binding

The bindings section can contain 2 subsections keyboard and mouse for keyboard and mouse bindings.

Valid values for mouse button names are right, middle, left, side, extra, forward, back and task.

Bindings can have a dedicated end action that gets triggered whenever a key is released or additional keys are pressed. It ensures that a begin action definitely is followed by the end action.

"L+t"  = {
  begin = action-push-to-talk-start
  end = action-push-to-talk-stop


Marks can be used to quickly navigate to views. They can also execute commands when they are not currently bound to a view. This functionality can be used to start an application that can then take over that mark using auto configuration. Note that the started application does not automatically take over the mark.

To specify commands that are issued on unassigned marks one can specify the commands associated with the mark in the marks section in the configuration file.

marks {
  s = sakura


When an application start its views can be automatically configured by hikari. Each view has a property called id, in the views section this can be used to specify certain properties you want for that view to apply.

To configure views of the systat id to become a member of the group monitor and automatically assign them to sheet 0 with a given position and focus grabbing we would do something like this. Child views inherit the group and sheet property while overwriting floating to true, all the other properties are set to their respective default values.

systat = {
  group = monitor
  sheet = 0
  position = {
    x = 1429
    y = 1077
  focus = true

  inherit = [ group, sheet, { floating = true } ]


hikari is not a tiling compositor so naturally some things will behave a bit differently compared to traditional tiling approaches. First and foremost, hikari tries to minimize operations that will affect a lot of views at the same time e.g. opening a new view will not automatically insert a view into an existing layout and resize all of the already tiled views. To insert a view into an existing layout the user has to issue a tiling action. This way opening a new view does not scramble an existing layout and the user can actively decide when to incorporate a view into a layout.

A layout is bound to a sheet, each sheet can have at most one layout and laying out a sheet will incorporate all of its views unless they are invisible or floating. Resetting a layout will reset the geometry of all of the laid out views to its original geometry (also resetting maximization).

Configuring layouts happens in the layouts section in the configuration file. Layouts are assigned to layout registers from a to z and special layout registers 0 to 9 which correspond to default layouts for a respective sheet. A layout itself is a combination of splits and containers with tiling algorithms.

Splits are used to subdivide regions of space and containers are used to consume views and layout them according to a specific tiling algorithm.


A layout subdivides the screen space using splits. Dividing up the screen space uses a binary space partitioning approach. One can split a region of space horizontally or vertically into to new regions which can contain either another split or a container with a tiling algorithm.

To split up the screen vertically into two equally sized section one has to specify when the left and right hand side of the split should contain.

  left = ...
  right = ...

Respectively to split horizontally you have to specify top and bottom.

Notice that the order in which you specify left, right, top and bottom is important, since it defined the orientation of the split. The side of the split that gets specified first is the part the gets filled first when tiling a sheet, it becomes the dominant part of the split.

Sometimes splitting a region of space should not result in equally sized subdivisions and the dominant part of the split should be scaled up or down. This can be done by specifying the scale attribute which can vary between 0.1 to 0.9, if no scale is specified this value defaults to 0.5.

To horizontally split a region on space where the top portion of the split should take up 75% would be specified like so:

  scale = 0.75
  top = ...
  bottom = ...

Additionally to setting a fixed scale value, hikari allows to specify a scale object with min and max values. This is called dynamic scaling, and it uses the size of the first view inside the container to determine the size of the entire container. The min and max values are used to specify possible minimum and maximum scale values for the container. Omitting the values for min or max sets the former to 0.1 and the latter to 0.9.

  scale = {
    min = 0.5
    max = 0.75
  left = single
  right = stack


Containers consume a number of views and arrange them according to a tiling algorithm. There are 6 different tiling algorithms that you can assign to a container.

The easiest way to define a layout is by simply stating the tiling algorithm. Binding a fullscreen layout to the layout register f can be trivially achieved.

f = full

This layout does not subdivide the screen using splits in any way. The container takes up the entire screen space (respecting gap settings) and uses the full algorithm to arrange the views.

More complex layouts might demand that the user specifies the number of views that the container may contain up to a maximum. This can be achieved by specifying a container object.

To define a queue container that contains up to 4 views one would define it like that:

  views = 4
  layout = queue

Just stating the tiling algorithm is a short-hand for a layout object with where views is set to 256.


Getting everything to look right is an important aspect of feeling “at home”. hikari offers a couple of options to tweak visuals to the users content. All of these configuration options are part of the ui section.

Standard border thickness is set to 1.

border = 1

The standard gap value is 5.

gap = 5

hikari uses monospace 10 as its default font setting.

font = "monospace 10"

The standard step value is 100.

step = 100


hikari uses color to indicate different states of views and their indicator bars. By specifying a colorscheme section the user can control these colors. A colorscheme is a number of properties that can be changed individually. Colors are specified using hexadecimal RGB values (e.g. 0xE6DB74).

These are the default settings for the hikari colorscheme.

colorscheme {
  background = 0x282C34
  foreground = 0x000000
  selected   = 0xF5E094
  grouped    = 0xFDAF53
  first      = 0xB8E673
  conflict   = 0xED6B32
  insert     = 0xE3C3FA
  active     = 0xFFFFFF
  inactive   = 0x465457


The inputs section is used to configure input devices. Device names can be determined using libinput(1).


Pointers can be configured in the pointers subsection. The following options are available.

Configuring the System mouse input device could look like this.

inputs {
  pointers {
    "System mouse" = {
      accel = 1.0
      scroll-method = on-button-down
      scroll-button = middle

A special name “*” is used to address all pointers. Values defined for this pseudo pointer override unconfigured values for any other pointer.


hikari is using xkb to configure its keyboards via the keyboards section. xkb rules can be set independently or via a file using the xkb attribute.

To specify rules individually one can use the following options. Refer to xkeyboard-config(7) for possible settings.

Additionally hikari can also configure key repeat using the following attributes.

Configuring the AT keyboard input device could look like this.

inputs {
  keyboards {
    "*" = {
      xkb = {
        layout = "de(nodeadkeys)"
        options = "caps:escape"
      repeat-rate = 25
      repeat-delay = 600

A special name “*” is used to address all keyboards. Values defined for this pseudo keyboard override unconfigured values for any other pointer.

Keyboards can also be configured using XKB environment variables. hikari will automatically fall back to these settings if a keyboard is not explicitly configured.

To specify a layout set XKB_DEFAULT_LAYOUT to the desired layout. This needs to happen before starting hikari.

XKB_DEFAULT_LAYOUT "de(nodeadkeys),de"


Switches can be configured in the switches subsection. A switch just takes an action and functions like a regular key binding using the name of the switch as an identifier. The begin action is triggered when turning the switch on and end is triggered when turning it off.

inputs {
  switches {
    "Control Method Lid Switch" = lock


The outputs section allows users to define the background and position for a output using its name. A special name “*” is used to address all outputs. Values defined for this pseudo output override unconfigured values for any other output.

Backgrounds are configured via the background attribute which can be either the path to the background image, or an object which enables the user to define additional attributes for the background image. Background file format has to be PNG.

When defining a background object the following attributes are available.

Configuring output eDP-1 and WL-1 could look like this.

outputs {
  "eDP-1" = {
    background = "/path/to/wallpaper"

  WL-1 = {
    background = {
      path = "/path/to/wallpaper"
      fit = center

Output position can be given explicitly using the position attribute. If none is given during startup hikari will automatically configure the output.

"eDP-1" = {
  position = {
    x = 1680
    y = 0

"HDMI-A-1" = {
  position = {
    x = 0
    y = 0