darkman

package module
v1.5.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 18, 2023 License: ISC Imports: 17 Imported by: 0

README

darkman

A framework for dark-mode and light-mode transitions on Unix-like desktops.

Introduction

darkman runs in the background and turns on dark mode at sundown, and turns it off again at sunrise. darkman is not designed to be used interactively: it's designed to be set up once, and run in the background.

At sundown, it will look for scripts in $XDG_DATA_DIRS/dark-mode.d/. At sunrise, it will look for scripts in $XDG_DATA_DIRS/light-mode.d/.

These scripts individually configure different components and applications.

Sample and reference scripts are included in the examples directory, and further contributions for specific applications or environments are welcome.

Hint: $XDG_DATA_DIRS usually matches these, amongst others:

~/.local/share/
/usr/local/share/
/usr/share/

Packages may also drop-in their own scripts into any of these locations, although application developers are encouraged to use the D-Bus API to determine the current mode and listen for changes (see below for details).

Installation

ArchLinux
paru -S darkman
Fedora
dnf install darkman
Others

You'll need scdoc and go installed to build from source. There are typically both available in distribution repositories (e.g.: apt-get install...).

git clone git@gitlab.com:WhyNotHugo/darkman.git
cd darkman
make
sudo make install

Setup

You can run the service any way you prefer. A simple and safe approach is to just run it via the autostart/exec mechanism of your window manager (or wayland compositor).

If you use superd, the superd-services package includes a service definition:

superctl enable --now darkman

If you use systemd, a service file is included:

systemctl --user enable --now darkman.service

Note that the dark-mode and light-mode scripts mentioned above (and available in the source repository) are not included in this package. You'll need to drop-in the scripts you desire.

How it works

When it starts, darkman tries to determine your current location:

  • The config file.
  • The cache file from last time it ran.
  • Using the system geoclue.

Based on your location, darkman will determine sunrise/sundown. It will then switch to dark mode or light mode accordingly.

Finally, it'll set a timer for the next sundown / sunrise (whichever comes first), to switch to the opposite mode, set another timer, and sleep again.

It's designed to run as a service and require as little intervention as possible.

It is possible to manually query or change the current mode using the darkman. See darkman --help for details.

Configuration

See the man page (man darkman) for configuration details.

D-Bus service

A D-Bus endpoint is also exposed. There's a property to determine the current mode (Mode), and a signal to listen to changes (ModeChanged). Third-party applications can use this to determine whether they should render light mode or dark mode.

See dbus-api.xml for an XML description/introspection of the service.

A libdarkman go package is available to query the same D-Bus API from other client applications written in go. See its documentation for details.

Development

darkman works well and is actively maintained.

For bug and suggestions, see Issues on GitLab. Ongoing research is also gathered into these issues.

If you find the tool useful, please, considering sponsoring its development.

Feel free to join the IRC channel: #whynothugo on irc.libera.chat.

LICENCE

darkman is licensed under the ISC licence. See LICENCE for details.

Documentation

Overview

Package darkman implements darkman's service itself.

This package is used by gitlab.com/WhyNotHugo/darkman/cmd, which is the cli that wraps around the service and the client.

Index

Constants

View Source
const PORTAL_BUS_NAME = "org.freedesktop.impl.portal.desktop.darkman"
View Source
const PORTAL_INTERFACE = "org.freedesktop.impl.portal.Settings"
View Source
const PORTAL_KEY = "color-scheme"
View Source
const PORTAL_NAMESPACE = "org.freedesktop.appearance"
View Source
const PORTAL_OBJ_PATH = "/org/freedesktop/portal/desktop"

Variables

This section is empty.

Functions

func BoolFromYaml added in v1.5.0

func BoolFromYaml(yamlConfig map[interface{}]interface{}, key string) (*bool, error)

Returns nil if the variable is unset.

func ExecuteService

func ExecuteService() error

Run the darkman service.

func FloatFromYaml added in v1.5.0

func FloatFromYaml(yamlConfig map[interface{}]interface{}, key string) (*float64, error)

Returns nil if the variable is unset.

func GetLocations

func GetLocations(onLocation func(geoclue.Location)) (err error)

Periodically fetch the current location.

By default, we indicate set geoclue in a rather passive mode; it'll ignore location changes that occurr in less than four hours, or of less than 40km.

func NewScheduler

func NewScheduler(initialLocation *geoclue.Location, changeCallback func(Mode), useGeoclue bool) error

The scheduler schedules timer to wake up in time for the next sundown/sunrise.

func NextSunriseAndSundown

func NextSunriseAndSundown(loc geoclue.Location, now time.Time) (sunrise time.Time, sundown time.Time, err error)

Returns the time of the next sunrise and the next sundown. Note that they next sundown may be before the next sunrise or viceversa.

