mirror of
https://codeberg.org/vyivel/dulcepan/
synced 2025-06-24 22:44:18 +03:00
Compare commits
21 Commits
v1.0.0
...
quick-sele
Author | SHA1 | Date | |
---|---|---|---|
60518fe18e | |||
b9e94f9584 | |||
8bc62fdae4 | |||
accbd01ad6 | |||
0167c017a0 | |||
9879f3ad69 | |||
51f744c876 | |||
640b015053 | |||
0ccb868b39 | |||
7cc26b8e38 | |||
d2620c60c5 | |||
c343fc1f73 | |||
f9665e2661 | |||
fccc2e2e97 | |||
5bbfbd4d84 | |||
e797e5a324 | |||
bdf8308c4e | |||
6158dd112b | |||
6f7dfab735 | |||
553147259b | |||
e81e274380 |
@ -30,4 +30,4 @@ GPL-3.0-only
|
|||||||
|
|
||||||
See `LICENSE` for more information.
|
See `LICENSE` for more information.
|
||||||
|
|
||||||
Copyright © 2024 Kirill Primak
|
Copyright © 2025 Kirill Primak
|
||||||
|
12
dulcepan.cfg
12
dulcepan.cfg
@ -33,12 +33,18 @@ animation-duration = 0
|
|||||||
# 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
|
||||||
|
|
||||||
# If true, dulcepan will remember selection between runs
|
# If true, dulcepan will allow editing the current selection if quick-select is
|
||||||
|
# true. Has no effect if quick-select is false.
|
||||||
|
quick-select-allow-editing = true
|
||||||
|
|
||||||
|
# If true, dulcepan will remember selection between runs.
|
||||||
|
# The state is stored at $XDG_CACHE_HOME/dulcepan.
|
||||||
persistence = true
|
persistence = true
|
||||||
|
|
||||||
# PNG (zlib) compression level, 0-9
|
# PNG (zlib) compression level, 0-9
|
||||||
png-compression = 6
|
png-compression = 6
|
||||||
|
|
||||||
# Key bindings
|
# Key bindings. Each binding is a comma-separated list of key names; empty names
|
||||||
|
# are ignored. A binding may be empty.
|
||||||
quit-key = Escape
|
quit-key = Escape
|
||||||
save-key = Space
|
save-key = Space,Enter
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
project(
|
project(
|
||||||
'dulcepan',
|
'dulcepan',
|
||||||
'c',
|
'c',
|
||||||
version: '1.0.0',
|
version: '1.0.3',
|
||||||
license: 'GPL-3.0-only',
|
license: 'GPL-3.0-only',
|
||||||
default_options: [
|
default_options: [
|
||||||
'c_std=c11',
|
'c_std=c11',
|
||||||
|
63
src/config.c
63
src/config.c
@ -15,7 +15,22 @@ static inline void bytes_to_color(uint8_t bytes[static 4], float out[static 4])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void load_color(const char *value, int line_idx, float out[static 4]) {
|
static void keybinding_init(struct dp_keybinding *kb, xkb_keysym_t *syms, size_t n_syms) {
|
||||||
|
*kb = (struct dp_keybinding){
|
||||||
|
.syms = dp_zalloc(sizeof(*kb->syms) * n_syms),
|
||||||
|
.n_syms = n_syms,
|
||||||
|
};
|
||||||
|
for (size_t i = 0; i < n_syms; i++) {
|
||||||
|
xkb_keysym_t sym = syms[i];
|
||||||
|
kb->syms[i] = sym;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keybinding_finish(struct dp_keybinding *kb) {
|
||||||
|
free(kb->syms);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void load_color(char *value, int line_idx, float out[static 4]) {
|
||||||
size_t len = strlen(value);
|
size_t len = strlen(value);
|
||||||
|
|
||||||
uint8_t bytes[4] = {0, 0, 0, 0};
|
uint8_t bytes[4] = {0, 0, 0, 0};
|
||||||
@ -46,7 +61,7 @@ bad:
|
|||||||
dp_log_fatal("Config: invalid color %s on line %d", value, line_idx);
|
dp_log_fatal("Config: invalid color %s on line %d", value, line_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void load_int(const char *value, int line_idx, int min, int max, int *out) {
|
static void load_int(char *value, int line_idx, int min, int max, int *out) {
|
||||||
const char *p = value;
|
const char *p = value;
|
||||||
int mul = 1;
|
int mul = 1;
|
||||||
if (*p == '-') {
|
if (*p == '-') {
|
||||||
@ -68,7 +83,7 @@ static void load_int(const char *value, int line_idx, int min, int max, int *out
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void load_bool(const char *value, int line_idx, bool *out) {
|
static void load_bool(char *value, int line_idx, bool *out) {
|
||||||
if (strcmp(value, "true") == 0) {
|
if (strcmp(value, "true") == 0) {
|
||||||
*out = true;
|
*out = true;
|
||||||
} else if (strcmp(value, "false") == 0) {
|
} else if (strcmp(value, "false") == 0) {
|
||||||
@ -78,19 +93,31 @@ static void load_bool(const char *value, int line_idx, bool *out) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void load_key(const char *value, int line_idx, xkb_keysym_t *out) {
|
static void load_key(char *value, int line_idx, struct dp_keybinding *out) {
|
||||||
*out = xkb_keysym_from_name(value, XKB_KEYSYM_CASE_INSENSITIVE);
|
size_t n_syms = 0;
|
||||||
if (*out == XKB_KEY_NoSymbol) {
|
xkb_keysym_t syms[32];
|
||||||
dp_log_fatal("Config: unknown key %s on line %d", value, line_idx);
|
|
||||||
|
char *save_ptr = NULL;
|
||||||
|
for (char *name; (name = strtok_r(value, ",", &save_ptr)) != NULL; value = NULL) {
|
||||||
|
if (n_syms == sizeof(syms) / sizeof(*syms)) {
|
||||||
|
// chill out
|
||||||
|
dp_log_fatal("Config: too many keys on line %d", line_idx);
|
||||||
}
|
}
|
||||||
|
xkb_keysym_t sym = xkb_keysym_from_name(name, XKB_KEYSYM_CASE_INSENSITIVE);
|
||||||
|
if (sym == XKB_KEY_NoSymbol) {
|
||||||
|
dp_log_fatal("Config: unknown key \"%s\" on line %d", name, line_idx);
|
||||||
|
}
|
||||||
|
syms[n_syms++] = sym;
|
||||||
|
}
|
||||||
|
|
||||||
|
keybinding_finish(out);
|
||||||
|
keybinding_init(out, syms, n_syms);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dp_config_load(struct dp_state *state, const char *user_path) {
|
void dp_config_load(struct dp_state *state, const char *user_path) {
|
||||||
struct dp_config *config = &state->config;
|
struct dp_config *config = &state->config;
|
||||||
|
|
||||||
*config = (struct dp_config){
|
*config = (struct dp_config){
|
||||||
.quit_key = XKB_KEY_Escape,
|
|
||||||
.save_key = XKB_KEY_space,
|
|
||||||
.border_size = 2,
|
.border_size = 2,
|
||||||
.border_gradient = DP_BORDER_GRADIENT_NONE,
|
.border_gradient = DP_BORDER_GRADIENT_NONE,
|
||||||
.gradient_angle = 45,
|
.gradient_angle = 45,
|
||||||
@ -98,13 +125,18 @@ void dp_config_load(struct dp_state *state, const char *user_path) {
|
|||||||
.animation_duration = 0,
|
.animation_duration = 0,
|
||||||
.png_compression = 6,
|
.png_compression = 6,
|
||||||
.quick_select = false,
|
.quick_select = false,
|
||||||
|
.quick_select_allow_editing = true,
|
||||||
.persistence = true,
|
.persistence = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
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);
|
bytes_to_color((uint8_t[]){0x00, 0x00, 0x00, 0xff}, config->border_secondary_color);
|
||||||
|
|
||||||
|
keybinding_init(&config->quit_kb, (xkb_keysym_t[]){XKB_KEY_Escape}, 1);
|
||||||
|
keybinding_init(&config->save_kb, (xkb_keysym_t[]){XKB_KEY_space, XKB_KEY_Return}, 2);
|
||||||
|
|
||||||
FILE *fp = NULL;
|
FILE *fp = NULL;
|
||||||
if (user_path != NULL) {
|
if (user_path != NULL) {
|
||||||
fp = fopen(user_path, "r");
|
fp = fopen(user_path, "r");
|
||||||
@ -212,12 +244,14 @@ void dp_config_load(struct dp_state *state, const char *user_path) {
|
|||||||
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) {
|
||||||
load_bool(value, line_idx, &config->quick_select);
|
load_bool(value, line_idx, &config->quick_select);
|
||||||
|
} else if (strcmp(key, "quick-select-allow-editing") == 0) {
|
||||||
|
load_bool(value, line_idx, &config->quick_select_allow_editing);
|
||||||
} else if (strcmp(key, "persistence") == 0) {
|
} else if (strcmp(key, "persistence") == 0) {
|
||||||
load_bool(value, line_idx, &config->persistence);
|
load_bool(value, line_idx, &config->persistence);
|
||||||
} else if (strcmp(key, "quit-key") == 0) {
|
} else if (strcmp(key, "quit-key") == 0) {
|
||||||
load_key(value, line_idx, &config->quit_key);
|
load_key(value, line_idx, &config->quit_kb);
|
||||||
} else if (strcmp(key, "save-key") == 0) {
|
} else if (strcmp(key, "save-key") == 0) {
|
||||||
load_key(value, line_idx, &config->save_key);
|
load_key(value, line_idx, &config->save_kb);
|
||||||
} else {
|
} else {
|
||||||
dp_log_error("Config: unknown key %s on line %d", key, line_idx);
|
dp_log_error("Config: unknown key %s on line %d", key, line_idx);
|
||||||
}
|
}
|
||||||
@ -226,3 +260,10 @@ void dp_config_load(struct dp_state *state, const char *user_path) {
|
|||||||
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dp_config_finish(struct dp_state *state) {
|
||||||
|
struct dp_config *config = &state->config;
|
||||||
|
|
||||||
|
keybinding_finish(&config->quit_kb);
|
||||||
|
keybinding_finish(&config->save_kb);
|
||||||
|
}
|
||||||
|
@ -71,6 +71,7 @@ struct dp_output {
|
|||||||
void *frame_data;
|
void *frame_data;
|
||||||
size_t frame_size;
|
size_t frame_size;
|
||||||
|
|
||||||
|
// wl_buffer is NULL if uninitialized
|
||||||
struct dp_buffer swapchain[DP_SWAPCHAIN_LEN];
|
struct dp_buffer swapchain[DP_SWAPCHAIN_LEN];
|
||||||
|
|
||||||
struct wl_list link;
|
struct wl_list link;
|
||||||
@ -88,6 +89,7 @@ struct dp_seat {
|
|||||||
struct xkb_keymap *xkb_keymap;
|
struct xkb_keymap *xkb_keymap;
|
||||||
struct xkb_state *xkb_state;
|
struct xkb_state *xkb_state;
|
||||||
|
|
||||||
|
uint32_t pointer_serial;
|
||||||
struct wp_cursor_shape_device_v1 *cursor_shape_device;
|
struct wp_cursor_shape_device_v1 *cursor_shape_device;
|
||||||
|
|
||||||
// The output the pointer is on
|
// The output the pointer is on
|
||||||
@ -124,6 +126,14 @@ struct dp_selection {
|
|||||||
int resize_edges;
|
int resize_edges;
|
||||||
// Resize anchor
|
// Resize anchor
|
||||||
double resize_x, resize_y;
|
double resize_x, resize_y;
|
||||||
|
|
||||||
|
// Used for toggling
|
||||||
|
struct {
|
||||||
|
// If not NULL, the current output has been selected via the dedicated
|
||||||
|
// action rather than by persistent state loading or manual resizing
|
||||||
|
struct dp_output *output;
|
||||||
|
double x, y, width, height;
|
||||||
|
} last_partial;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum dp_file_format {
|
enum dp_file_format {
|
||||||
@ -139,6 +149,11 @@ enum dp_border_gradient {
|
|||||||
DP_BORDER_GRADIENT_LOOP,
|
DP_BORDER_GRADIENT_LOOP,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct dp_keybinding {
|
||||||
|
xkb_keysym_t *syms;
|
||||||
|
size_t n_syms;
|
||||||
|
};
|
||||||
|
|
||||||
struct dp_config {
|
struct dp_config {
|
||||||
// RGBA, not premultiplied
|
// RGBA, not premultiplied
|
||||||
float unselected_color[4];
|
float unselected_color[4];
|
||||||
@ -146,8 +161,8 @@ struct dp_config {
|
|||||||
float border_color[4];
|
float border_color[4];
|
||||||
float border_secondary_color[4];
|
float border_secondary_color[4];
|
||||||
|
|
||||||
xkb_keysym_t quit_key;
|
struct dp_keybinding quit_kb;
|
||||||
xkb_keysym_t save_key;
|
struct dp_keybinding save_kb;
|
||||||
|
|
||||||
int border_size; // 0 if disabled
|
int border_size; // 0 if disabled
|
||||||
enum dp_border_gradient border_gradient;
|
enum dp_border_gradient border_gradient;
|
||||||
@ -156,7 +171,10 @@ struct dp_config {
|
|||||||
int animation_duration; // In milliseconds
|
int animation_duration; // In milliseconds
|
||||||
|
|
||||||
int png_compression;
|
int png_compression;
|
||||||
|
|
||||||
bool quick_select;
|
bool quick_select;
|
||||||
|
bool quick_select_allow_editing;
|
||||||
|
|
||||||
bool persistence;
|
bool persistence;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -172,6 +190,8 @@ struct dp_state {
|
|||||||
struct wl_shm *shm;
|
struct wl_shm *shm;
|
||||||
struct zwlr_layer_shell_v1 *layer_shell;
|
struct zwlr_layer_shell_v1 *layer_shell;
|
||||||
struct zwlr_screencopy_manager_v1 *screencopy_manager;
|
struct zwlr_screencopy_manager_v1 *screencopy_manager;
|
||||||
|
|
||||||
|
// Optional
|
||||||
struct wp_cursor_shape_manager_v1 *cursor_shape_manager;
|
struct wp_cursor_shape_manager_v1 *cursor_shape_manager;
|
||||||
|
|
||||||
bool initialized;
|
bool initialized;
|
||||||
@ -191,6 +211,8 @@ struct dp_state {
|
|||||||
const char *output_path; // May be NULL
|
const char *output_path; // May be NULL
|
||||||
enum dp_file_format output_format;
|
enum dp_file_format output_format;
|
||||||
|
|
||||||
|
bool allow_selection_editing;
|
||||||
|
|
||||||
bool show_cursors;
|
bool show_cursors;
|
||||||
|
|
||||||
cairo_pattern_t *border_pattern;
|
cairo_pattern_t *border_pattern;
|
||||||
@ -200,6 +222,7 @@ struct dp_state {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void dp_config_load(struct dp_state *state, const char *user_path);
|
void dp_config_load(struct dp_state *state, const char *user_path);
|
||||||
|
void dp_config_finish(struct dp_state *state);
|
||||||
|
|
||||||
// When done, data must be unmapped
|
// When done, data must be unmapped
|
||||||
struct wl_buffer *dp_buffer_create(struct dp_state *state, int32_t width, int32_t height,
|
struct wl_buffer *dp_buffer_create(struct dp_state *state, int32_t width, int32_t height,
|
||||||
@ -222,7 +245,7 @@ void dp_select_stop_interactive(struct dp_selection *selection);
|
|||||||
void dp_select_notify_pointer_position(
|
void dp_select_notify_pointer_position(
|
||||||
struct dp_selection *selection, struct dp_output *output, double x, double y);
|
struct dp_selection *selection, struct dp_output *output, double x, double y);
|
||||||
|
|
||||||
void dp_select_whole(struct dp_selection *selection, struct dp_output *output);
|
void dp_select_toggle_whole(struct dp_selection *selection, struct dp_output *output);
|
||||||
|
|
||||||
void dp_save(struct dp_state *state);
|
void dp_save(struct dp_state *state);
|
||||||
|
|
||||||
|
39
src/main.c
39
src/main.c
@ -1,6 +1,7 @@
|
|||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <wayland-client-core.h>
|
#include <wayland-client-core.h>
|
||||||
#include <wayland-client-protocol.h>
|
#include <wayland-client-protocol.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
@ -89,8 +90,6 @@ static void run(struct dp_state *state) {
|
|||||||
dp_log_fatal("The compositor has no zwlr_layer_shell_v1");
|
dp_log_fatal("The compositor has no zwlr_layer_shell_v1");
|
||||||
} else if (state->screencopy_manager == NULL) {
|
} else if (state->screencopy_manager == NULL) {
|
||||||
dp_log_fatal("The compositor has no zwlr_screencopy_manager_v1");
|
dp_log_fatal("The compositor has no zwlr_screencopy_manager_v1");
|
||||||
} else if (state->cursor_shape_manager == NULL) {
|
|
||||||
dp_log_fatal("The compositor has no wp_cursor_shape_manager_v1");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wl_list_empty(&state->outputs)) {
|
if (wl_list_empty(&state->outputs)) {
|
||||||
@ -150,7 +149,10 @@ static void run(struct dp_state *state) {
|
|||||||
wl_shm_destroy(state->shm);
|
wl_shm_destroy(state->shm);
|
||||||
zwlr_layer_shell_v1_destroy(state->layer_shell);
|
zwlr_layer_shell_v1_destroy(state->layer_shell);
|
||||||
zwlr_screencopy_manager_v1_destroy(state->screencopy_manager);
|
zwlr_screencopy_manager_v1_destroy(state->screencopy_manager);
|
||||||
|
|
||||||
|
if (state->cursor_shape_manager) {
|
||||||
wp_cursor_shape_manager_v1_destroy(state->cursor_shape_manager);
|
wp_cursor_shape_manager_v1_destroy(state->cursor_shape_manager);
|
||||||
|
}
|
||||||
|
|
||||||
wl_registry_destroy(registry);
|
wl_registry_destroy(registry);
|
||||||
}
|
}
|
||||||
@ -165,12 +167,16 @@ static void help(const char *prog) {
|
|||||||
" -f <format> Specify the output file format.\n"
|
" -f <format> Specify the output file format.\n"
|
||||||
" -o <path> Specify the output file path.\n"
|
" -o <path> Specify the output file path.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"If the output file path is not specified, the resuling image will be printed\n"
|
"If the output file path is not specified, the resuling image will be printed to\n"
|
||||||
"to the standard output. If the output file format is not specified, it is\n"
|
"the standard output, which is expected to not be a terminal. If the output file\n"
|
||||||
"guessed from the output file path if it's specified, and assumed to be PNG\n"
|
"format is not specified, it is guessed from the output file path if it's\n"
|
||||||
"otherwise.\n"
|
"specified, and assumed to be PNG otherwise.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Supported formats: png, ppm.\n",
|
"Supported formats: png, ppm.\n"
|
||||||
|
"\n"
|
||||||
|
"Use the left or right mouse button to select a part of the output. A selection\n"
|
||||||
|
"can also be moved and resized with the left mouse button. Clicking the middle\n"
|
||||||
|
"mouse button toggles the selection of the entire output.\n",
|
||||||
prog);
|
prog);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,9 +212,13 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.basedir_ctx = sfdo_basedir_ctx_create();
|
if (state.output_path == NULL && isatty(STDOUT_FILENO)) {
|
||||||
|
fprintf(stderr,
|
||||||
dp_config_load(&state, config_path);
|
"Refusing to run as the standard output is a terminal and there's no output file\n"
|
||||||
|
"path specified.\n\n");
|
||||||
|
help(argv[0]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
if (state.output_format == DP_FILE_UNKNOWN) {
|
if (state.output_format == DP_FILE_UNKNOWN) {
|
||||||
if (state.output_path != NULL) {
|
if (state.output_path != NULL) {
|
||||||
@ -223,6 +233,10 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.basedir_ctx = sfdo_basedir_ctx_create();
|
||||||
|
|
||||||
|
dp_config_load(&state, config_path);
|
||||||
|
|
||||||
wl_list_init(&state.outputs);
|
wl_list_init(&state.outputs);
|
||||||
wl_list_init(&state.seats);
|
wl_list_init(&state.seats);
|
||||||
|
|
||||||
@ -234,6 +248,9 @@ int main(int argc, char **argv) {
|
|||||||
state.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
state.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
|
|
||||||
struct dp_config *config = &state.config;
|
struct dp_config *config = &state.config;
|
||||||
|
|
||||||
|
state.allow_selection_editing = !config->quick_select || config->quick_select_allow_editing;
|
||||||
|
|
||||||
if (config->border_gradient != DP_BORDER_GRADIENT_NONE) {
|
if (config->border_gradient != DP_BORDER_GRADIENT_NONE) {
|
||||||
state.border_pattern = cairo_pattern_create_linear(0, 0, 1, 0);
|
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],
|
cairo_pattern_add_color_stop_rgba(state.border_pattern, 0, config->border_color[0],
|
||||||
@ -264,6 +281,8 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
run(&state);
|
run(&state);
|
||||||
|
|
||||||
|
dp_config_finish(&state);
|
||||||
|
|
||||||
cairo_pattern_destroy(state.border_pattern);
|
cairo_pattern_destroy(state.border_pattern);
|
||||||
|
|
||||||
xkb_context_unref(state.xkb_context);
|
xkb_context_unref(state.xkb_context);
|
||||||
|
56
src/output.c
56
src/output.c
@ -79,6 +79,12 @@ static void frame_handle_buffer(void *data, struct zwlr_screencopy_frame_v1 *fra
|
|||||||
wl_buffer_destroy(output->frame_buffer);
|
wl_buffer_destroy(output->frame_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((int32_t)width != output->width || (int32_t)height != output->height) {
|
||||||
|
dp_log_fatal("Output/buffer size mismatch: mode=%" PRIi32 "x%" PRIi32 ", buffer=%" PRIu32
|
||||||
|
"x%" PRIu32,
|
||||||
|
output->width, output->height, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
output->frame_format = format;
|
output->frame_format = format;
|
||||||
output->frame_stride = (int)stride;
|
output->frame_stride = (int)stride;
|
||||||
output->frame_buffer = dp_buffer_create(output->state, output->width, output->height,
|
output->frame_buffer = dp_buffer_create(output->state, output->width, output->height,
|
||||||
@ -126,6 +132,19 @@ static const struct wl_buffer_listener buffer_listener = {
|
|||||||
.release = buffer_handle_release,
|
.release = buffer_handle_release,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void init_buffer(struct dp_output *output, struct dp_buffer *buffer) {
|
||||||
|
int stride = output->transformed_width * 4;
|
||||||
|
|
||||||
|
buffer->wl_buffer =
|
||||||
|
dp_buffer_create(output->state, output->transformed_width, output->transformed_height,
|
||||||
|
stride, WL_SHM_FORMAT_ARGB8888, &buffer->data, &buffer->size);
|
||||||
|
buffer->cairo_surface = cairo_image_surface_create_for_data(buffer->data, CAIRO_FORMAT_ARGB32,
|
||||||
|
output->transformed_width, output->transformed_height, stride);
|
||||||
|
buffer->cairo = cairo_create(buffer->cairo_surface);
|
||||||
|
|
||||||
|
wl_buffer_add_listener(buffer->wl_buffer, &buffer_listener, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
static void output_handle_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y,
|
static void output_handle_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y,
|
||||||
int32_t phys_width, int32_t phys_height, int32_t subpixel, const char *make,
|
int32_t phys_width, int32_t phys_height, int32_t subpixel, const char *make,
|
||||||
const char *model, int32_t transform) {
|
const char *model, int32_t transform) {
|
||||||
@ -143,6 +162,10 @@ static void output_handle_geometry(void *data, struct wl_output *wl_output, int3
|
|||||||
|
|
||||||
static void output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags,
|
static void output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags,
|
||||||
int32_t width, int32_t height, int32_t refresh) {
|
int32_t width, int32_t height, int32_t refresh) {
|
||||||
|
if ((flags & WL_OUTPUT_MODE_CURRENT) == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
struct dp_output *output = data;
|
struct dp_output *output = data;
|
||||||
|
|
||||||
if (output->has_geom) {
|
if (output->has_geom) {
|
||||||
@ -165,6 +188,7 @@ static void output_handle_done(void *data, struct wl_output *wl_output) {
|
|||||||
|
|
||||||
assert(output->name != NULL);
|
assert(output->name != NULL);
|
||||||
|
|
||||||
|
assert(output->width > 0 && output->height > 0);
|
||||||
if ((output->transform & WL_OUTPUT_TRANSFORM_90) != 0) {
|
if ((output->transform & WL_OUTPUT_TRANSFORM_90) != 0) {
|
||||||
output->transformed_width = output->height;
|
output->transformed_width = output->height;
|
||||||
output->transformed_height = output->width;
|
output->transformed_height = output->width;
|
||||||
@ -208,19 +232,6 @@ static void output_handle_done(void *data, struct wl_output *wl_output) {
|
|||||||
wl_subsurface_set_desync(output->select_subsurface);
|
wl_subsurface_set_desync(output->select_subsurface);
|
||||||
|
|
||||||
wl_surface_set_user_data(output->select_surface, output);
|
wl_surface_set_user_data(output->select_surface, output);
|
||||||
|
|
||||||
for (size_t i = 0; i < DP_SWAPCHAIN_LEN; i++) {
|
|
||||||
struct dp_buffer *buffer = &output->swapchain[i];
|
|
||||||
int stride = output->transformed_width * 4;
|
|
||||||
buffer->wl_buffer =
|
|
||||||
dp_buffer_create(state, output->transformed_width, output->transformed_height,
|
|
||||||
stride, WL_SHM_FORMAT_ARGB8888, &buffer->data, &buffer->size);
|
|
||||||
buffer->cairo_surface = cairo_image_surface_create_for_data(buffer->data,
|
|
||||||
CAIRO_FORMAT_ARGB32, output->transformed_width, output->transformed_height, stride);
|
|
||||||
buffer->cairo = cairo_create(buffer->cairo_surface);
|
|
||||||
|
|
||||||
wl_buffer_add_listener(buffer->wl_buffer, &buffer_listener, buffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void output_handle_scale(void *data, struct wl_output *wl_output, int32_t scale) {
|
static void output_handle_scale(void *data, struct wl_output *wl_output, int32_t scale) {
|
||||||
@ -330,6 +341,8 @@ static void redraw(struct dp_output *output) {
|
|||||||
if (buffer == NULL) {
|
if (buffer == NULL) {
|
||||||
dp_log_error("Output %s: no free buffers in a swapchain\n", output->name);
|
dp_log_error("Output %s: no free buffers in a swapchain\n", output->name);
|
||||||
return;
|
return;
|
||||||
|
} else if (buffer->wl_buffer == NULL) {
|
||||||
|
init_buffer(output, buffer);
|
||||||
}
|
}
|
||||||
buffer->used = true;
|
buffer->used = true;
|
||||||
|
|
||||||
@ -371,15 +384,22 @@ static void redraw(struct dp_output *output) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cairo_stroke(buffer->cairo);
|
cairo_stroke(buffer->cairo);
|
||||||
|
|
||||||
|
if (config->border_gradient != DP_BORDER_GRADIENT_NONE &&
|
||||||
|
config->animation_duration != 0) {
|
||||||
|
bool whole = selection->x == 0 && selection->y == 0 &&
|
||||||
|
selection->width == output->effective_width &&
|
||||||
|
selection->height == output->effective_height;
|
||||||
|
if (!whole) {
|
||||||
|
// The border is animated and visible
|
||||||
|
output->needs_redraw = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cairo_rectangle(buffer->cairo, x, y, width, height);
|
cairo_rectangle(buffer->cairo, x, y, width, 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);
|
||||||
@ -431,11 +451,13 @@ void dp_output_destroy(struct dp_output *output) {
|
|||||||
|
|
||||||
for (size_t i = 0; i < DP_SWAPCHAIN_LEN; i++) {
|
for (size_t i = 0; i < DP_SWAPCHAIN_LEN; i++) {
|
||||||
struct dp_buffer *buffer = &output->swapchain[i];
|
struct dp_buffer *buffer = &output->swapchain[i];
|
||||||
|
if (buffer->wl_buffer != NULL) {
|
||||||
wl_buffer_destroy(buffer->wl_buffer);
|
wl_buffer_destroy(buffer->wl_buffer);
|
||||||
cairo_destroy(buffer->cairo);
|
cairo_destroy(buffer->cairo);
|
||||||
cairo_surface_destroy(buffer->cairo_surface);
|
cairo_surface_destroy(buffer->cairo_surface);
|
||||||
munmap(buffer->data, buffer->size);
|
munmap(buffer->data, buffer->size);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wl_subsurface_destroy(output->select_subsurface);
|
wl_subsurface_destroy(output->select_subsurface);
|
||||||
wp_viewport_destroy(output->select_viewport);
|
wp_viewport_destroy(output->select_viewport);
|
||||||
|
70
src/seat.c
70
src/seat.c
@ -50,6 +50,15 @@ static void keyboard_handle_leave(
|
|||||||
// Ignored
|
// Ignored
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool match_keybinding(struct dp_keybinding *kb, uint32_t sym) {
|
||||||
|
for (size_t i = 0; i < kb->n_syms; i++) {
|
||||||
|
if (sym == kb->syms[i]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
||||||
uint32_t time_msec, uint32_t keycode, enum wl_keyboard_key_state key_state) {
|
uint32_t time_msec, uint32_t keycode, enum wl_keyboard_key_state key_state) {
|
||||||
struct dp_seat *seat = data;
|
struct dp_seat *seat = data;
|
||||||
@ -62,9 +71,9 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard, uin
|
|||||||
struct dp_state *state = seat->state;
|
struct dp_state *state = seat->state;
|
||||||
struct dp_config *config = &state->config;
|
struct dp_config *config = &state->config;
|
||||||
|
|
||||||
if (keysym == config->quit_key) {
|
if (match_keybinding(&config->quit_kb, keysym)) {
|
||||||
state->status = DP_STATUS_QUIT;
|
state->status = DP_STATUS_QUIT;
|
||||||
} else if (keysym == config->save_key) {
|
} else if (match_keybinding(&config->save_kb, keysym)) {
|
||||||
state->status = DP_STATUS_SAVED;
|
state->status = DP_STATUS_SAVED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,13 +92,15 @@ static const struct wl_keyboard_listener keyboard_listener = {
|
|||||||
.modifiers = keyboard_handle_modifiers,
|
.modifiers = keyboard_handle_modifiers,
|
||||||
};
|
};
|
||||||
|
|
||||||
static enum wp_cursor_shape_device_v1_shape get_cursor_shape(struct dp_selection *selection) {
|
static enum wp_cursor_shape_device_v1_shape get_cursor_shape(struct dp_state *state) {
|
||||||
|
if (state->allow_selection_editing) {
|
||||||
|
struct dp_selection *selection = &state->selection;
|
||||||
switch (selection->action) {
|
switch (selection->action) {
|
||||||
case DP_SELECTION_ACTION_NONE:
|
case DP_SELECTION_ACTION_NONE:
|
||||||
return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR;
|
break;
|
||||||
case DP_SELECTION_ACTION_RESIZING:
|
case DP_SELECTION_ACTION_RESIZING:
|
||||||
if (selection->width == 0 || selection->height == 0) {
|
if (selection->width == 0 || selection->height == 0) {
|
||||||
return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR;
|
break;
|
||||||
}
|
}
|
||||||
switch (selection->resize_edges) {
|
switch (selection->resize_edges) {
|
||||||
case DP_EDGE_TOP:
|
case DP_EDGE_TOP:
|
||||||
@ -116,24 +127,29 @@ static enum wp_cursor_shape_device_v1_shape get_cursor_shape(struct dp_selection
|
|||||||
selection->width == selection->output->effective_width &&
|
selection->width == selection->output->effective_width &&
|
||||||
selection->height == selection->output->effective_height) {
|
selection->height == selection->output->effective_height) {
|
||||||
// Moving is impossible
|
// Moving is impossible
|
||||||
return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT;
|
break;
|
||||||
}
|
}
|
||||||
return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE;
|
return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE;
|
||||||
}
|
}
|
||||||
abort(); // Unreachable
|
}
|
||||||
|
|
||||||
|
// The default cursor
|
||||||
|
return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_cursor(struct dp_seat *seat, uint32_t serial) {
|
static void update_cursor(struct dp_seat *seat) {
|
||||||
|
if (seat->cursor_shape_device != NULL) {
|
||||||
wp_cursor_shape_device_v1_set_shape(
|
wp_cursor_shape_device_v1_set_shape(
|
||||||
seat->cursor_shape_device, serial, get_cursor_shape(&seat->state->selection));
|
seat->cursor_shape_device, seat->pointer_serial, get_cursor_shape(seat->state));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void process_position(struct dp_seat *seat, wl_fixed_t x, wl_fixed_t y, uint32_t serial) {
|
static void process_position(struct dp_seat *seat, wl_fixed_t x, wl_fixed_t y) {
|
||||||
struct dp_output *output = seat->ptr_output;
|
struct dp_output *output = seat->ptr_output;
|
||||||
seat->ptr_x = wl_fixed_to_double(x);
|
seat->ptr_x = wl_fixed_to_double(x);
|
||||||
seat->ptr_y = wl_fixed_to_double(y);
|
seat->ptr_y = wl_fixed_to_double(y);
|
||||||
dp_select_notify_pointer_position(&seat->state->selection, output, seat->ptr_x, seat->ptr_y);
|
dp_select_notify_pointer_position(&seat->state->selection, output, seat->ptr_x, seat->ptr_y);
|
||||||
update_cursor(seat, serial);
|
update_cursor(seat);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
|
static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
|
||||||
@ -141,22 +157,24 @@ static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, uint
|
|||||||
struct dp_seat *seat = data;
|
struct dp_seat *seat = data;
|
||||||
seat->ptr_output = wl_surface_get_user_data(surface);
|
seat->ptr_output = wl_surface_get_user_data(surface);
|
||||||
assert(seat->ptr_output != NULL);
|
assert(seat->ptr_output != NULL);
|
||||||
process_position(seat, sx, sy, serial);
|
seat->pointer_serial = serial;
|
||||||
|
process_position(seat, sx, sy);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pointer_handle_leave(
|
static void pointer_handle_leave(
|
||||||
void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) {
|
void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) {
|
||||||
struct dp_seat *seat = data;
|
struct dp_seat *seat = data;
|
||||||
seat->ptr_output = NULL;
|
seat->ptr_output = NULL;
|
||||||
|
seat->pointer_serial = serial;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pointer_handle_motion(
|
static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time_msec,
|
||||||
void *data, struct wl_pointer *wl_pointer, uint32_t serial, wl_fixed_t sx, wl_fixed_t sy) {
|
wl_fixed_t sx, wl_fixed_t sy) {
|
||||||
struct dp_seat *seat = data;
|
struct dp_seat *seat = data;
|
||||||
if (seat->ptr_output == NULL) {
|
if (seat->ptr_output == NULL) {
|
||||||
return; // Shouldn't happen
|
return; // Shouldn't happen
|
||||||
}
|
}
|
||||||
process_position(seat, sx, sy, serial);
|
process_position(seat, sx, sy);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
|
static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
|
||||||
@ -164,9 +182,12 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uin
|
|||||||
struct dp_seat *seat = data;
|
struct dp_seat *seat = data;
|
||||||
struct dp_state *state = seat->state;
|
struct dp_state *state = seat->state;
|
||||||
|
|
||||||
|
seat->pointer_serial = serial;
|
||||||
|
|
||||||
struct dp_selection *selection = &state->selection;
|
struct dp_selection *selection = &state->selection;
|
||||||
if (button_state != WL_POINTER_BUTTON_STATE_PRESSED) {
|
if (button_state != WL_POINTER_BUTTON_STATE_PRESSED) {
|
||||||
if (selection->width > 0 && selection->height > 0 && state->config.quick_select) {
|
if (selection->action == DP_SELECTION_ACTION_RESIZING && selection->width > 0 &&
|
||||||
|
selection->height > 0 && state->config.quick_select) {
|
||||||
state->status = DP_STATUS_SAVED;
|
state->status = DP_STATUS_SAVED;
|
||||||
}
|
}
|
||||||
dp_select_stop_interactive(selection);
|
dp_select_stop_interactive(selection);
|
||||||
@ -180,16 +201,16 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uin
|
|||||||
switch (button) {
|
switch (button) {
|
||||||
case BTN_LEFT:
|
case BTN_LEFT:
|
||||||
case BTN_RIGHT:
|
case BTN_RIGHT:
|
||||||
dp_select_start_interactive(
|
dp_select_start_interactive(selection, seat->ptr_output, seat->ptr_x, seat->ptr_y,
|
||||||
selection, seat->ptr_output, seat->ptr_x, seat->ptr_y, button == BTN_LEFT);
|
state->allow_selection_editing && button == BTN_LEFT);
|
||||||
update_cursor(seat, serial);
|
update_cursor(seat);
|
||||||
break;
|
break;
|
||||||
case BTN_MIDDLE:
|
case BTN_MIDDLE:
|
||||||
dp_select_whole(selection, seat->ptr_output);
|
dp_select_toggle_whole(selection, seat->ptr_output);
|
||||||
|
|
||||||
// dp_select_whole() doesn't invalidate the interactive state, so do it manually
|
// Update the interaction state manually
|
||||||
dp_select_notify_pointer_position(selection, seat->ptr_output, seat->ptr_x, seat->ptr_y);
|
dp_select_notify_pointer_position(selection, seat->ptr_output, seat->ptr_x, seat->ptr_y);
|
||||||
update_cursor(seat, serial);
|
update_cursor(seat);
|
||||||
|
|
||||||
if (state->config.quick_select) {
|
if (state->config.quick_select) {
|
||||||
state->status = DP_STATUS_SAVED;
|
state->status = DP_STATUS_SAVED;
|
||||||
@ -220,9 +241,12 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, uint32
|
|||||||
if ((caps & WL_SEAT_CAPABILITY_POINTER) != 0 && seat->pointer == NULL) {
|
if ((caps & WL_SEAT_CAPABILITY_POINTER) != 0 && seat->pointer == NULL) {
|
||||||
seat->pointer = wl_seat_get_pointer(wl_seat);
|
seat->pointer = wl_seat_get_pointer(wl_seat);
|
||||||
wl_pointer_add_listener(seat->pointer, &pointer_listener, seat);
|
wl_pointer_add_listener(seat->pointer, &pointer_listener, seat);
|
||||||
|
|
||||||
|
if (seat->state->cursor_shape_manager != NULL) {
|
||||||
seat->cursor_shape_device = wp_cursor_shape_manager_v1_get_pointer(
|
seat->cursor_shape_device = wp_cursor_shape_manager_v1_get_pointer(
|
||||||
seat->state->cursor_shape_manager, seat->pointer);
|
seat->state->cursor_shape_manager, seat->pointer);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name) {
|
static void seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name) {
|
||||||
@ -256,6 +280,8 @@ void dp_seat_destroy(struct dp_seat *seat) {
|
|||||||
}
|
}
|
||||||
if (seat->pointer != NULL) {
|
if (seat->pointer != NULL) {
|
||||||
wl_pointer_release(seat->pointer);
|
wl_pointer_release(seat->pointer);
|
||||||
|
}
|
||||||
|
if (seat->cursor_shape_device != NULL) {
|
||||||
wp_cursor_shape_device_v1_destroy(seat->cursor_shape_device);
|
wp_cursor_shape_device_v1_destroy(seat->cursor_shape_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
48
src/select.c
48
src/select.c
@ -20,6 +20,24 @@ static void set_selected_output(struct dp_selection *selection, struct dp_output
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool has_last_partial(struct dp_selection *selection) {
|
||||||
|
return selection->last_partial.output != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void reset_last_partial(struct dp_selection *selection) {
|
||||||
|
selection->last_partial.output = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool has_partial(struct dp_selection *selection) {
|
||||||
|
struct dp_output *output = selection->output;
|
||||||
|
if (output == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return selection->x != 0 || selection->y != 0 || selection->width != output->effective_width ||
|
||||||
|
selection->height != output->effective_height;
|
||||||
|
}
|
||||||
|
|
||||||
static void update_action(
|
static void update_action(
|
||||||
struct dp_selection *selection, struct dp_output *output, double x, double y) {
|
struct dp_selection *selection, struct dp_output *output, double x, double y) {
|
||||||
if (output == selection->output) {
|
if (output == selection->output) {
|
||||||
@ -161,6 +179,8 @@ void dp_select_start_interactive(struct dp_selection *selection, struct dp_outpu
|
|||||||
update_action(selection, output, x, y);
|
update_action(selection, output, x, y);
|
||||||
selection->action_active = true;
|
selection->action_active = true;
|
||||||
|
|
||||||
|
reset_last_partial(selection);
|
||||||
|
|
||||||
if (modify_existing) {
|
if (modify_existing) {
|
||||||
switch (selection->action) {
|
switch (selection->action) {
|
||||||
case DP_SELECTION_ACTION_NONE:
|
case DP_SELECTION_ACTION_NONE:
|
||||||
@ -187,6 +207,8 @@ void dp_select_start_interactive(struct dp_selection *selection, struct dp_outpu
|
|||||||
selection->resize_edges = DP_EDGE_BOTTOM | DP_EDGE_RIGHT;
|
selection->resize_edges = DP_EDGE_BOTTOM | DP_EDGE_RIGHT;
|
||||||
|
|
||||||
init_resize(selection, x, y);
|
init_resize(selection, x, y);
|
||||||
|
|
||||||
|
dp_output_redraw(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dp_select_stop_interactive(struct dp_selection *selection) {
|
void dp_select_stop_interactive(struct dp_selection *selection) {
|
||||||
@ -211,13 +233,35 @@ void dp_select_notify_pointer_position(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void dp_select_whole(struct dp_selection *selection, struct dp_output *output) {
|
void dp_select_toggle_whole(struct dp_selection *selection, struct dp_output *output) {
|
||||||
|
if (selection->output == output && has_last_partial(selection)) {
|
||||||
|
// Toggle the selection back to the last partial one
|
||||||
|
set_selected_output(selection, selection->last_partial.output);
|
||||||
|
|
||||||
|
selection->x = selection->last_partial.x;
|
||||||
|
selection->y = selection->last_partial.y;
|
||||||
|
selection->width = selection->last_partial.width;
|
||||||
|
selection->height = selection->last_partial.height;
|
||||||
|
|
||||||
|
reset_last_partial(selection);
|
||||||
|
} else {
|
||||||
|
// Don't save another whole output selection as partial
|
||||||
|
if (has_partial(selection) && !has_last_partial(selection)) {
|
||||||
|
selection->last_partial.output = selection->output;
|
||||||
|
|
||||||
|
selection->last_partial.x = selection->x;
|
||||||
|
selection->last_partial.y = selection->y;
|
||||||
|
selection->last_partial.width = selection->width;
|
||||||
|
selection->last_partial.height = selection->height;
|
||||||
|
}
|
||||||
|
|
||||||
set_selected_output(selection, output);
|
set_selected_output(selection, output);
|
||||||
|
|
||||||
selection->x = 0;
|
selection->x = 0;
|
||||||
selection->y = 0;
|
selection->y = 0;
|
||||||
selection->width = output->effective_width;
|
selection->width = output->effective_width;
|
||||||
selection->height = output->effective_height;
|
selection->height = output->effective_height;
|
||||||
|
}
|
||||||
|
|
||||||
dp_output_redraw(output);
|
dp_output_redraw(selection->output);
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,10 @@ void dp_log_fatal(const char *fmt, ...) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void *dp_zalloc(size_t size) {
|
void *dp_zalloc(size_t size) {
|
||||||
|
if (size == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void *ptr = calloc(1, size);
|
void *ptr = calloc(1, size);
|
||||||
if (ptr == NULL) {
|
if (ptr == NULL) {
|
||||||
dp_log_fatal("Failed to allocate %zu bytes", size);
|
dp_log_fatal("Failed to allocate %zu bytes", size);
|
||||||
|
Reference in New Issue
Block a user