mirror of
https://codeberg.org/vyivel/dulcepan/
synced 2025-12-17 23:55:12 +02:00
Add fancy borders
This commit is contained in:
21
dulcepan.cfg
21
dulcepan.cfg
@@ -4,10 +4,31 @@
|
|||||||
unselected-color = ffffff40
|
unselected-color = ffffff40
|
||||||
selected-color = 00000000
|
selected-color = 00000000
|
||||||
border-color = ffffff
|
border-color = ffffff
|
||||||
|
border-secondary-color = 000000
|
||||||
|
|
||||||
# 0 to disable borders
|
# 0 to disable borders
|
||||||
border-size = 2
|
border-size = 2
|
||||||
|
|
||||||
|
# Border gradient type:
|
||||||
|
# - none: only the primary border color is used
|
||||||
|
# - linear: uses a linear gradient relative to the selection
|
||||||
|
# - loop: uses a repeated linear gradient
|
||||||
|
border-gradient = none
|
||||||
|
|
||||||
|
# For "linear" gradient mode
|
||||||
|
# Counterclockwise, in degrees
|
||||||
|
gradient-angle = 45
|
||||||
|
|
||||||
|
# For "loop" gradient mode
|
||||||
|
# The distance between gradient stops
|
||||||
|
loop-step = 100
|
||||||
|
|
||||||
|
# For "linear" and "loop" gradient modes
|
||||||
|
# In milliseconds, 0 to disable animation
|
||||||
|
# With "linear" mode: the time it takes for the pattern to make one full turn
|
||||||
|
# With "loop" mode: the time it takes for the pattern to move by double the loop step
|
||||||
|
animation-duration = 0
|
||||||
|
|
||||||
# If true, dulcepan will save immediately when interactive selection is stopped
|
# If true, dulcepan will save immediately when interactive selection is stopped
|
||||||
# or when a whole output is selected with a mouse button.
|
# or when a whole output is selected with a mouse button.
|
||||||
quick-select = false
|
quick-select = false
|
||||||
|
|||||||
25
src/config.c
25
src/config.c
@@ -92,12 +92,17 @@ void dp_config_load(struct dp_state *state, const char *user_path) {
|
|||||||
.quit_key = XKB_KEY_Escape,
|
.quit_key = XKB_KEY_Escape,
|
||||||
.save_key = XKB_KEY_space,
|
.save_key = XKB_KEY_space,
|
||||||
.border_size = 2,
|
.border_size = 2,
|
||||||
|
.border_gradient = DP_BORDER_GRADIENT_NONE,
|
||||||
|
.gradient_angle = 45,
|
||||||
|
.loop_step = 100,
|
||||||
|
.animation_duration = 0,
|
||||||
.png_compression = 6,
|
.png_compression = 6,
|
||||||
.quick_select = false,
|
.quick_select = false,
|
||||||
};
|
};
|
||||||
bytes_to_color((uint8_t[]){0xff, 0xff, 0xff, 0x40}, config->unselected_color);
|
bytes_to_color((uint8_t[]){0xff, 0xff, 0xff, 0x40}, config->unselected_color);
|
||||||
bytes_to_color((uint8_t[]){0x00, 0x00, 0x00, 0x00}, config->selected_color);
|
bytes_to_color((uint8_t[]){0x00, 0x00, 0x00, 0x00}, config->selected_color);
|
||||||
bytes_to_color((uint8_t[]){0xff, 0xff, 0xff, 0xff}, config->border_color);
|
bytes_to_color((uint8_t[]){0xff, 0xff, 0xff, 0xff}, config->border_color);
|
||||||
|
bytes_to_color((uint8_t[]){0x00, 0x00, 0x00, 0xff}, config->border_secondary_color);
|
||||||
|
|
||||||
FILE *fp = NULL;
|
FILE *fp = NULL;
|
||||||
if (user_path != NULL) {
|
if (user_path != NULL) {
|
||||||
@@ -180,8 +185,28 @@ void dp_config_load(struct dp_state *state, const char *user_path) {
|
|||||||
load_color(value, line_idx, config->selected_color);
|
load_color(value, line_idx, config->selected_color);
|
||||||
} else if (strcmp(key, "border-color") == 0) {
|
} else if (strcmp(key, "border-color") == 0) {
|
||||||
load_color(value, line_idx, config->border_color);
|
load_color(value, line_idx, config->border_color);
|
||||||
|
} else if (strcmp(key, "border-secondary-color") == 0) {
|
||||||
|
load_color(value, line_idx, config->border_secondary_color);
|
||||||
} else if (strcmp(key, "border-size") == 0) {
|
} else if (strcmp(key, "border-size") == 0) {
|
||||||
load_int(value, line_idx, 0, INT_MAX, &config->border_size);
|
load_int(value, line_idx, 0, INT_MAX, &config->border_size);
|
||||||
|
} else if (strcmp(key, "border-gradient") == 0) {
|
||||||
|
if (strcmp(value, "none") == 0) {
|
||||||
|
config->border_gradient = DP_BORDER_GRADIENT_NONE;
|
||||||
|
} else if (strcmp(value, "linear") == 0) {
|
||||||
|
config->border_gradient = DP_BORDER_GRADIENT_LINEAR;
|
||||||
|
} else if (strcmp(value, "loop") == 0) {
|
||||||
|
config->border_gradient = DP_BORDER_GRADIENT_LOOP;
|
||||||
|
} else {
|
||||||
|
dp_log_fatal("Config: invalid border-gradient %s on line %d", value, line_idx);
|
||||||
|
}
|
||||||
|
} else if (strcmp(key, "gradient-angle") == 0) {
|
||||||
|
int deg;
|
||||||
|
load_int(value, line_idx, INT_MIN, INT_MAX, °);
|
||||||
|
config->gradient_angle = (double)deg * DP_PI / 180;
|
||||||
|
} else if (strcmp(key, "loop-step") == 0) {
|
||||||
|
load_int(value, line_idx, 1, INT_MAX, &config->loop_step);
|
||||||
|
} else if (strcmp(key, "animation-duration") == 0) {
|
||||||
|
load_int(value, line_idx, 0, INT_MAX, &config->animation_duration);
|
||||||
} else if (strcmp(key, "png-compression") == 0) {
|
} else if (strcmp(key, "png-compression") == 0) {
|
||||||
load_int(value, line_idx, 0, 9, &config->png_compression);
|
load_int(value, line_idx, 0, 9, &config->png_compression);
|
||||||
} else if (strcmp(key, "quick-select") == 0) {
|
} else if (strcmp(key, "quick-select") == 0) {
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
#include <wayland-util.h>
|
#include <wayland-util.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
|
||||||
|
#define DP_PI 3.14159265358979323846
|
||||||
|
// sqrt(2) / 2
|
||||||
|
#define DP_SQRT2_2 0.7071067811865476
|
||||||
|
|
||||||
// Per-output
|
// Per-output
|
||||||
#define DP_SWAPCHAIN_LEN 2
|
#define DP_SWAPCHAIN_LEN 2
|
||||||
|
|
||||||
@@ -128,14 +132,28 @@ enum dp_file_format {
|
|||||||
DP_FILE_PPM,
|
DP_FILE_PPM,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum dp_border_gradient {
|
||||||
|
DP_BORDER_GRADIENT_NONE,
|
||||||
|
DP_BORDER_GRADIENT_LINEAR,
|
||||||
|
DP_BORDER_GRADIENT_LOOP,
|
||||||
|
};
|
||||||
|
|
||||||
struct dp_config {
|
struct dp_config {
|
||||||
// RGBA, not premultiplied
|
// RGBA, not premultiplied
|
||||||
float unselected_color[4];
|
float unselected_color[4];
|
||||||
float selected_color[4];
|
float selected_color[4];
|
||||||
float border_color[4];
|
float border_color[4];
|
||||||
|
float border_secondary_color[4];
|
||||||
|
|
||||||
xkb_keysym_t quit_key;
|
xkb_keysym_t quit_key;
|
||||||
xkb_keysym_t save_key;
|
xkb_keysym_t save_key;
|
||||||
|
|
||||||
int border_size; // 0 if disabled
|
int border_size; // 0 if disabled
|
||||||
|
enum dp_border_gradient border_gradient;
|
||||||
|
double gradient_angle; // In radians
|
||||||
|
int loop_step;
|
||||||
|
int animation_duration; // In milliseconds
|
||||||
|
|
||||||
int png_compression;
|
int png_compression;
|
||||||
bool quick_select;
|
bool quick_select;
|
||||||
};
|
};
|
||||||
@@ -172,6 +190,11 @@ struct dp_state {
|
|||||||
enum dp_file_format output_format;
|
enum dp_file_format output_format;
|
||||||
|
|
||||||
bool show_cursors;
|
bool show_cursors;
|
||||||
|
|
||||||
|
cairo_pattern_t *border_pattern;
|
||||||
|
cairo_matrix_t precomputed_linear_matrix;
|
||||||
|
double loop_step_scale;
|
||||||
|
double time_multiplier;
|
||||||
};
|
};
|
||||||
|
|
||||||
void dp_config_load(struct dp_state *state, const char *user_path);
|
void dp_config_load(struct dp_state *state, const char *user_path);
|
||||||
@@ -207,6 +230,8 @@ void dp_log_fatal(const char *fmt, ...);
|
|||||||
void *dp_zalloc(size_t size);
|
void *dp_zalloc(size_t size);
|
||||||
char *dp_strdup(const char *str);
|
char *dp_strdup(const char *str);
|
||||||
|
|
||||||
|
void dp_matrix_compute_linear(cairo_matrix_t *matrix, double angle);
|
||||||
|
|
||||||
const char *dp_ext_from_path(const char *path);
|
const char *dp_ext_from_path(const char *path);
|
||||||
enum dp_file_format dp_ext_to_format(const char *ext);
|
enum dp_file_format dp_ext_to_format(const char *ext);
|
||||||
|
|
||||||
|
|||||||
35
src/main.c
35
src/main.c
@@ -222,15 +222,46 @@ int main(int argc, char **argv) {
|
|||||||
wl_list_init(&state.outputs);
|
wl_list_init(&state.outputs);
|
||||||
wl_list_init(&state.seats);
|
wl_list_init(&state.seats);
|
||||||
|
|
||||||
state.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
|
||||||
|
|
||||||
state.display = wl_display_connect(NULL);
|
state.display = wl_display_connect(NULL);
|
||||||
if (state.display == NULL) {
|
if (state.display == NULL) {
|
||||||
dp_log_fatal("Failed to connect to a Wayland compositor");
|
dp_log_fatal("Failed to connect to a Wayland compositor");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
|
|
||||||
|
struct dp_config *config = &state.config;
|
||||||
|
if (config->border_gradient != DP_BORDER_GRADIENT_NONE) {
|
||||||
|
state.border_pattern = cairo_pattern_create_linear(0, 0, 1, 0);
|
||||||
|
cairo_pattern_add_color_stop_rgba(state.border_pattern, 0, config->border_color[0],
|
||||||
|
config->border_color[1], config->border_color[2], config->border_color[3]);
|
||||||
|
cairo_pattern_add_color_stop_rgba(state.border_pattern, 1,
|
||||||
|
config->border_secondary_color[0], config->border_secondary_color[1],
|
||||||
|
config->border_secondary_color[2], config->border_secondary_color[3]);
|
||||||
|
|
||||||
|
switch (config->border_gradient) {
|
||||||
|
case DP_BORDER_GRADIENT_NONE:
|
||||||
|
abort(); // Unreachable
|
||||||
|
case DP_BORDER_GRADIENT_LINEAR:
|
||||||
|
if (config->animation_duration != 0) {
|
||||||
|
state.time_multiplier = DP_PI * 2.0 / config->animation_duration;
|
||||||
|
} else {
|
||||||
|
dp_matrix_compute_linear(&state.precomputed_linear_matrix, config->gradient_angle);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DP_BORDER_GRADIENT_LOOP:
|
||||||
|
cairo_pattern_set_extend(state.border_pattern, CAIRO_EXTEND_REFLECT);
|
||||||
|
state.loop_step_scale = 1.0 / config->loop_step;
|
||||||
|
if (config->animation_duration != 0) {
|
||||||
|
state.time_multiplier = 2.0 / config->animation_duration;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
run(&state);
|
run(&state);
|
||||||
|
|
||||||
|
cairo_pattern_destroy(state.border_pattern);
|
||||||
|
|
||||||
xkb_context_unref(state.xkb_context);
|
xkb_context_unref(state.xkb_context);
|
||||||
|
|
||||||
wl_display_disconnect(state.display);
|
wl_display_disconnect(state.display);
|
||||||
|
|||||||
72
src/output.c
72
src/output.c
@@ -1,6 +1,7 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
#include <time.h>
|
||||||
#include <wayland-client-protocol.h>
|
#include <wayland-client-protocol.h>
|
||||||
|
|
||||||
#include "dulcepan.h"
|
#include "dulcepan.h"
|
||||||
@@ -265,6 +266,56 @@ static inline void set_cairo_color(cairo_t *cairo, float color[static 4]) {
|
|||||||
cairo_set_source_rgba(cairo, color[0], color[1], color[2], color[3]);
|
cairo_set_source_rgba(cairo, color[0], color[1], color[2], color[3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline uint32_t get_time_msec(void) {
|
||||||
|
struct timespec now;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
return (uint32_t)(now.tv_sec * 1000 + now.tv_nsec / 1000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setup_linear_gradient(cairo_t *cairo, struct dp_state *state) {
|
||||||
|
struct dp_selection *selection = &state->selection;
|
||||||
|
struct dp_config *config = &state->config;
|
||||||
|
|
||||||
|
cairo_matrix_t matrix;
|
||||||
|
|
||||||
|
if (config->animation_duration != 0) {
|
||||||
|
double angle = (double)get_time_msec() * state->time_multiplier;
|
||||||
|
dp_matrix_compute_linear(&matrix, angle);
|
||||||
|
} else {
|
||||||
|
matrix = state->precomputed_linear_matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
cairo_matrix_scale(&matrix, 1.0 / selection->width, 1.0 / selection->height);
|
||||||
|
cairo_matrix_translate(&matrix, -selection->x, -selection->y);
|
||||||
|
|
||||||
|
cairo_pattern_set_matrix(state->border_pattern, &matrix);
|
||||||
|
cairo_set_source(cairo, state->border_pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setup_loop_gradient(cairo_t *cairo, struct dp_state *state) {
|
||||||
|
struct dp_selection *selection = &state->selection;
|
||||||
|
struct dp_config *config = &state->config;
|
||||||
|
|
||||||
|
double scale = state->loop_step_scale / selection->output->scale;
|
||||||
|
|
||||||
|
double offset =
|
||||||
|
(selection->x + selection->y + (selection->width + selection->height) / 2.0) * scale;
|
||||||
|
if (config->animation_duration != 0) {
|
||||||
|
offset += (double)get_time_msec() * state->time_multiplier / selection->output->scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
cairo_matrix_t matrix;
|
||||||
|
cairo_matrix_init(&matrix, scale, 0, 0, scale, fmod(offset, 2.0), 0);
|
||||||
|
|
||||||
|
// Rotate by 45°
|
||||||
|
cairo_matrix_t rotation;
|
||||||
|
cairo_matrix_init(&rotation, -DP_SQRT2_2, DP_SQRT2_2, -DP_SQRT2_2, -DP_SQRT2_2, 0, 0);
|
||||||
|
cairo_matrix_multiply(&matrix, &rotation, &matrix);
|
||||||
|
|
||||||
|
cairo_pattern_set_matrix(state->border_pattern, &matrix);
|
||||||
|
cairo_set_source(cairo, state->border_pattern);
|
||||||
|
}
|
||||||
|
|
||||||
static void redraw(struct dp_output *output) {
|
static void redraw(struct dp_output *output) {
|
||||||
assert(output->redraw_callback == NULL);
|
assert(output->redraw_callback == NULL);
|
||||||
|
|
||||||
@@ -282,6 +333,8 @@ static void redraw(struct dp_output *output) {
|
|||||||
}
|
}
|
||||||
buffer->used = true;
|
buffer->used = true;
|
||||||
|
|
||||||
|
output->needs_redraw = false;
|
||||||
|
|
||||||
struct dp_state *state = output->state;
|
struct dp_state *state = output->state;
|
||||||
struct dp_selection *selection = &state->selection;
|
struct dp_selection *selection = &state->selection;
|
||||||
struct dp_config *config = &state->config;
|
struct dp_config *config = &state->config;
|
||||||
@@ -299,7 +352,19 @@ static void redraw(struct dp_output *output) {
|
|||||||
double off = scaled_size / 2.0;
|
double off = scaled_size / 2.0;
|
||||||
cairo_rectangle(buffer->cairo, selection->x - off, selection->y - off,
|
cairo_rectangle(buffer->cairo, selection->x - off, selection->y - off,
|
||||||
selection->width + scaled_size, selection->height + scaled_size);
|
selection->width + scaled_size, selection->height + scaled_size);
|
||||||
set_cairo_color(buffer->cairo, config->border_color);
|
|
||||||
|
switch (config->border_gradient) {
|
||||||
|
case DP_BORDER_GRADIENT_NONE:
|
||||||
|
set_cairo_color(buffer->cairo, config->border_color);
|
||||||
|
break;
|
||||||
|
case DP_BORDER_GRADIENT_LINEAR:
|
||||||
|
setup_linear_gradient(buffer->cairo, state);
|
||||||
|
break;
|
||||||
|
case DP_BORDER_GRADIENT_LOOP:
|
||||||
|
setup_loop_gradient(buffer->cairo, state);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
cairo_stroke(buffer->cairo);
|
cairo_stroke(buffer->cairo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,6 +372,10 @@ static void redraw(struct dp_output *output) {
|
|||||||
buffer->cairo, selection->x, selection->y, selection->width, selection->height);
|
buffer->cairo, selection->x, selection->y, selection->width, selection->height);
|
||||||
set_cairo_color(buffer->cairo, config->selected_color);
|
set_cairo_color(buffer->cairo, config->selected_color);
|
||||||
cairo_fill(buffer->cairo);
|
cairo_fill(buffer->cairo);
|
||||||
|
|
||||||
|
if (config->border_gradient != DP_BORDER_GRADIENT_NONE && config->animation_duration != 0) {
|
||||||
|
output->needs_redraw = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wl_surface_attach(output->select_surface, buffer->wl_buffer, 0, 0);
|
wl_surface_attach(output->select_surface, buffer->wl_buffer, 0, 0);
|
||||||
@@ -315,7 +384,6 @@ static void redraw(struct dp_output *output) {
|
|||||||
|
|
||||||
output->redraw_callback = wl_surface_frame(output->select_surface);
|
output->redraw_callback = wl_surface_frame(output->select_surface);
|
||||||
wl_callback_add_listener(output->redraw_callback, &redraw_callback_listener, output);
|
wl_callback_add_listener(output->redraw_callback, &redraw_callback_listener, output);
|
||||||
output->needs_redraw = false;
|
|
||||||
|
|
||||||
wl_surface_commit(output->select_surface);
|
wl_surface_commit(output->select_surface);
|
||||||
}
|
}
|
||||||
|
|||||||
14
src/util.c
14
src/util.c
@@ -63,3 +63,17 @@ enum dp_file_format dp_ext_to_format(const char *ext) {
|
|||||||
}
|
}
|
||||||
return DP_FILE_UNKNOWN;
|
return DP_FILE_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dp_matrix_compute_linear(cairo_matrix_t *matrix, double angle) {
|
||||||
|
double c = cos(angle), s = sin(angle);
|
||||||
|
double f = 1.0 / (fabs(c) + fabs(s));
|
||||||
|
|
||||||
|
cairo_matrix_init_translate(matrix, 0.5, 0.5);
|
||||||
|
cairo_matrix_scale(matrix, f, 1);
|
||||||
|
|
||||||
|
cairo_matrix_t tmp;
|
||||||
|
cairo_matrix_init(&tmp, c, s, -s, c, 0, 0);
|
||||||
|
cairo_matrix_multiply(matrix, &tmp, matrix);
|
||||||
|
|
||||||
|
cairo_matrix_translate(matrix, -0.5, -0.5);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user