├── .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 | --------------------------------------------------------------------------------