-rw-r--r-- src/config.c
1 #include <cairo/cairo.h> 2 #include <drm_fourcc.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <string.h> 6 #include <wordexp.h> 7 #include <wayland-server-core.h> 8 #include <wlr/render/wlr_texture.h> 9 #include <unistd.h> 10 11 #include "action.h" 12 #include "config.h" 13 #include "desk.h" 14 #include "keybind.h" 15 #include "output.h" 16 #include "scratchpad.h" 17 #include "types.h" 18 19 #define CONFIG_HOME "$HOME/.config/wimp/startup" 20 #define CONFIG_HOME_XDG "$XDG_CONFIG_HOME/wimp/startup" 21 22 23 void assign_colour(char *hex, float dest[4]) { 24 if (!hex) { 25 return; 26 } 27 if (hex[0] == '#') { 28 hex++; 29 } 30 31 char c[2]; 32 int copy; 33 if (strlen(hex) == 6) { 34 dest[3] = 1; 35 copy = 3; 36 } else if (strlen(hex) == 8) { 37 copy = 4; 38 } else { 39 wlr_log(WLR_INFO, "Invalid colour: %s. Should be in the form #rrggbb.", hex); 40 return; 41 } 42 43 for (int i = 0; i < copy; i++) { 44 strncpy(c, &hex[i * 2], 2); 45 dest[i] = (float)strtol(c, NULL, 16) / 255; 46 } 47 } 48 49 50 static void load_wallpaper(struct desk *desk, char *path) { 51 cairo_surface_t *image = cairo_image_surface_create_from_png(path); 52 53 if (!image || cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) { 54 wlr_log(WLR_INFO, "Could not load image: %s", path); 55 return; 56 } 57 58 struct wallpaper *wallpaper = calloc(1, sizeof(struct wallpaper)); 59 wallpaper->width = cairo_image_surface_get_width(image); 60 wallpaper->height = cairo_image_surface_get_height(image); 61 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, wallpaper->width); 62 63 // we copy the image to a second surface to ensure the pixel format is 64 // compatible with wlr_texture_from_pixels 65 cairo_surface_t *canvas = cairo_image_surface_create( 66 CAIRO_FORMAT_ARGB32, wallpaper->width, wallpaper->height); 67 cairo_t *cr = cairo_create(canvas); 68 cairo_set_source_surface(cr, image, 0, 0); 69 cairo_paint(cr); 70 71 wallpaper->texture = wlr_texture_from_pixels( 72 wimp.renderer, DRM_FORMAT_ARGB8888, stride, wallpaper->width, 73 wallpaper->height, cairo_image_surface_get_data(canvas) 74 ); 75 desk->wallpaper = wallpaper; 76 damage_all_outputs(); 77 cairo_destroy(cr); 78 cairo_surface_destroy(image); 79 cairo_surface_destroy(canvas); 80 } 81 82 83 static void set_wallpaper(struct desk *desk, char *wallpaper) { 84 // wallpaper is a colour 85 if (strlen(wallpaper) == 7 && wallpaper[0] == '#') { 86 assign_colour(wallpaper, desk->background); 87 return; 88 } 89 90 // wallpaper is a path to an image 91 wordexp_t p; 92 wordexp(wallpaper, &p, WRDE_NOCMD | WRDE_UNDEF); 93 load_wallpaper(desk, p.we_wordv[0]); 94 wordfree(&p); 95 } 96 97 98 static void disable_vt_switching() { 99 struct binding *kb, *tmp; 100 wl_list_for_each_safe(kb, tmp, &wimp.key_bindings, link) { 101 if (kb->action == &change_vt) { 102 free_binding(kb); 103 } 104 } 105 } 106 107 108 static void setup_vt_switching() { 109 uint32_t func_keys[] = { 110 XKB_KEY_F1, 111 XKB_KEY_F2, 112 XKB_KEY_F3, 113 XKB_KEY_F4, 114 XKB_KEY_F5, 115 XKB_KEY_F6, 116 }; 117 struct binding *kb; 118 119 for (unsigned i = 0; i < 6; i++) { 120 kb = calloc(1, sizeof(struct binding)); 121 kb->mods = WLR_MODIFIER_CTRL; 122 kb->key = func_keys[i]; 123 kb->action = &change_vt; 124 kb->data = calloc(1, sizeof(unsigned)); 125 *(unsigned *)(kb->data) = i + 1; 126 wl_list_insert(wimp.key_bindings.prev, &kb->link); 127 } 128 } 129 130 131 void set_configurable(char *message, char *response) { 132 char *s; 133 if (!(s = strtok(NULL, " \t\n\r"))) { 134 sprintf(response, "What do you want to set?."); 135 return; 136 } 137 138 // desks <int> 139 if (!strcasecmp(s, "desks")) { 140 if ((s = strtok(NULL, " \t\n\r")) && is_number(s)) { 141 configure_desks(strtod(s, NULL)); 142 } 143 } 144 145 // desk ... 146 else if (!strcasecmp(s, "desk")) { 147 if ((s = strtok(NULL, " \t\n\r")) && is_number(s)) { 148 int index = strtod(s, NULL) - 1; 149 s = strtok(NULL, " \t\n\r"); 150 struct desk *desk; 151 bool found = false; 152 wl_list_for_each(desk, &wimp.desks, link) { 153 if (desk->index == index) { 154 found = true; 155 break; 156 } 157 } 158 if (!found) { 159 return; 160 } 161 162 // desk <index> background #rrggbb 163 // desk <index> background ~/path/to/image.png 164 if (!strcasecmp(s, "background")) { 165 if ((s = strtok(NULL, " \t\n\r"))) { 166 set_wallpaper(desk, s); 167 } 168 } 169 170 // desk <index> borders [focus|normal] <#rrggbb> 171 // desk <index> borders width <int> 172 else if (!strcasecmp(s, "borders")) { 173 s = strtok(NULL, " \t\n\r"); 174 if (!strcasecmp(s, "normal")) { 175 assign_colour(strtok(NULL, " \t\n\r"), desk->border_normal); 176 } 177 else if (!strcasecmp(s, "focus")) { 178 assign_colour(strtok(NULL, " \t\n\r"), desk->border_focus); 179 } 180 else if (!strcasecmp(s, "width")) { 181 if ((s = strtok(NULL, " \t\n\r")) && is_number(s)) { 182 desk->border_width = strtod(s, NULL); 183 } 184 } 185 } 186 187 // desk <index> corners [focus|normal] <#rrggbb> 188 else if (!strcasecmp(s, "corners")) { 189 s = strtok(NULL, " \t\n\r"); 190 if (!strcasecmp(s, "normal")) { 191 assign_colour(strtok(NULL, " \t\n\r"), desk->corner_normal); 192 } 193 else if (!strcasecmp(s, "focus")) { 194 assign_colour(strtok(NULL, " \t\n\r"), desk->corner_focus); 195 } 196 } 197 } 198 } 199 200 // zoom_min <float> 201 else if (!strcasecmp(s, "zoom_min")) { 202 if ((s = strtok(NULL, " \t\n\r")) && is_number(s)) { 203 wimp.zoom_min = strtod(s, NULL); 204 } 205 } 206 207 // zoom_max <float> 208 else if (!strcasecmp(s, "zoom_max")) { 209 if ((s = strtok(NULL, " \t\n\r")) && is_number(s)) { 210 wimp.zoom_max = strtod(s, NULL); 211 } 212 } 213 214 // mark_indicator #rrggbb 215 else if (!strcasecmp(s, "mark_indicator")) { 216 assign_colour(strtok(NULL, " \t\n\r"), wimp.mark_indicator.colour); 217 wimp.mark_indicator.box.x = 0; 218 wimp.mark_indicator.box.y = 0; 219 } 220 221 // snap_box #rrggbb 222 else if (!strcasecmp(s, "snap_box")) { 223 assign_colour(strtok(NULL, " \t\n\r"), wimp.snapbox_colour); 224 } 225 226 // vt_switching [off|on] 227 else if (!strcasecmp(s, "vt_switching")) { 228 s = strtok(NULL, " \t\n\r"); 229 if (!strcasecmp(s, "off")) 230 disable_vt_switching(); 231 else if (!strcasecmp(s, "on")) 232 setup_vt_switching(); 233 } 234 235 // scroll_direction [natural|reverse] 236 else if (!strcasecmp(s, "scroll_direction")) { 237 s = strtok(NULL, " \t\n\r"); 238 if (!strcasecmp(s, "natural")) 239 wimp.reverse_scrolling = false; 240 else if (!strcasecmp(s, "reverse")) 241 wimp.reverse_scrolling = true; 242 } 243 244 // mod <modifier> 245 else if (!strcasecmp(s, "mod")) { 246 set_mod(message, response); 247 } 248 249 // bind_marks [on|off] 250 else if (!strcasecmp(s, "bind_marks")) { 251 s = strtok(NULL, " \t\n\r"); 252 if (!strcasecmp(s, "off")) 253 wimp.bind_marks = false; 254 else if (!strcasecmp(s, "on")) 255 wimp.bind_marks = true; 256 } 257 258 // auto_focus [on|off] 259 else if (!strcasecmp(s, "auto_focus")) { 260 s = strtok(NULL, " \t\n\r"); 261 if (!strcasecmp(s, "off")) { 262 wimp.auto_focus = false; 263 } else if (!strcasecmp(s, "on")) { 264 wimp.auto_focus = true; 265 } 266 } 267 } 268 269 270 struct startup_data { 271 struct wl_event_source *wl_event_source; 272 char *script; 273 }; 274 275 276 static int _startup(void *vdata) { 277 struct startup_data *data = vdata; 278 wl_event_source_remove(data->wl_event_source); 279 if (fork() == 0) { 280 if (execl(data->script, data->script, (void *)NULL) == -1) { 281 exit(EXIT_FAILURE); 282 } 283 } else { 284 wlr_log(WLR_DEBUG, "Running startup script: %s", data->script); 285 } 286 free(data->script); 287 free(data); 288 return 0; 289 } 290 291 292 void schedule_startup() { 293 char *xdg_config = getenv("XDG_CONFIG_HOME"); 294 char *config_dir = (xdg_config && *xdg_config) ? CONFIG_HOME_XDG : CONFIG_HOME; 295 struct startup_data *data = calloc(1, sizeof(struct startup_data)); 296 wordexp_t p; 297 wordexp(config_dir, &p, WRDE_NOCMD | WRDE_UNDEF); 298 data->script = strdup(p.we_wordv[0]); 299 wordfree(&p); 300 301 if (is_executable(data->script)) { 302 struct wl_event_loop *event_loop = wl_display_get_event_loop(wimp.display); 303 data->wl_event_source = wl_event_loop_add_timer(event_loop, _startup, data); 304 wl_event_source_timer_update(data->wl_event_source, 1); 305 } else { 306 wlr_log(WLR_ERROR, "Cannot run startup script: %s", data->script); 307 free(data->script); 308 free(data); 309 } 310 311 } 312