diff --git a/resources/head.ui b/resources/head.ui
index 9329acd..7cba682 100644
--- a/resources/head.ui
+++ b/resources/head.ui
@@ -7,23 +7,6 @@
1
10
-
16383
1
@@ -194,6 +177,7 @@
8
+ refresh
True
True
9
@@ -245,7 +229,6 @@
True
True
True
- transforms
@@ -323,6 +306,7 @@
+ width
True
True
4
@@ -351,6 +335,7 @@
+ height
True
True
4
@@ -371,9 +356,7 @@
True
True
Select Mode Preset
- 8
8
- modes
True
@@ -407,75 +390,4 @@
-
- False
- rotate_button
-
-
- True
- False
- 10
- 10
- 10
- 10
- vertical
-
-
- True
- True
- True
- transform.rotate_0
- Don't Rotate
-
-
- False
- True
- 0
-
-
-
-
- True
- True
- True
- transform.rotate_90
- Rotate 90°
-
-
- False
- True
- 1
-
-
-
-
- True
- True
- True
- transform.rotate_180
- Rotate 180°
-
-
- False
- True
- 2
-
-
-
-
- True
- True
- True
- transform.rotate_270
- Rotate 270°
-
-
- False
- True
- 3
-
-
-
-
-
diff --git a/resources/meson.build b/resources/meson.build
index a72de41..045460b 100644
--- a/resources/meson.build
+++ b/resources/meson.build
@@ -3,9 +3,9 @@
gnome = import('gnome')
resources = gnome.compile_resources(
- 'waydisplay-resources', 'resources.xml',
+ 'wdisplays-resources', 'resources.xml',
source_dir : '.',
- c_name : 'waydisplay_resources')
+ c_name : 'wdisplays_resources')
scour = find_program('scour', required: false)
diff --git a/resources/wdisplays.ui b/resources/wdisplays.ui
index e43996f..d16130d 100644
--- a/resources/wdisplays.ui
+++ b/resources/wdisplays.ui
@@ -11,62 +11,6 @@
1
10
-
- False
-
-
- True
- False
- 10
- 10
- 10
- 10
- vertical
-
-
- True
- True
- True
- app.auto-apply
- _Automatically Apply Changes
-
-
- False
- True
- 0
-
-
-
-
- True
- True
- True
- app.capture-screens
- Show Screen Contents
-
-
- False
- True
- 1
-
-
-
-
- True
- True
- True
- app.show-overlay
- Overlay Screen Names
-
-
- False
- True
- 2
-
-
-
-
-
False
wdisplays
@@ -171,8 +115,6 @@
True
False
center
- 8
- 8
8
8
8
@@ -248,14 +190,14 @@
True
False
- expand
True
True
True
Zoom Out
-
+ app.zoom-out
+
True
@@ -263,7 +205,6 @@
zoom-out-symbolic
-
True
@@ -278,7 +219,7 @@
True
True
Zoom Reset
-
+ app.zoom-reset
@@ -294,7 +235,8 @@
True
True
Zoom In
-
+ app.zoom-in
+
True
@@ -302,7 +244,6 @@
zoom-in-symbolic
-
True
@@ -311,6 +252,9 @@
True
+
@@ -318,7 +262,6 @@
True
True
True
- main_menu
True
@@ -355,7 +298,7 @@
True
True
True
-
+ app.apply-changes
@@ -371,7 +314,7 @@
True
True
True
-
+ app.cancel-changes
1
diff --git a/src/main.c b/src/main.c
index be31af6..82d449f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -20,22 +20,18 @@ __attribute__((noreturn)) void wd_fatal_error(int status, const char *message) {
#define MAX_ZOOM 1000.
#define CANVAS_MARGIN 40
+static const char *HEAD_PREFIX = "head";
static const char *MODE_PREFIX = "mode";
-static const char *TRANSFORM_PREFIX = "transform";
+static const char *ROTATE_PREFIX = "rotate";
static const char *APP_PREFIX = "app";
-#define NUM_ROTATIONS 4
-static const char *ROTATE_IDS[NUM_ROTATIONS] = {
- "rotate_0", "rotate_90", "rotate_180", "rotate_270"
-};
-
-static int get_rotate_index(enum wl_output_transform transform) {
+static int32_t get_rotate_value(enum wl_output_transform transform) {
if (transform == WL_OUTPUT_TRANSFORM_90 || transform == WL_OUTPUT_TRANSFORM_FLIPPED_90) {
- return 1;
+ return 90;
} else if (transform == WL_OUTPUT_TRANSFORM_180 || transform == WL_OUTPUT_TRANSFORM_FLIPPED_180) {
- return 2;
+ return 180;
} else if (transform == WL_OUTPUT_TRANSFORM_270 || transform == WL_OUTPUT_TRANSFORM_FLIPPED_270) {
- return 3;
+ return 270;
}
return 0;
}
@@ -45,6 +41,7 @@ static bool has_changes(const struct wd_state *state) {
for (GList *form_iter = forms; form_iter != NULL; form_iter = form_iter->next) {
GtkBuilder *builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form_iter->data), "builder"));
const struct wd_head *head = g_object_get_data(G_OBJECT(form_iter->data), "head");
+ GAction *rotate_action = G_ACTION(g_object_get_data(G_OBJECT(form_iter->data), "rotate_action"));
if (head->enabled != gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "enabled")))) {
return TRUE;
}
@@ -71,16 +68,8 @@ static bool has_changes(const struct wd_state *state) {
if (r / 1000. != gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "refresh")))) {
return TRUE;
}
- for (int i = 0; i < NUM_ROTATIONS; i++) {
- GtkWidget *rotate = GTK_WIDGET(gtk_builder_get_object(builder, ROTATE_IDS[i]));
- gboolean selected;
- g_object_get(rotate, "active", &selected, NULL);
- if (selected) {
- if (i != get_rotate_index(head->transform)) {
- return TRUE;
- }
- break;
- }
+ if (g_variant_get_int32(g_action_get_state(rotate_action)) != get_rotate_value(head->transform)) {
+ return TRUE;
}
bool flipped = head->transform == WL_OUTPUT_TRANSFORM_FLIPPED
|| head->transform == WL_OUTPUT_TRANSFORM_FLIPPED_90
@@ -103,20 +92,13 @@ void fill_output_from_form(struct wd_head_config *output, GtkWidget *form) {
output->width = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "width")));
output->height = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "height")));
output->refresh = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "refresh"))) * 1000.;
+ int32_t rotate = g_variant_get_int32(g_action_get_state(G_ACTION(g_object_get_data(G_OBJECT(form), "rotate_action"))));
gboolean flipped = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "flipped")));
- for (int i = 0; i < NUM_ROTATIONS; i++) {
- GtkWidget *rotate = GTK_WIDGET(gtk_builder_get_object(builder, ROTATE_IDS[i]));
- gboolean selected;
- g_object_get(rotate, "active", &selected, NULL);
- if (selected) {
- switch (i) {
- case 0: output->transform = flipped ? WL_OUTPUT_TRANSFORM_FLIPPED : WL_OUTPUT_TRANSFORM_NORMAL; break;
- case 1: output->transform = flipped ? WL_OUTPUT_TRANSFORM_FLIPPED_90 : WL_OUTPUT_TRANSFORM_90; break;
- case 2: output->transform = flipped ? WL_OUTPUT_TRANSFORM_FLIPPED_180 : WL_OUTPUT_TRANSFORM_180; break;
- case 3: output->transform = flipped ? WL_OUTPUT_TRANSFORM_FLIPPED_270 : WL_OUTPUT_TRANSFORM_270; break;
- }
- break;
- }
+ switch (rotate) {
+ case 0: output->transform = flipped ? WL_OUTPUT_TRANSFORM_FLIPPED : WL_OUTPUT_TRANSFORM_NORMAL; break;
+ case 90: output->transform = flipped ? WL_OUTPUT_TRANSFORM_FLIPPED_90 : WL_OUTPUT_TRANSFORM_90; break;
+ case 180: output->transform = flipped ? WL_OUTPUT_TRANSFORM_FLIPPED_180 : WL_OUTPUT_TRANSFORM_180; break;
+ case 270: output->transform = flipped ? WL_OUTPUT_TRANSFORM_FLIPPED_270 : WL_OUTPUT_TRANSFORM_270; break;
}
}
@@ -294,15 +276,13 @@ static inline void flip_anim(uint64_t *timer, uint64_t tick) {
}
}
-static void update_hovered(struct wd_state *state) {
- GdkDisplay *display = gdk_display_get_default();
- GdkWindow *window = gtk_widget_get_window(state->canvas);
+static void update_hovered(struct wd_state *state,
+ gdouble mouse_x, gdouble mouse_y) {
if (!gtk_widget_get_realized(state->canvas)) {
return;
}
GdkFrameClock *clock = gtk_widget_get_frame_clock(state->canvas);
uint64_t tick = gdk_frame_clock_get_frame_time(clock);
- g_autoptr(GList) seats = gdk_display_list_seats(display);
bool any_hovered = FALSE;
struct wd_render_head_data *render;
wl_list_for_each(render, &state->render.heads, link) {
@@ -315,18 +295,10 @@ static void update_hovered(struct wd_state *state) {
render->hovered = TRUE;
any_hovered = TRUE;
} else if (state->clicked == NULL) {
- for (GList *iter = seats; iter != NULL; iter = iter->next) {
- double mouse_x;
- double mouse_y;
-
- GdkDevice *pointer = gdk_seat_get_pointer(GDK_SEAT(iter->data));
- gdk_window_get_device_position_double(window, pointer, &mouse_x, &mouse_y, NULL);
- if (mouse_x >= render->x1 && mouse_x < render->x2 &&
- mouse_y >= render->y1 && mouse_y < render->y2) {
- render->hovered = TRUE;
- any_hovered = TRUE;
- break;
- }
+ if (mouse_x >= render->x1 && mouse_x < render->x2 &&
+ mouse_y >= render->y1 && mouse_y < render->y2) {
+ render->hovered = TRUE;
+ any_hovered = TRUE;
}
}
if (init_hovered != render->hovered) {
@@ -348,18 +320,8 @@ static inline void color_to_float_array(GtkStyleContext *ctx,
}
static unsigned form_get_rotation(GtkWidget *form) {
- GtkBuilder *builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form), "builder"));
- unsigned rot;
- for (rot = 0; rot < NUM_ROTATIONS; rot++) {
- GtkWidget *rotate = GTK_WIDGET(gtk_builder_get_object(builder,
- ROTATE_IDS[rot]));
- gboolean selected;
- g_object_get(rotate, "active", &selected, NULL);
- if (selected) {
- return rot;
- }
- }
- return -1;
+ int32_t rotate = g_variant_get_int32(g_action_get_state(G_ACTION(g_object_get_data(G_OBJECT(form), "rotate_action"))));
+ return rotate / 90;
}
#define SWAP(_type, _a, _b) { _type _tmp = (_a); (_a) = (_b); (_b) = _tmp; }
@@ -443,67 +405,81 @@ static void update_sensitivity(GtkWidget *form) {
}
}
-static void select_rotate_option(GtkWidget *form, GtkWidget *model_button) {
- GtkBuilder *builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form), "builder"));
+static void rotate_selected(GSimpleAction *action, GVariant *param, gpointer data) {
+ const struct wd_head *head = g_object_get_data(G_OBJECT(data), "head");
+ GtkBuilder *builder = GTK_BUILDER(g_object_get_data(data, "builder"));
GtkWidget *rotate_button = GTK_WIDGET(gtk_builder_get_object(builder, "rotate_button"));
- for (int i = 0; i < NUM_ROTATIONS; i++) {
- GtkWidget *rotate = GTK_WIDGET(gtk_builder_get_object(builder, ROTATE_IDS[i]));
- gboolean selected = model_button == rotate;
- g_object_set(rotate, "active", selected, NULL);
- if (selected) {
- g_autofree gchar *rotate_text = NULL;
- g_object_get(rotate, "text", &rotate_text, NULL);
- gtk_button_set_label(GTK_BUTTON(rotate_button), rotate_text);
+ GMenuModel *menu = gtk_menu_button_get_menu_model(GTK_MENU_BUTTON(rotate_button));
+ int items = g_menu_model_get_n_items(menu);
+ for (int i = 0; i < items; i++) {
+ g_autoptr(GVariant) target = g_menu_model_get_item_attribute_value(menu, i, G_MENU_ATTRIBUTE_TARGET, NULL);
+ g_autoptr(GVariant) label = g_menu_model_get_item_attribute_value(menu, i, G_MENU_ATTRIBUTE_LABEL, NULL);
+ if (g_variant_get_int32(target) == g_variant_get_int32(param)) {
+ gtk_button_set_label(GTK_BUTTON(rotate_button), g_variant_get_string(label, NULL));
+ break;
}
}
-}
-
-static void rotate_selected(GSimpleAction *action, GVariant *param, gpointer data) {
- select_rotate_option(GTK_WIDGET(data), g_object_get_data(G_OBJECT(action), "widget"));
- const struct wd_head *head = g_object_get_data(G_OBJECT(data), "head");
+ g_simple_action_set_state(action, param);
update_ui(head->state);
}
-static void select_mode_option(GtkWidget *form, int32_t w, int32_t h, int32_t r) {
- GtkBuilder *builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form), "builder"));
- GtkWidget *mode_box = GTK_WIDGET(gtk_builder_get_object(builder, "mode_box"));
- g_autoptr(GList) children = gtk_container_get_children(GTK_CONTAINER(mode_box));
- for (GList *child = children; child != NULL; child = child->next) {
- const struct wd_mode *mode = g_object_get_data(G_OBJECT(child->data), "mode");
- g_object_set(child->data, "active", w == mode->width && h == mode->height && r == mode->refresh, NULL);
- }
+static GVariant *create_mode_variant(int32_t w, int32_t h, int32_t r) {
+ GVariant * const children[] = {
+ g_variant_new_int32(w),
+ g_variant_new_int32(h),
+ g_variant_new_int32(r),
+ };
+ return g_variant_new_tuple(children, G_N_ELEMENTS(children));
}
-static void update_mode_entries(GtkWidget *form, int32_t w, int32_t h, int32_t r) {
- GtkBuilder *builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form), "builder"));
- GtkWidget *width = GTK_WIDGET(gtk_builder_get_object(builder, "width"));
- GtkWidget *height = GTK_WIDGET(gtk_builder_get_object(builder, "height"));
- GtkWidget *refresh = GTK_WIDGET(gtk_builder_get_object(builder, "refresh"));
+struct vid_mode {
+ int32_t width;
+ int32_t height;
+ int32_t refresh;
+};
- gtk_spin_button_set_value(GTK_SPIN_BUTTON(width), w);
- gtk_spin_button_set_value(GTK_SPIN_BUTTON(height), h);
- gtk_spin_button_set_value(GTK_SPIN_BUTTON(refresh), r / 1000.);
+static void unpack_mode_variant(GVariant *value, struct vid_mode *mode) {
+ g_autoptr(GVariant) width = g_variant_get_child_value(value, 0);
+ mode->width = g_variant_get_int32(width);
+ g_autoptr(GVariant) height = g_variant_get_child_value(value, 1);
+ mode->height = g_variant_get_int32(height);
+ g_autoptr(GVariant) refresh = g_variant_get_child_value(value, 2);
+ mode->refresh = g_variant_get_int32(refresh);
+}
+
+static void mode_spin_changed(GtkSpinButton *spin_button, gpointer data) {
+ GtkWidget *form = data;
+ const struct wd_head *head = g_object_get_data(G_OBJECT(form), "head");
+ struct vid_mode mode;
+ GAction *mode_action = G_ACTION(g_object_get_data(G_OBJECT(form), "mode_action"));
+ GVariant *value = g_action_get_state(mode_action);
+ unpack_mode_variant(value, &mode);
+ if (strcmp(gtk_widget_get_name(GTK_WIDGET(spin_button)), "width") == 0) {
+ mode.width = gtk_spin_button_get_value(spin_button);
+ } else if (strcmp(gtk_widget_get_name(GTK_WIDGET(spin_button)), "height") == 0) {
+ mode.height = gtk_spin_button_get_value(spin_button);
+ } else if (strcmp(gtk_widget_get_name(GTK_WIDGET(spin_button)), "refresh") == 0) {
+ mode.refresh = gtk_spin_button_get_value(spin_button) * 1000.;
+ }
+ g_action_activate(mode_action, create_mode_variant(mode.width, mode.height, mode.refresh));
+ update_ui(head->state);
}
static void mode_selected(GSimpleAction *action, GVariant *param, gpointer data) {
GtkWidget *form = data;
const struct wd_head *head = g_object_get_data(G_OBJECT(form), "head");
- const struct wd_mode *mode = g_object_get_data(G_OBJECT(action), "mode");
+ GtkBuilder *builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form), "builder"));
+ struct vid_mode mode;
+ unpack_mode_variant(param, &mode);
- update_mode_entries(form, mode->width, mode->height, mode->refresh);
- select_mode_option(form, mode->width, mode->height, mode->refresh);
+ g_simple_action_set_state(action, param);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "width")), mode.width);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "height")), mode.height);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "refresh")), mode.refresh / 1000.);
update_ui(head->state);
}
// END FORM CALLBACKS
-static void clear_menu(GtkWidget *box, GActionMap *action_map) {
- g_autoptr(GList) children = gtk_container_get_children(GTK_CONTAINER(box));
- for (GList *child = children; child != NULL; child = child->next) {
- g_action_map_remove_action(action_map, strchr(gtk_actionable_get_action_name(GTK_ACTIONABLE(child->data)), '.') + 1);
- gtk_container_remove(GTK_CONTAINER(box), GTK_WIDGET(child->data));
- }
-}
-
static void update_head_form(GtkWidget *form, unsigned int fields) {
GtkBuilder *builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form), "builder"));
GtkWidget *description = GTK_WIDGET(gtk_builder_get_object(builder, "description"));
@@ -512,9 +488,11 @@ static void update_head_form(GtkWidget *form, unsigned int fields) {
GtkWidget *scale = GTK_WIDGET(gtk_builder_get_object(builder, "scale"));
GtkWidget *pos_x = GTK_WIDGET(gtk_builder_get_object(builder, "pos_x"));
GtkWidget *pos_y = GTK_WIDGET(gtk_builder_get_object(builder, "pos_y"));
- GtkWidget *mode_box = GTK_WIDGET(gtk_builder_get_object(builder, "mode_box"));
+ GtkWidget *mode_button = GTK_WIDGET(gtk_builder_get_object(builder, "mode_button"));
GtkWidget *flipped = GTK_WIDGET(gtk_builder_get_object(builder, "flipped"));
const struct wd_head *head = g_object_get_data(G_OBJECT(form), "head");
+ GAction *mode_action = G_ACTION(g_object_get_data(G_OBJECT(form), "mode_action"));
+ GAction *rotate_action = G_ACTION(g_object_get_data(G_OBJECT(form), "rotate_action"));
if (fields & WD_FIELD_NAME) {
gtk_container_child_set(GTK_CONTAINER(head->state->stack), form, "title", head->name, NULL);
@@ -538,27 +516,17 @@ static void update_head_form(GtkWidget *form, unsigned int fields) {
}
if (fields & WD_FIELD_MODE) {
- GActionMap *mode_actions = G_ACTION_MAP(g_object_get_data(G_OBJECT(form), "mode-group"));
- clear_menu(mode_box, mode_actions);
+ GMenu *mode_menu = g_menu_new();
struct wd_mode *mode;
+ g_autofree gchar *action = g_strdup_printf("%s.%s", HEAD_PREFIX, MODE_PREFIX);
wl_list_for_each(mode, &head->modes, link) {
g_autofree gchar *name = g_strdup_printf("%d×%d@%0.3fHz", mode->width, mode->height, mode->refresh / 1000.);
- GSimpleAction *action = g_simple_action_new(name, NULL);
- g_action_map_add_action(G_ACTION_MAP(mode_actions), G_ACTION(action));
- g_signal_connect(action, "activate", G_CALLBACK(mode_selected), form);
- g_object_set_data(G_OBJECT(action), "mode", mode);
- g_object_unref(action);
-
- GtkWidget *button = gtk_model_button_new();
- g_autoptr(GString) prefixed_name = g_string_new(MODE_PREFIX);
- g_string_append(prefixed_name, ".");
- g_string_append(prefixed_name, name);
- gtk_actionable_set_action_name(GTK_ACTIONABLE(button), prefixed_name->str);
- g_object_set(button, "role", GTK_BUTTON_ROLE_RADIO, "text", name, NULL);
- gtk_box_pack_start(GTK_BOX(mode_box), button, FALSE, FALSE, 0);
- g_object_set_data(G_OBJECT(button), "mode", mode);
- gtk_widget_show_all(button);
+ GMenuItem *item = g_menu_item_new(name, action);
+ g_menu_item_set_attribute_value(item, G_MENU_ATTRIBUTE_TARGET,
+ create_mode_variant(mode->width, mode->height, mode->refresh));
+ g_menu_append_item(mode_menu, item);
}
+ gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(mode_button), G_MENU_MODEL(mode_menu));
// Mode entries
int w = head->custom_mode.width;
int h = head->custom_mode.height;
@@ -579,14 +547,12 @@ static void update_head_form(GtkWidget *form, unsigned int fields) {
}
}
- update_mode_entries(form, w, h, r);
- select_mode_option(form, w, h, r);
- gtk_widget_show_all(mode_box);
+ g_action_change_state(mode_action, create_mode_variant(w, h, r));
}
if (fields & WD_FIELD_TRANSFORM) {
- int active_rotate = get_rotate_index(head->transform);
- select_rotate_option(form, GTK_WIDGET(gtk_builder_get_object(builder, ROTATE_IDS[active_rotate])));
+ int active_rotate = get_rotate_value(head->transform);
+ g_action_change_state(rotate_action, g_variant_new_int32(active_rotate));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(flipped),
head->transform == WL_OUTPUT_TRANSFORM_FLIPPED
@@ -625,36 +591,48 @@ void wd_ui_reset_heads(struct wd_state *state) {
GtkWidget *mode_button = GTK_WIDGET(gtk_builder_get_object(builder, "mode_button"));
GtkWidget *rotate_button = GTK_WIDGET(gtk_builder_get_object(builder, "rotate_button"));
- GSimpleActionGroup *mode_actions = g_simple_action_group_new();
- gtk_widget_insert_action_group(mode_button, MODE_PREFIX, G_ACTION_GROUP(mode_actions));
- g_object_set_data(G_OBJECT(form), "mode-group", mode_actions);
- g_object_unref(mode_actions);
+ GSimpleActionGroup *head_actions = g_simple_action_group_new();
+ gtk_widget_insert_action_group(mode_button, HEAD_PREFIX, G_ACTION_GROUP(head_actions));
+ gtk_widget_insert_action_group(rotate_button, HEAD_PREFIX, G_ACTION_GROUP(head_actions));
- GSimpleActionGroup *transform_actions = g_simple_action_group_new();
- gtk_widget_insert_action_group(rotate_button, TRANSFORM_PREFIX, G_ACTION_GROUP(transform_actions));
- g_object_unref(transform_actions);
+ GMenu *rotate_menu = g_menu_new();
+ g_menu_append(rotate_menu, "Don't Rotate", "head.rotate(0)");
+ g_menu_append(rotate_menu, "Rotate 90°", "head.rotate(90)");
+ g_menu_append(rotate_menu, "Rotate 180°", "head.rotate(180)");
+ g_menu_append(rotate_menu, "Rotate 270°", "head.rotate(270)");
+ gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(rotate_button), G_MENU_MODEL(rotate_menu));
- for (int i = 0; i < NUM_ROTATIONS; i++) {
- GtkWidget *button = GTK_WIDGET(gtk_builder_get_object(builder, ROTATE_IDS[i]));
- g_object_set(button, "role", GTK_BUTTON_ROLE_RADIO, NULL);
- GSimpleAction *action = g_simple_action_new(ROTATE_IDS[i], NULL);
- g_action_map_add_action(G_ACTION_MAP(transform_actions), G_ACTION(action));
- g_signal_connect(action, "activate", G_CALLBACK(rotate_selected), form);
- g_object_set_data(G_OBJECT(action), "widget", button);
- g_object_unref(action);
- }
+ static const GVariantType * const mode_types[] = {
+ G_VARIANT_TYPE_INT32,
+ G_VARIANT_TYPE_INT32,
+ G_VARIANT_TYPE_INT32
+ };
+ GSimpleAction *action = g_simple_action_new_stateful("mode",
+ g_variant_type_new_tuple(mode_types, G_N_ELEMENTS(mode_types)),
+ create_mode_variant(0, 0, 0));
+ g_action_map_add_action(G_ACTION_MAP(head_actions), G_ACTION(action));
+ g_signal_connect(action, "change-state", G_CALLBACK(mode_selected), form);
+ g_object_set_data(G_OBJECT(form), "mode_action", action);
+ g_object_unref(action);
+
+ action = g_simple_action_new_stateful(ROTATE_PREFIX, G_VARIANT_TYPE_INT32,
+ g_variant_new_int32(0));
+ g_action_map_add_action(G_ACTION_MAP(head_actions), G_ACTION(action));
+ g_signal_connect(action, "change-state", G_CALLBACK(rotate_selected), form);
+ g_object_set_data(G_OBJECT(form), "rotate_action", action);
+ g_object_unref(action);
+
+ g_object_unref(head_actions);
update_head_form(form, WD_FIELDS_ALL);
- gtk_widget_show_all(form);
-
g_signal_connect_swapped(gtk_builder_get_object(builder, "enabled"), "toggled", G_CALLBACK(update_sensitivity), form);
+ g_signal_connect(gtk_builder_get_object(builder, "width"), "value-changed", G_CALLBACK(mode_spin_changed), form);
+ g_signal_connect(gtk_builder_get_object(builder, "height"), "value-changed", G_CALLBACK(mode_spin_changed), form);
+ g_signal_connect(gtk_builder_get_object(builder, "refresh"), "value-changed", G_CALLBACK(mode_spin_changed), form);
g_signal_connect_swapped(gtk_builder_get_object(builder, "enabled"), "toggled", G_CALLBACK(update_ui), state);
g_signal_connect_swapped(gtk_builder_get_object(builder, "scale"), "value-changed", G_CALLBACK(update_ui), state);
g_signal_connect_swapped(gtk_builder_get_object(builder, "pos_x"), "value-changed", G_CALLBACK(update_ui), state);
g_signal_connect_swapped(gtk_builder_get_object(builder, "pos_y"), "value-changed", G_CALLBACK(update_ui), state);
- g_signal_connect_swapped(gtk_builder_get_object(builder, "width"), "value-changed", G_CALLBACK(update_ui), state);
- g_signal_connect_swapped(gtk_builder_get_object(builder, "height"), "value-changed", G_CALLBACK(update_ui), state);
- g_signal_connect_swapped(gtk_builder_get_object(builder, "refresh"), "value-changed", G_CALLBACK(update_ui), state);
g_signal_connect_swapped(gtk_builder_get_object(builder, "flipped"), "toggled", G_CALLBACK(update_ui), state);
} else {
@@ -786,15 +764,18 @@ static void zoom_to(struct wd_state *state, double zoom) {
update_zoom(state);
}
-static void zoom_out(struct wd_state *state) {
+static void zoom_out(GSimpleAction *action, GVariant *param, gpointer data) {
+ struct wd_state *state = data;
zoom_to(state, state->zoom * 0.75);
}
-static void zoom_reset(struct wd_state *state) {
+static void zoom_reset(GSimpleAction *action, GVariant *param, gpointer data) {
+ struct wd_state *state = data;
zoom_to(state, DEFAULT_ZOOM);
}
-static void zoom_in(struct wd_state *state) {
+static void zoom_in(GSimpleAction *action, GVariant *param, gpointer data) {
+ struct wd_state *state = data;
zoom_to(state, state->zoom / 0.75);
}
@@ -923,246 +904,232 @@ static void set_clicked_head(struct wd_state *state,
state->clicked = clicked;
}
-static gboolean canvas_click(GtkWidget *widget, GdkEvent *event,
- gpointer data) {
+static void canvas_drag1_begin(GtkGestureDrag *drag,
+ gdouble mouse_x, gdouble mouse_y, gpointer data) {
struct wd_state *state = data;
- if (event->button.type == GDK_BUTTON_PRESS) {
- if (event->button.button == 1) {
- struct wd_render_head_data *render;
- state->clicked = NULL;
- wl_list_for_each(render, &state->render.heads, link) {
- double mouse_x = event->button.x;
- double mouse_y = event->button.y;
- if (mouse_x >= render->x1 && mouse_x < render->x2 &&
- mouse_y >= render->y1 && mouse_y < render->y2) {
- set_clicked_head(state, render);
- state->click_offset.x = event->button.x - render->x1;
- state->click_offset.y = event->button.y - render->y1;
- break;
- }
- }
- if (state->clicked != NULL) {
- wl_list_remove(&state->clicked->link);
- wl_list_insert(&state->render.heads, &state->clicked->link);
- struct wd_render_head_data *render;
- wl_list_for_each(render, &state->render.heads, link) {
- render->updated_at = 0;
- render->preview = TRUE;
- }
- gtk_gl_area_queue_render(GTK_GL_AREA(state->canvas));
- g_autoptr(GList) forms = gtk_container_get_children(GTK_CONTAINER(state->stack));
- for (GList *form_iter = forms; form_iter != NULL; form_iter = form_iter->next) {
- const struct wd_head *other = g_object_get_data(G_OBJECT(form_iter->data), "head");
- if (state->clicked == other->render) {
- gtk_stack_set_visible_child(GTK_STACK(state->stack), form_iter->data);
- break;
- }
- }
- }
- } else if (event->button.button == 2) {
- state->panning = TRUE;
- state->pan_last.x = event->button.x;
- state->pan_last.y = event->button.y;
+ struct wd_render_head_data *render;
+ state->clicked = NULL;
+ wl_list_for_each(render, &state->render.heads, link) {
+ if (mouse_x >= render->x1 && mouse_x < render->x2 &&
+ mouse_y >= render->y1 && mouse_y < render->y2) {
+ set_clicked_head(state, render);
+ state->drag_start.x = mouse_x;
+ state->drag_start.y = mouse_y;
+ state->head_drag_start.x = (mouse_x - render->x1) / (render->x2 - render->x1);
+ state->head_drag_start.y = (mouse_y - render->y1) / (render->y2 - render->y1);
+ break;
}
}
- return TRUE;
-}
+ if (state->clicked != NULL) {
+ wl_list_remove(&state->clicked->link);
+ wl_list_insert(&state->render.heads, &state->clicked->link);
-static gboolean canvas_release(GtkWidget *widget, GdkEvent *event,
- gpointer data) {
- struct wd_state *state = data;
- if (event->button.button == 1) {
- set_clicked_head(state, NULL);
- }
- if (event->button.button == 2) {
- state->panning = FALSE;
- }
- update_cursor(state);
- return TRUE;
-}
-
-#define SNAP_DIST 6.
-
-static gboolean canvas_motion(GtkWidget *widget, GdkEvent *event,
- gpointer data) {
- struct wd_state *state = data;
- if (event->motion.state & GDK_BUTTON2_MASK) {
- GtkAdjustment *xadj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(state->scroller));
- GtkAdjustment *yadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(state->scroller));
- double delta_x = event->motion.x - state->pan_last.x;
- double delta_y = event->motion.y - state->pan_last.y;
- gtk_adjustment_set_value(xadj, gtk_adjustment_get_value(xadj) + delta_x);
- gtk_adjustment_set_value(yadj, gtk_adjustment_get_value(yadj) + delta_y);
- state->pan_last.x = event->motion.x;
- state->pan_last.y = event->motion.y;
- queue_canvas_draw(state);
- }
- if ((event->motion.state & GDK_BUTTON1_MASK) && state->clicked != NULL) {
- GtkWidget *form = NULL;
+ struct wd_render_head_data *render;
+ wl_list_for_each(render, &state->render.heads, link) {
+ render->updated_at = 0;
+ render->preview = TRUE;
+ }
+ gtk_gl_area_queue_render(GTK_GL_AREA(state->canvas));
g_autoptr(GList) forms = gtk_container_get_children(GTK_CONTAINER(state->stack));
for (GList *form_iter = forms; form_iter != NULL; form_iter = form_iter->next) {
const struct wd_head *other = g_object_get_data(G_OBJECT(form_iter->data), "head");
if (state->clicked == other->render) {
- form = form_iter->data;
+ gtk_stack_set_visible_child(GTK_STACK(state->stack), form_iter->data);
break;
}
}
- if (form != NULL) {
- GtkBuilder *builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form), "builder"));
- struct wd_point size = {
- .x = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "width"))),
- .y = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "height"))),
- };
- double scale = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "scale")));
- if (scale > 0.) {
- size.x /= scale;
- size.y /= scale;
- }
- unsigned rot = form_get_rotation(form);
- if (rot & 1) {
- SWAP(int, size.x, size.y);
- }
- struct wd_point tl = {
- .x = (event->motion.x - state->click_offset.x
- + state->render.x_origin + state->render.scroll_x) / state->zoom,
- .y = (event->motion.y - state->click_offset.y
- + state->render.y_origin + state->render.scroll_y) / state->zoom
- };
- const struct wd_point br = {
- .x = tl.x + size.x,
- .y = tl.y + size.y
- };
- struct wd_point new_pos = tl;
- float snap = SNAP_DIST / state->zoom;
-
- for (GList *form_iter = forms; form_iter != NULL; form_iter = form_iter->next) {
- const struct wd_head *other = g_object_get_data(G_OBJECT(form_iter->data), "head");
- if (other->render != state->clicked && !(event->motion.state & GDK_SHIFT_MASK)) {
- GtkBuilder *other_builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form_iter->data), "builder"));
- double x1 = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(other_builder, "pos_x")));
- double y1 = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(other_builder, "pos_y")));
- double w = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(other_builder, "width")));
- double h = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(other_builder, "height")));
- scale = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(other_builder, "scale")));
- if (scale > 0.) {
- w /= scale;
- h /= scale;
- }
- rot = form_get_rotation(GTK_WIDGET(form_iter->data));
- if (rot & 1) {
- SWAP(int, w, h);
- }
- double x2 = x1 + w;
- double y2 = y1 + h;
- if (fabs(br.x) <= snap)
- new_pos.x = -size.x;
- if (fabs(br.y) <= snap)
- new_pos.y = -size.y;
- if (fabs(br.x - x1) <= snap)
- new_pos.x = x1 - size.x;
- if (fabs(br.x - x2) <= snap)
- new_pos.x = x2 - size.x;
- if (fabs(br.y - y1) <= snap)
- new_pos.y = y1 - size.y;
- if (fabs(br.y - y2) <= snap)
- new_pos.y = y2 - size.y;
-
- if (fabs(tl.x) <= snap)
- new_pos.x = 0.;
- if (fabs(tl.y) <= snap)
- new_pos.y = 0.;
- if (fabs(tl.x - x1) <= snap)
- new_pos.x = x1;
- if (fabs(tl.x - x2) <= snap)
- new_pos.x = x2;
- if (fabs(tl.y - y1) <= snap)
- new_pos.y = y1;
- if (fabs(tl.y - y2) <= snap)
- new_pos.y = y2;
- }
- }
- GtkWidget *pos_x = GTK_WIDGET(gtk_builder_get_object(builder, "pos_x"));
- GtkWidget *pos_y = GTK_WIDGET(gtk_builder_get_object(builder, "pos_y"));
- gtk_spin_button_set_value(GTK_SPIN_BUTTON(pos_x), new_pos.x);
- gtk_spin_button_set_value(GTK_SPIN_BUTTON(pos_y), new_pos.y);
- }
}
- update_hovered(state);
- return TRUE;
}
-static gboolean canvas_enter(GtkWidget *widget, GdkEvent *event,
- gpointer data) {
+#define SNAP_DIST 6.
+
+static void canvas_drag1_update(GtkGestureDrag *drag,
+ gdouble delta_x, gdouble delta_y, gpointer data) {
struct wd_state *state = data;
- if (!(event->crossing.state & GDK_BUTTON1_MASK)) {
+
+ if (state->clicked == NULL)
+ return;
+ GtkWidget *form = NULL;
+ g_autoptr(GList) forms = gtk_container_get_children(GTK_CONTAINER(state->stack));
+ for (GList *form_iter = forms; form_iter != NULL; form_iter = form_iter->next) {
+ const struct wd_head *other = g_object_get_data(G_OBJECT(form_iter->data), "head");
+ if (state->clicked == other->render) {
+ form = form_iter->data;
+ break;
+ }
+ }
+ if (form != NULL) {
+ GtkBuilder *builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form), "builder"));
+ struct wd_point size = {
+ .x = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "width"))),
+ .y = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "height"))),
+ };
+ double scale = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "scale")));
+ if (scale > 0.) {
+ size.x /= scale;
+ size.y /= scale;
+ }
+ unsigned rot = form_get_rotation(form);
+ if (rot & 1) {
+ SWAP(int, size.x, size.y);
+ }
+ struct wd_point tl = { /* top left */
+ .x = (state->drag_start.x + delta_x - state->head_drag_start.x * size.x * state->zoom
+ + state->render.x_origin + state->render.scroll_x) / state->zoom,
+ .y = (state->drag_start.y + delta_y - state->head_drag_start.y * size.y * state->zoom
+ + state->render.y_origin + state->render.scroll_y) / state->zoom
+ };
+
+ const struct wd_point br = { /* bottom right */
+ .x = tl.x + size.x,
+ .y = tl.y + size.y
+ };
+ struct wd_point new_pos = tl;
+ float snap = SNAP_DIST / state->zoom;
+
+ GdkEvent *event = gtk_get_current_event();
+ GdkModifierType mod_state = event->motion.state;
+
+ /* snapping */
+ for (GList *form_iter = forms; form_iter != NULL; form_iter = form_iter->next) {
+ const struct wd_head *other = g_object_get_data(G_OBJECT(form_iter->data), "head");
+ if (other->render != state->clicked && !(mod_state & GDK_SHIFT_MASK)) {
+ GtkBuilder *other_builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form_iter->data), "builder"));
+ double x1 = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(other_builder, "pos_x")));
+ double y1 = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(other_builder, "pos_y")));
+ double w = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(other_builder, "width")));
+ double h = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(other_builder, "height")));
+ scale = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(other_builder, "scale")));
+ if (scale > 0.) {
+ w /= scale;
+ h /= scale;
+ }
+ rot = form_get_rotation(GTK_WIDGET(form_iter->data));
+ if (rot & 1) {
+ SWAP(int, w, h);
+ }
+ double x2 = x1 + w;
+ double y2 = y1 + h;
+ if (fabs(br.x) <= snap)
+ new_pos.x = -size.x;
+ if (fabs(br.y) <= snap)
+ new_pos.y = -size.y;
+ if (fabs(br.x - x1) <= snap)
+ new_pos.x = x1 - size.x;
+ if (fabs(br.x - x2) <= snap)
+ new_pos.x = x2 - size.x;
+ if (fabs(br.y - y1) <= snap)
+ new_pos.y = y1 - size.y;
+ if (fabs(br.y - y2) <= snap)
+ new_pos.y = y2 - size.y;
+
+ if (fabs(tl.x) <= snap)
+ new_pos.x = 0.;
+ if (fabs(tl.y) <= snap)
+ new_pos.y = 0.;
+ if (fabs(tl.x - x1) <= snap)
+ new_pos.x = x1;
+ if (fabs(tl.x - x2) <= snap)
+ new_pos.x = x2;
+ if (fabs(tl.y - y1) <= snap)
+ new_pos.y = y1;
+ if (fabs(tl.y - y2) <= snap)
+ new_pos.y = y2;
+ }
+ }
+ GtkWidget *pos_x = GTK_WIDGET(gtk_builder_get_object(builder, "pos_x"));
+ GtkWidget *pos_y = GTK_WIDGET(gtk_builder_get_object(builder, "pos_y"));
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(pos_x), new_pos.x);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(pos_y), new_pos.y);
+ }
+}
+
+static void canvas_drag1_end(GtkGestureDrag *drag,
+ gdouble mouse_x, gdouble mouse_y, gpointer data) {
+ struct wd_state *state = data;
+ set_clicked_head(state, NULL);
+ update_cursor(state);
+}
+
+static void canvas_drag2_begin(GtkGestureDrag *drag,
+ gdouble mouse_x, gdouble mouse_y, gpointer data) {
+ struct wd_state *state = data;
+ state->panning = TRUE;
+ GtkAdjustment *xadj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(state->scroller));
+ GtkAdjustment *yadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(state->scroller));
+ state->pan_start.x = gtk_adjustment_get_value(xadj);
+ state->pan_start.y = gtk_adjustment_get_value(yadj);
+}
+
+static void canvas_drag2_update(GtkGestureDrag *drag,
+ gdouble delta_x, gdouble delta_y, gpointer data) {
+ struct wd_state *state = data;
+ GtkAdjustment *xadj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(state->scroller));
+ GtkAdjustment *yadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(state->scroller));
+ gtk_adjustment_set_value(xadj, state->pan_start.x + delta_x);
+ gtk_adjustment_set_value(yadj, state->pan_start.y + delta_y);
+ queue_canvas_draw(state);
+}
+
+static void canvas_drag2_end(GtkGestureDrag *drag,
+ gdouble mouse_x, gdouble mouse_y, gpointer data) {
+ struct wd_state *state = data;
+ state->panning = FALSE;
+ update_cursor(state);
+}
+
+static void canvas_motion(GtkEventControllerMotion *controller,
+ gdouble mouse_x, gdouble mouse_y, gpointer data) {
+ struct wd_state *state = data;
+ update_hovered(state, mouse_x, mouse_y);
+}
+
+static void canvas_enter(GtkEventControllerMotion *controller,
+ gdouble x, gdouble y, gpointer data) {
+ struct wd_state *state = data;
+ GdkEvent *event = gtk_get_current_event();
+ GdkModifierType mod_state = event->crossing.state;
+
+ if (!(mod_state & GDK_BUTTON1_MASK)) {
set_clicked_head(state, NULL);
}
- if (!(event->crossing.state & GDK_BUTTON2_MASK)) {
+ if (!(mod_state & GDK_BUTTON2_MASK)) {
state->panning = FALSE;
}
update_cursor(state);
- return TRUE;
}
-static gboolean canvas_leave(GtkWidget *widget, GdkEvent *event,
- gpointer data) {
+static void canvas_leave(GtkEventControllerMotion *controller,
+ gpointer data) {
struct wd_state *state = data;
struct wd_render_head_data *render;
wl_list_for_each(render, &state->render.heads, link) {
render->hovered = FALSE;
}
update_tick_callback(state);
- return TRUE;
}
-static gboolean canvas_scroll(GtkWidget *widget, GdkEvent *event,
- gpointer data) {
+static gboolean canvas_scroll(GtkEventControllerScroll *controller,
+ gdouble delta_x, gdouble delta_y, gpointer data) {
struct wd_state *state = data;
- if (event->scroll.state & GDK_CONTROL_MASK) {
- switch (event->scroll.direction) {
- case GDK_SCROLL_UP:
- zoom_in(state);
- break;
- case GDK_SCROLL_DOWN:
- zoom_out(state);
- break;
- case GDK_SCROLL_SMOOTH:
- if (event->scroll.delta_y)
- zoom_to(state, state->zoom * pow(0.75, event->scroll.delta_y));
- break;
- default:
- break;
- }
+ GdkEvent *event = gtk_get_current_event();
+ GdkModifierType mod_state = event->scroll.state;
+
+ if (mod_state & GDK_CONTROL_MASK) {
+ if (delta_y)
+ zoom_to(state, state->zoom * pow(0.75, delta_y));
} else {
GtkAdjustment *xadj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(state->scroller));
GtkAdjustment *yadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(state->scroller));
double xstep = gtk_adjustment_get_step_increment(xadj);
double ystep = gtk_adjustment_get_step_increment(yadj);
- switch (event->scroll.direction) {
- case GDK_SCROLL_UP:
- gtk_adjustment_set_value(yadj, gtk_adjustment_get_value(yadj) - ystep);
- break;
- case GDK_SCROLL_DOWN:
- gtk_adjustment_set_value(yadj, gtk_adjustment_get_value(yadj) + ystep);
- break;
- case GDK_SCROLL_LEFT:
- gtk_adjustment_set_value(xadj, gtk_adjustment_get_value(xadj) - xstep);
- break;
- case GDK_SCROLL_RIGHT:
- gtk_adjustment_set_value(xadj, gtk_adjustment_get_value(xadj) + xstep);
- break;
- case GDK_SCROLL_SMOOTH:
- if (event->scroll.delta_x)
- gtk_adjustment_set_value(xadj, gtk_adjustment_get_value(xadj) + xstep * event->scroll.delta_x);
- if (event->scroll.delta_y)
- gtk_adjustment_set_value(yadj, gtk_adjustment_get_value(yadj) + ystep * event->scroll.delta_y);
- break;
- default:
- break;
- }
+ if (delta_x)
+ gtk_adjustment_set_value(xadj, gtk_adjustment_get_value(xadj) + xstep * delta_x);
+ if (delta_y)
+ gtk_adjustment_set_value(yadj, gtk_adjustment_get_value(yadj) + ystep * delta_y);
}
- return FALSE;
+ return TRUE;
}
static void canvas_resize(GtkWidget *widget, GdkRectangle *allocation,
@@ -1171,13 +1138,13 @@ static void canvas_resize(GtkWidget *widget, GdkRectangle *allocation,
update_scroll_size(state);
}
-static void cancel_changes(GtkButton *button, gpointer data) {
+static void cancel_changes(GSimpleAction *action, GVariant *param, gpointer data) {
struct wd_state *state = data;
gtk_stack_set_visible_child_name(GTK_STACK(state->header_stack), "title");
wd_ui_reset_all(state);
}
-static void apply_changes(GtkButton *button, gpointer data) {
+static void apply_changes(GSimpleAction *action, GVariant *param, gpointer data) {
apply_state(data);
}
@@ -1195,8 +1162,8 @@ static void info_bar_animation_done(GObject *object, GParamSpec *pspec, gpointer
static void auto_apply_selected(GSimpleAction *action, GVariant *param, gpointer data) {
struct wd_state *state = data;
- state->autoapply = !state->autoapply;
- g_simple_action_set_state(action, g_variant_new_boolean(state->autoapply));
+ state->autoapply = g_variant_get_boolean(param);
+ g_simple_action_set_state(action, param);
}
static gboolean redraw_canvas(GtkWidget *widget, GdkFrameClock *frame_clock, gpointer data) {
@@ -1211,15 +1178,15 @@ static gboolean redraw_canvas(GtkWidget *widget, GdkFrameClock *frame_clock, gpo
static void capture_selected(GSimpleAction *action, GVariant *param, gpointer data) {
struct wd_state *state = data;
- state->capture = !state->capture;
- g_simple_action_set_state(action, g_variant_new_boolean(state->capture));
+ state->capture = g_variant_get_boolean(param);
+ g_simple_action_set_state(action, param);
update_tick_callback(state);
}
static void overlay_selected(GSimpleAction *action, GVariant *param, gpointer data) {
struct wd_state *state = data;
- state->show_overlay = !state->show_overlay;
- g_simple_action_set_state(action, g_variant_new_boolean(state->show_overlay));
+ state->show_overlay = g_variant_get_boolean(param);
+ g_simple_action_set_state(action, param);
struct wd_output *output;
wl_list_for_each(output, &state->outputs, link) {
@@ -1287,38 +1254,42 @@ static void activate(GtkApplication* app, gpointer user_data) {
state->info_label = GTK_WIDGET(gtk_builder_get_object(builder, "heads_info_label"));
state->menu_button = GTK_WIDGET(gtk_builder_get_object(builder, "menu_button"));
- gtk_builder_add_callback_symbol(builder, "apply_changes", G_CALLBACK(apply_changes));
- gtk_builder_add_callback_symbol(builder, "cancel_changes", G_CALLBACK(cancel_changes));
- gtk_builder_add_callback_symbol(builder, "zoom_out", G_CALLBACK(zoom_out));
- gtk_builder_add_callback_symbol(builder, "zoom_reset", G_CALLBACK(zoom_reset));
- gtk_builder_add_callback_symbol(builder, "zoom_in", G_CALLBACK(zoom_in));
- gtk_builder_add_callback_symbol(builder, "info_response", G_CALLBACK(info_response));
- gtk_builder_add_callback_symbol(builder, "destroy", G_CALLBACK(cleanup));
- gtk_builder_connect_signals(builder, state);
- gtk_box_set_homogeneous(GTK_BOX(gtk_builder_get_object(builder, "zoom_box")), FALSE);
-
g_signal_connect(window, "window-state-event", G_CALLBACK(window_state_changed), state);
+ g_signal_connect(window, "destroy", G_CALLBACK(cleanup), state);
state->canvas = wd_gl_viewport_new();
- gtk_container_add(GTK_CONTAINER(state->scroller), state->canvas);
gtk_widget_add_events(state->canvas, GDK_POINTER_MOTION_MASK
| GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK
| GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
g_signal_connect(state->canvas, "realize", G_CALLBACK(canvas_realize), state);
g_signal_connect(state->canvas, "render", G_CALLBACK(canvas_render), state);
g_signal_connect(state->canvas, "unrealize", G_CALLBACK(canvas_unrealize), state);
- g_signal_connect(state->canvas, "button-press-event", G_CALLBACK(canvas_click), state);
- g_signal_connect(state->canvas, "button-release-event", G_CALLBACK(canvas_release), state);
- g_signal_connect(state->canvas, "enter-notify-event", G_CALLBACK(canvas_enter), state);
- g_signal_connect(state->canvas, "leave-notify-event", G_CALLBACK(canvas_leave), state);
- g_signal_connect(state->canvas, "motion-notify-event", G_CALLBACK(canvas_motion), state);
- g_signal_connect(state->canvas, "scroll-event", G_CALLBACK(canvas_scroll), state);
g_signal_connect(state->canvas, "size-allocate", G_CALLBACK(canvas_resize), state);
- gtk_gl_area_set_use_es(GTK_GL_AREA(state->canvas), TRUE);
gtk_gl_area_set_required_version(GTK_GL_AREA(state->canvas), 2, 0);
+ gtk_gl_area_set_use_es(GTK_GL_AREA(state->canvas), TRUE);
gtk_gl_area_set_has_alpha(GTK_GL_AREA(state->canvas), TRUE);
gtk_gl_area_set_auto_render(GTK_GL_AREA(state->canvas), state->capture);
+ GtkGesture *canvas_drag1_controller = gtk_gesture_drag_new(state->canvas);
+ GtkGesture *canvas_drag2_controller = gtk_gesture_drag_new(state->canvas);
+ gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(canvas_drag2_controller), 2);
+ GtkEventController *canvas_motion_controller = gtk_event_controller_motion_new(state->canvas);
+ gtk_event_controller_set_propagation_phase(canvas_motion_controller, GTK_PHASE_CAPTURE);
+ GtkEventController *canvas_scroll_controller = gtk_event_controller_scroll_new(state->canvas, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES);
+ gtk_event_controller_set_propagation_phase(canvas_scroll_controller, GTK_PHASE_CAPTURE);
+ g_signal_connect(canvas_drag1_controller, "drag-begin", G_CALLBACK(canvas_drag1_begin), state);
+ g_signal_connect(canvas_drag1_controller, "drag-update", G_CALLBACK(canvas_drag1_update), state);
+ g_signal_connect(canvas_drag1_controller, "drag-end", G_CALLBACK(canvas_drag1_end), state);
+ g_signal_connect(canvas_drag2_controller, "drag-begin", G_CALLBACK(canvas_drag2_begin), state);
+ g_signal_connect(canvas_drag2_controller, "drag-update", G_CALLBACK(canvas_drag2_update), state);
+ g_signal_connect(canvas_drag2_controller, "drag-end", G_CALLBACK(canvas_drag2_end), state);
+ g_signal_connect(canvas_motion_controller, "enter", G_CALLBACK(canvas_enter), state);
+ g_signal_connect(canvas_motion_controller, "leave", G_CALLBACK(canvas_leave), state);
+ g_signal_connect(canvas_motion_controller, "motion", G_CALLBACK(canvas_motion), state);
+ g_signal_connect(canvas_scroll_controller, "scroll", G_CALLBACK(canvas_scroll), state);
+
+ gtk_container_add(GTK_CONTAINER(state->scroller), state->canvas);
+
GtkAdjustment *scroll_x_adj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(state->scroller));
GtkAdjustment *scroll_y_adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(state->scroller));
g_signal_connect_swapped(scroll_x_adj, "value-changed", G_CALLBACK(queue_canvas_draw), state);
@@ -1327,24 +1298,51 @@ static void activate(GtkApplication* app, gpointer user_data) {
update_zoom(state);
GSimpleActionGroup *main_actions = g_simple_action_group_new();
- gtk_widget_insert_action_group(state->menu_button, APP_PREFIX, G_ACTION_GROUP(main_actions));
+ gtk_widget_insert_action_group(window, APP_PREFIX, G_ACTION_GROUP(main_actions));
g_object_unref(main_actions);
- GSimpleAction *autoapply_action = g_simple_action_new_stateful("auto-apply", NULL,
+ GSimpleAction *action = g_simple_action_new("apply-changes", NULL);
+ g_signal_connect(action, "activate", G_CALLBACK(apply_changes), state);
+ g_action_map_add_action(G_ACTION_MAP(main_actions), G_ACTION(action));
+
+ action = g_simple_action_new("cancel-changes", NULL);
+ g_signal_connect(action, "activate", G_CALLBACK(cancel_changes), state);
+ g_action_map_add_action(G_ACTION_MAP(main_actions), G_ACTION(action));
+
+ action = g_simple_action_new("zoom-out", NULL);
+ g_signal_connect(action, "activate", G_CALLBACK(zoom_out), state);
+ g_action_map_add_action(G_ACTION_MAP(main_actions), G_ACTION(action));
+
+ action = g_simple_action_new("zoom-reset", NULL);
+ g_signal_connect(action, "activate", G_CALLBACK(zoom_reset), state);
+ g_action_map_add_action(G_ACTION_MAP(main_actions), G_ACTION(action));
+
+ action = g_simple_action_new("zoom-in", NULL);
+ g_signal_connect(action, "activate", G_CALLBACK(zoom_in), state);
+ g_action_map_add_action(G_ACTION_MAP(main_actions), G_ACTION(action));
+
+ action = g_simple_action_new_stateful("auto-apply", NULL,
g_variant_new_boolean(state->autoapply));
- g_signal_connect(autoapply_action, "activate", G_CALLBACK(auto_apply_selected), state);
- g_action_map_add_action(G_ACTION_MAP(main_actions), G_ACTION(autoapply_action));
+ g_signal_connect(action, "change-state", G_CALLBACK(auto_apply_selected), state);
+ g_action_map_add_action(G_ACTION_MAP(main_actions), G_ACTION(action));
GSimpleAction *capture_action = g_simple_action_new_stateful("capture-screens", NULL,
g_variant_new_boolean(state->capture));
- g_signal_connect(capture_action, "activate", G_CALLBACK(capture_selected), state);
+ g_signal_connect(capture_action, "change-state", G_CALLBACK(capture_selected), state);
g_action_map_add_action(G_ACTION_MAP(main_actions), G_ACTION(capture_action));
GSimpleAction *overlay_action = g_simple_action_new_stateful("show-overlay", NULL,
g_variant_new_boolean(state->show_overlay));
- g_signal_connect(overlay_action, "activate", G_CALLBACK(overlay_selected), state);
+ g_signal_connect(overlay_action, "change-state", G_CALLBACK(overlay_selected), state);
g_action_map_add_action(G_ACTION_MAP(main_actions), G_ACTION(overlay_action));
+ GMenu *main_menu = g_menu_new();
+ g_menu_append(main_menu, "_Automatically Apply Changes", "app.auto-apply");
+ g_menu_append(main_menu, "_Show Screen Contents", "app.capture-screens");
+ g_menu_append(main_menu, "_Overlay Screen Names", "app.show-overlay");
+ gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(state->menu_button), G_MENU_MODEL(main_menu));
+
+ g_signal_connect(state->info_bar, "response", G_CALLBACK(info_response), state);
/* first child of GtkInfoBar is always GtkRevealer */
g_autoptr(GList) info_children = gtk_container_get_children(GTK_CONTAINER(state->info_bar));
g_signal_connect(info_children->data, "notify::child-revealed", G_CALLBACK(info_bar_animation_done), state);
diff --git a/src/meson.build b/src/meson.build
index ea5af79..c0a0fbe 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -4,8 +4,8 @@
cc = meson.get_compiler('c')
m_dep = cc.find_library('m', required : false)
rt_dep = cc.find_library('rt', required : false)
-gdk = dependency('gdk-3.0')
-gtk = dependency('gtk+-3.0')
+gdk = dependency('gdk-3.0', version: '>= 3.24')
+gtk = dependency('gtk+-3.0', version: '>= 3.24')
assert(gdk.get_pkgconfig_variable('targets').split().contains('wayland'), 'Wayland GDK backend not present')
epoxy = dependency('epoxy')
diff --git a/src/wdisplays.h b/src/wdisplays.h
index 6244b9b..27b5141 100644
--- a/src/wdisplays.h
+++ b/src/wdisplays.h
@@ -205,10 +205,10 @@ struct wd_state {
unsigned int reset_idle;
struct wd_render_head_data *clicked;
- /* top left, bottom right */
- struct wd_point click_offset;
+ struct wd_point drag_start;
+ struct wd_point head_drag_start; /* 0-1 range in head rect */
bool panning;
- struct wd_point pan_last;
+ struct wd_point pan_start;
GtkWidget *main_box;
GtkWidget *header_stack;