func ReadBoolEnvVar added in v1.5.0

func ReadBoolEnvVar(name string) (*bool, error)

Returns nil if the environment variable is unset.

func ReadFloatEnvVar added in v1.5.0

func ReadFloatEnvVar(name string) (*float64, error)

Returns nil if the environment variable is unset.

func RunScripts

func RunScripts(mode Mode)

Run transition scripts for a given mode.

Fires up all scripts asyncrhonously and returns immediately.

func SunriseAndSundown

func SunriseAndSundown(loc geoclue.Location, now time.Time) (sunrise time.Time, sundown time.Time, err error)

Return the time for sunrise and sundown for a given day and location.

Types

type Config

type Config struct {
	Lat        *float64
	Lng        *float64
	UseGeoclue bool
	DBusServer bool
	Portal     bool
}

func New added in v1.5.0

func New() Config

Returns a new Config with the default values.

func ReadConfig

func ReadConfig() (*Config, error)

func (*Config) GetLocation

func (config *Config) GetLocation() (*geoclue.Location, error)

func (*Config) LoadFromEnv added in v1.5.0

func (config *Config) LoadFromEnv() error

Loads and updates configuration in place.

Returns error for invalid settings. All fields are considered optional.

func (*Config) LoadFromYamlFile added in v1.5.0

func (config *Config) LoadFromYamlFile(filePath string) error

Loads a new configuration.

Returns error for invalid settings. All fields are considered optional.

type Mode

type Mode string
const (
	NULL  Mode = "null" // Only used while still initialising.
	LIGHT Mode = "light"
	DARK  Mode = "dark"
)

func CalculateCurrentMode

func CalculateCurrentMode(nextSunrise time.Time, nextSundown time.Time) Mode

func DetermineModeForRightNow added in v1.4.0

func DetermineModeForRightNow(location geoclue.Location) (*Mode, error)

func GetInitialMode added in v1.4.0

func GetInitialMode(location *geoclue.Location) Mode

Gets the initial mode. If the location is known, the mode is computed for that location. Unless the device has travelled across timezones, it should be the correct setting. Otherwise, load the last-known mode. This work well for manually controlled devices, which are unlikely to have a "last known location".

type PortalHandle added in v1.0.0

type PortalHandle struct {
	// contains filtered or unexported fields
}

func NewPortal added in v1.0.0

func NewPortal(initial Mode) (*PortalHandle, func(Mode), error)

Create a new D-Bus server instance for the XDG portal API.

Returns a callback function which should be called each time the current mode changes.

func (*PortalHandle) Read added in v1.0.0

func (portal *PortalHandle) Read(namespace string, key string) (dbus.Variant, *dbus.Error)

func (*PortalHandle) ReadAll added in v1.0.0

func (portal *PortalHandle) ReadAll(namespaces []string) (map[string]map[string]dbus.Variant, *dbus.Error)

type Scheduler

type Scheduler struct {
	// contains filtered or unexported fields
}

Scheduler handles setting timers based on the current location, and trigering changes based on the current location and sun position.

func (*Scheduler) Tick

func (handler *Scheduler) Tick()

A single tick.

Update the mode based on the current time, execute transition, and set the timer for the next tick.

func (*Scheduler) UpdateLocation

func (handler *Scheduler) UpdateLocation(newLocation geoclue.Location)

type ServerHandle

type ServerHandle struct {
	// contains filtered or unexported fields
}

func NewDbusServer

func NewDbusServer(initial Mode, onChange func(Mode)) (*ServerHandle, func(Mode), error)

Create a new D-Bus server instance for our API.

Takes as parameter a function that will be called each time the current mode is changed via this D-Bus API.

Returns a callback function which should be called each time the current mode changes by some other mechanism.

func (*ServerHandle) Close

func (handle *ServerHandle) Close() error

type Service added in v1.0.0

type Service struct {
	// contains filtered or unexported fields
}

func NewService added in v1.4.0

func NewService(initialMode Mode) Service

Creates a new Service instance.

func (*Service) AddListener added in v1.0.0

func (service *Service) AddListener(listener func(Mode))

Add a callback to be run each time the current mode changes.

func (*Service) ChangeMode added in v1.0.0

func (service *Service) ChangeMode(mode Mode)

Change the current mode (and run all callbacks).

Directories

Path Synopsis
Package boottimer provides a timer that is accurate over suspend.
Package boottimer provides a timer that is accurate over suspend.
cmd
Package geoclue implements a client for Geoclue's D-Bus.
Package geoclue implements a client for Geoclue's D-Bus.
Package implementing a wrapper around darkman's D-Bus API.
Package implementing a wrapper around darkman's D-Bus API.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL