├── .gitignore
├── .gitmodules
├── LICENSE
├── Makefile
├── README.md
├── res
├── de.mherzberg.wlrbrightness.xml
└── wlr-brightness.service
└── src
└── wlr-brightness.c
/.gitignore:
--------------------------------------------------------------------------------
1 | gen
2 | out
3 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "vendor/wlr-protocols"]
2 | path = vendor/wlr-protocols
3 | url = https://github.com/swaywm/wlr-protocols.git
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Michael Herzberg
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | INSTALL_PATH?=/usr/local
2 | WAYLAND_SCANNER=$(shell pkg-config --variable=wayland_scanner wayland-scanner)
3 | LIBS=\
4 | $(shell pkg-config --cflags --libs wlroots) \
5 | $(shell pkg-config --cflags --libs wayland-client) \
6 | $(shell pkg-config --cflags --libs xkbcommon) \
7 | $(shell pkg-config --cflags --libs gio-2.0 gio-unix-2.0 glib-2.0) \
8 | -lm
9 |
10 | out/wlr-brightness: out \
11 | src/wlr-brightness.c \
12 | gen/wlr-gamma-control-unstable-v1-client-protocol.h \
13 | gen/wlr-gamma-control-unstable-v1-client-protocol.c \
14 | gen/wlrbrightnessbus.h \
15 | gen/wlrbrightnessbus.c
16 | $(CC) $(CFLAGS) \
17 | -I. -Werror -DWLR_USE_UNSTABLE \
18 | $(LIBS) \
19 | -o $@ \
20 | src/wlr-brightness.c \
21 | gen/wlr-gamma-control-unstable-v1-client-protocol.c \
22 | gen/wlrbrightnessbus.c
23 |
24 | gen/wlr-gamma-control-unstable-v1-client-protocol.h: gen
25 | $(WAYLAND_SCANNER) client-header \
26 | vendor/wlr-protocols/unstable/wlr-gamma-control-unstable-v1.xml $@
27 |
28 | gen/wlr-gamma-control-unstable-v1-client-protocol.c: gen
29 | $(WAYLAND_SCANNER) private-code \
30 | vendor/wlr-protocols/unstable/wlr-gamma-control-unstable-v1.xml $@
31 |
32 | gen/wlrbrightnessbus.c gen/wlrbrightnessbus.h: gen
33 | gdbus-codegen --output-directory gen --generate-c-code wlrbrightnessbus \
34 | --c-namespace WlrBrightnessBus --interface-prefix de.mherzberg. \
35 | res/de.mherzberg.wlrbrightness.xml
36 |
37 | gen:
38 | mkdir -p gen
39 |
40 | out:
41 | mkdir -p out
42 |
43 | install:
44 | install -Dm755 out/wlr-brightness $(INSTALL_PATH)/bin/wlr-brightness
45 |
46 | clean:
47 | rm -rf out gen
48 |
49 | .PHONY: clean
50 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # wlr-brightness
2 |
3 | wlr-brightness adjust the brightness of your screen when using
4 | [wlroots](https://github.com/swaywm/wlroots/)-based
5 | [Wayland](https://wayland.freedesktop.org/) compositors such as
6 | [sway](https://github.com/swaywm/sway/). It works by adjusting the gamma values
7 | of your screen and therefore supports both screens with and without backlight
8 | (such as OLED).
9 |
10 | ## Installation
11 |
12 | ### From Source
13 |
14 | Dependencies:
15 |
16 | * [wlroots](https://github.com/swaywm/wlroots)
17 | * wayland
18 | * dbus
19 | * wlr-protocols
20 |
21 | Pull wlr-protocols:
22 |
23 | git submodule update --init --recursive
24 |
25 | Compile:
26 |
27 | make
28 |
29 | If you want to install wlr-brightness, you can use the systemd service:
30 |
31 | sudo make install
32 | cp res/wlr-brightness.service ~/.config/systemd/user/
33 | systemctl --user enable --now wlr-brightness
34 |
35 | ## Usage
36 |
37 | Start the daemon:
38 |
39 | wlr-brightness
40 |
41 | wlr-brightness is a Wayland-client and therefore needs to keep running. The
42 | brightness will reset to the maximum on exit. To control wlr-brightness, use any
43 | dbus utility, e.g.:
44 |
45 | gdbus call -e -d de.mherzberg -o /de/mherzberg/wlrbrightness -m de.mherzberg.wlrbrightness.get
46 | gdbus call -e -d de.mherzberg -o /de/mherzberg/wlrbrightness -m de.mherzberg.wlrbrightness.set 0.7
47 | gdbus call -e -d de.mherzberg -o /de/mherzberg/wlrbrightness -m de.mherzberg.wlrbrightness.increase 0.1
48 | gdbus call -e -d de.mherzberg -o /de/mherzberg/wlrbrightness -m de.mherzberg.wlrbrightness.decrease 0.1
49 |
--------------------------------------------------------------------------------
/res/de.mherzberg.wlrbrightness.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/res/wlr-brightness.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=wlroots Brightness Control
3 | PartOf=graphical-session.target
4 |
5 | [Service]
6 | Type=simple
7 | ExecStart=wlr-brightness
8 |
9 | [Install]
10 | WantedBy=graphical-session.target
11 |
--------------------------------------------------------------------------------
/src/wlr-brightness.c:
--------------------------------------------------------------------------------
1 | #define _POSIX_C_SOURCE 200809L
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include "gen/wlr-gamma-control-unstable-v1-client-protocol.h"
14 | #include "gen/wlrbrightnessbus.h"
15 |
16 | struct output {
17 | struct wl_output *wl_output;
18 | struct zwlr_gamma_control_v1 *gamma_control;
19 | uint32_t ramp_size;
20 | int table_fd;
21 | uint16_t *table;
22 | struct wl_list link;
23 | };
24 |
25 | static struct wl_list outputs;
26 | static struct zwlr_gamma_control_manager_v1 *gamma_control_manager = NULL;
27 | static struct wl_display *display;
28 | static double current_brightness = 1.0;
29 |
30 | static int create_anonymous_file(off_t size) {
31 | char template[] = "/tmp/wlroots-shared-XXXXXX";
32 | int fd = mkstemp(template);
33 | if (fd < 0) {
34 | return -1;
35 | }
36 |
37 | int ret;
38 | do {
39 | errno = 0;
40 | ret = ftruncate(fd, size);
41 | } while (errno == EINTR);
42 | if (ret < 0) {
43 | close(fd);
44 | return -1;
45 | }
46 |
47 | unlink(template);
48 | return fd;
49 | }
50 |
51 | static int create_gamma_table(uint32_t ramp_size, uint16_t **table) {
52 | size_t table_size = ramp_size * 3 * sizeof(uint16_t);
53 | int fd = create_anonymous_file(table_size);
54 | if (fd < 0) {
55 | fprintf(stderr, "failed to create anonymous file\n");
56 | return -1;
57 | }
58 |
59 | void *data =
60 | mmap(NULL, table_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
61 | if (data == MAP_FAILED) {
62 | fprintf(stderr, "failed to mmap()\n");
63 | close(fd);
64 | return -1;
65 | }
66 |
67 | *table = data;
68 | return fd;
69 | }
70 |
71 | static void gamma_control_handle_gamma_size(void *data,
72 | struct zwlr_gamma_control_v1 *gamma_control, uint32_t ramp_size) {
73 | struct output *output = data;
74 | output->ramp_size = ramp_size;
75 | output->table_fd = create_gamma_table(ramp_size, &output->table);
76 | if (output->table_fd < 0) {
77 | exit(EXIT_FAILURE);
78 | }
79 | }
80 |
81 | static void gamma_control_handle_failed(void *data,
82 | struct zwlr_gamma_control_v1 *gamma_control) {
83 | fprintf(stderr, "failed to set gamma table\n");
84 | exit(EXIT_FAILURE);
85 | }
86 |
87 | static const struct zwlr_gamma_control_v1_listener gamma_control_listener = {
88 | .gamma_size = gamma_control_handle_gamma_size,
89 | .failed = gamma_control_handle_failed,
90 | };
91 |
92 | static void registry_handle_global(void *data, struct wl_registry *registry,
93 | uint32_t name, const char *interface, uint32_t version) {
94 | if (strcmp(interface, wl_output_interface.name) == 0) {
95 | struct output *output = calloc(1, sizeof(struct output));
96 | output->wl_output = wl_registry_bind(registry, name,
97 | &wl_output_interface, 1);
98 | wl_list_insert(&outputs, &output->link);
99 | } else if (strcmp(interface,
100 | zwlr_gamma_control_manager_v1_interface.name) == 0) {
101 | gamma_control_manager = wl_registry_bind(registry, name,
102 | &zwlr_gamma_control_manager_v1_interface, 1);
103 | }
104 | }
105 |
106 | static void registry_handle_global_remove(void *data,
107 | struct wl_registry *registry, uint32_t name) {
108 | }
109 |
110 | static const struct wl_registry_listener registry_listener = {
111 | .global = registry_handle_global,
112 | .global_remove = registry_handle_global_remove,
113 | };
114 |
115 | static void fill_gamma_table(uint16_t *table, uint32_t ramp_size,
116 | double brightness) {
117 | uint16_t *r = table;
118 | uint16_t *g = table + ramp_size;
119 | uint16_t *b = table + 2 * ramp_size;
120 | for (uint32_t i = 0; i < ramp_size; ++i) {
121 | double val = (double)i / (ramp_size - 1);
122 |
123 | // We never want to get brighter, but we also want to dim bright colors a
124 | // little bit more than dark colors.
125 | val = fmin(pow(val, 0.9) * brightness, val);
126 | if (val > 1.0) {
127 | val = 1.0;
128 | } else if (val < 0.0) {
129 | val = 0.0;
130 | }
131 | r[i] = g[i] = b[i] = (uint16_t)(UINT16_MAX * val);
132 | }
133 | }
134 |
135 | static void
136 | set_brightness(double new_brightness)
137 | {
138 | if(new_brightness > 1.0)
139 | {
140 | new_brightness = 1.0;
141 | }
142 | if(new_brightness < 0.0)
143 | {
144 | new_brightness = 0.0;
145 | }
146 |
147 | struct timespec tim, tim2;
148 | tim.tv_sec = 0;
149 | tim.tv_nsec = 0.02 * 1000 * 1000 * 1000;
150 |
151 | double delta = (new_brightness - current_brightness) / 10.0;
152 |
153 | for(int i = 0; i < 10; ++i)
154 | {
155 | if(nanosleep(&tim , &tim2) < 0 )
156 | {
157 | printf("Nano sleep system call failed \n");
158 | return;
159 | }
160 | current_brightness += delta;
161 | struct output *output;
162 | wl_list_for_each(output, &outputs, link) {
163 | output->table_fd = create_gamma_table(output->ramp_size, &output->table);
164 | fill_gamma_table(output->table, output->ramp_size,
165 | current_brightness);
166 | zwlr_gamma_control_v1_set_gamma(output->gamma_control,
167 | output->table_fd);
168 | }
169 | wl_display_flush(display);
170 | }
171 | current_brightness = new_brightness;
172 | }
173 |
174 | static gboolean
175 | on_handle_increase (WlrBrightnessBusWlrbrightness *interface, GDBusMethodInvocation *invocation,
176 | const gdouble value, gpointer user_data)
177 | {
178 | set_brightness(current_brightness + value);
179 | wlr_brightness_bus_wlrbrightness_complete_increase (interface, invocation, current_brightness);
180 | return TRUE;
181 | }
182 |
183 | static gboolean
184 | on_handle_decrease (WlrBrightnessBusWlrbrightness *interface, GDBusMethodInvocation *invocation,
185 | const gdouble value, gpointer user_data)
186 | {
187 | set_brightness(current_brightness - value);
188 | wlr_brightness_bus_wlrbrightness_complete_decrease (interface, invocation, current_brightness);
189 | return TRUE;
190 | }
191 |
192 | static gboolean
193 | on_handle_set (WlrBrightnessBusWlrbrightness *interface, GDBusMethodInvocation *invocation,
194 | const gdouble value, gpointer user_data)
195 | {
196 | set_brightness(value);
197 | wlr_brightness_bus_wlrbrightness_complete_get (interface, invocation, current_brightness);
198 | return TRUE;
199 | }
200 |
201 | static gboolean
202 | on_handle_get (WlrBrightnessBusWlrbrightness *interface, GDBusMethodInvocation *invocation, gpointer user_data)
203 | {
204 | wlr_brightness_bus_wlrbrightness_complete_get (interface, invocation, current_brightness);
205 | return TRUE;
206 | }
207 |
208 | static void
209 | on_name_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data)
210 | {
211 | WlrBrightnessBusWlrbrightness *interface;
212 | GError *error;
213 |
214 | interface = wlr_brightness_bus_wlrbrightness_skeleton_new();
215 | g_signal_connect (interface, "handle-get", G_CALLBACK (on_handle_get), NULL);
216 | g_signal_connect (interface, "handle-set", G_CALLBACK (on_handle_set), NULL);
217 | g_signal_connect (interface, "handle-increase", G_CALLBACK (on_handle_increase), NULL);
218 | g_signal_connect (interface, "handle-decrease", G_CALLBACK (on_handle_decrease), NULL);
219 | error = NULL;
220 | !g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (interface), connection, "/de/mherzberg/wlrbrightness", &error);
221 | }
222 |
223 | int main(int argc, char *argv[]) {
224 | wl_list_init(&outputs);
225 |
226 | display = wl_display_connect(NULL);
227 | if (display == NULL) {
228 | fprintf(stderr, "failed to create display\n");
229 | return -1;
230 | }
231 |
232 | struct wl_registry *registry = wl_display_get_registry(display);
233 | wl_registry_add_listener(registry, ®istry_listener, NULL);
234 | wl_display_dispatch(display);
235 | wl_display_roundtrip(display);
236 |
237 | if (gamma_control_manager == NULL) {
238 | fprintf(stderr,
239 | "compositor doesn't support wlr-gamma-control-unstable-v1\n");
240 | return EXIT_FAILURE;
241 | }
242 |
243 | struct output *output;
244 | wl_list_for_each(output, &outputs, link) {
245 | output->gamma_control = zwlr_gamma_control_manager_v1_get_gamma_control(
246 | gamma_control_manager, output->wl_output);
247 | zwlr_gamma_control_v1_add_listener(output->gamma_control,
248 | &gamma_control_listener, output);
249 | }
250 | wl_display_roundtrip(display);
251 |
252 |
253 | GMainLoop *loop;
254 | loop = g_main_loop_new (NULL, FALSE);
255 |
256 | g_bus_own_name(G_BUS_TYPE_SESSION, "de.mherzberg", G_BUS_NAME_OWNER_FLAGS_NONE, NULL,
257 | on_name_acquired, NULL, NULL, NULL);
258 |
259 | g_main_loop_run (loop);
260 |
261 | return EXIT_SUCCESS;
262 | }
263 |
--------------------------------------------------------------------------------