Create WdHeadForm class
This commit is contained in:
parent
f3f2b9e9e2
commit
0ed52013dd
@ -8,7 +8,7 @@ Copyright: 2020 Jason Francis <jason@cycles.network>
|
|||||||
License: CC0-1.0
|
License: CC0-1.0
|
||||||
|
|
||||||
Files: resources/style.css resources/wdisplays.desktop.in resources/*.ui
|
Files: resources/style.css resources/wdisplays.desktop.in resources/*.ui
|
||||||
resources/resources.xml
|
resources/resources.xml.in
|
||||||
Copyright: 2020 Jason Francis <jason@cycles.network>
|
Copyright: 2020 Jason Francis <jason@cycles.network>
|
||||||
License: GPL-3.0-or-later
|
License: GPL-3.0-or-later
|
||||||
|
|
||||||
|
@ -3,9 +3,11 @@
|
|||||||
|
|
||||||
project('network.cycles.wdisplays', 'c', license: 'MIT', version: '1.0')
|
project('network.cycles.wdisplays', 'c', license: 'MIT', version: '1.0')
|
||||||
|
|
||||||
conf = configuration_data()
|
conf = configuration_data({
|
||||||
conf.set('app_id', meson.project_name())
|
'app_id': meson.project_name(),
|
||||||
conf.set('version', meson.project_version())
|
'version': meson.project_version(),
|
||||||
|
'resource_prefix': '/' / '/'.join(meson.project_name().split('.')),
|
||||||
|
})
|
||||||
|
|
||||||
subdir('protocol')
|
subdir('protocol')
|
||||||
subdir('resources')
|
subdir('resources')
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!-- Generated with glade 3.22.0 -->
|
<!-- Generated with glade 3.22.2 -->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.22"/>
|
<requires lib="gtk+" version="3.22"/>
|
||||||
<object class="GtkAdjustment" id="height_adjustment">
|
<object class="GtkAdjustment" id="height_adjustment">
|
||||||
@ -33,7 +33,7 @@
|
|||||||
<property name="step_increment">1</property>
|
<property name="step_increment">1</property>
|
||||||
<property name="page_increment">10</property>
|
<property name="page_increment">10</property>
|
||||||
</object>
|
</object>
|
||||||
<object class="GtkGrid" id="form">
|
<template class="WdHeadForm" parent="GtkGrid">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="margin_start">8</property>
|
<property name="margin_start">8</property>
|
||||||
@ -52,7 +52,7 @@
|
|||||||
<property name="halign">start</property>
|
<property name="halign">start</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="draw_indicator">True</property>
|
<property name="draw_indicator">True</property>
|
||||||
<signal name="toggled" handler="enabled" swapped="no"/>
|
<signal name="toggled" handler="enabled_toggled" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="left_attach">1</property>
|
<property name="left_attach">1</property>
|
||||||
@ -82,7 +82,7 @@
|
|||||||
<property name="adjustment">scale_adjustment</property>
|
<property name="adjustment">scale_adjustment</property>
|
||||||
<property name="digits">2</property>
|
<property name="digits">2</property>
|
||||||
<property name="value">1</property>
|
<property name="value">1</property>
|
||||||
<signal name="change-value" handler="scale" swapped="no"/>
|
<signal name="value-changed" handler="position_spin_changed" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="left_attach">1</property>
|
<property name="left_attach">1</property>
|
||||||
@ -186,6 +186,7 @@
|
|||||||
<property name="digits">3</property>
|
<property name="digits">3</property>
|
||||||
<property name="numeric">True</property>
|
<property name="numeric">True</property>
|
||||||
<property name="update_policy">if-valid</property>
|
<property name="update_policy">if-valid</property>
|
||||||
|
<signal name="value-changed" handler="mode_spin_changed" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
@ -260,7 +261,7 @@
|
|||||||
<property name="halign">start</property>
|
<property name="halign">start</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="draw_indicator">True</property>
|
<property name="draw_indicator">True</property>
|
||||||
<signal name="toggled" handler="flipped" swapped="no"/>
|
<signal name="toggled" handler="flipped_toggled" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="left_attach">1</property>
|
<property name="left_attach">1</property>
|
||||||
@ -282,6 +283,7 @@
|
|||||||
<property name="adjustment">pos_x_adjustment</property>
|
<property name="adjustment">pos_x_adjustment</property>
|
||||||
<property name="numeric">True</property>
|
<property name="numeric">True</property>
|
||||||
<property name="update_policy">if-valid</property>
|
<property name="update_policy">if-valid</property>
|
||||||
|
<signal name="value-changed" handler="position_spin_changed" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="left_attach">0</property>
|
<property name="left_attach">0</property>
|
||||||
@ -298,6 +300,7 @@
|
|||||||
<property name="adjustment">pos_y_adjustment</property>
|
<property name="adjustment">pos_y_adjustment</property>
|
||||||
<property name="numeric">True</property>
|
<property name="numeric">True</property>
|
||||||
<property name="update_policy">if-valid</property>
|
<property name="update_policy">if-valid</property>
|
||||||
|
<signal name="value-changed" handler="position_spin_changed" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="left_attach">2</property>
|
<property name="left_attach">2</property>
|
||||||
@ -315,6 +318,7 @@
|
|||||||
<property name="adjustment">width_adjustment</property>
|
<property name="adjustment">width_adjustment</property>
|
||||||
<property name="numeric">True</property>
|
<property name="numeric">True</property>
|
||||||
<property name="update_policy">if-valid</property>
|
<property name="update_policy">if-valid</property>
|
||||||
|
<signal name="value-changed" handler="mode_spin_changed" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="left_attach">0</property>
|
<property name="left_attach">0</property>
|
||||||
@ -344,6 +348,7 @@
|
|||||||
<property name="adjustment">height_adjustment</property>
|
<property name="adjustment">height_adjustment</property>
|
||||||
<property name="numeric">True</property>
|
<property name="numeric">True</property>
|
||||||
<property name="update_policy">if-valid</property>
|
<property name="update_policy">if-valid</property>
|
||||||
|
<signal name="value-changed" handler="mode_spin_changed" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="left_attach">2</property>
|
<property name="left_attach">2</property>
|
||||||
@ -389,5 +394,5 @@
|
|||||||
<child>
|
<child>
|
||||||
<placeholder/>
|
<placeholder/>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</template>
|
||||||
</interface>
|
</interface>
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
# SPDX-FileCopyrightText: 2020 Jason Francis <jason@cycles.network>
|
# SPDX-FileCopyrightText: 2020 Jason Francis <jason@cycles.network>
|
||||||
# SPDX-License-Identifier: CC0-1.0
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
resources_xml = configure_file(
|
||||||
|
input: 'resources.xml.in',
|
||||||
|
output: 'resources.xml',
|
||||||
|
configuration: conf
|
||||||
|
)
|
||||||
|
|
||||||
gnome = import('gnome')
|
gnome = import('gnome')
|
||||||
resources = gnome.compile_resources(
|
resources = gnome.compile_resources(
|
||||||
'wdisplays-resources', 'resources.xml',
|
'wdisplays-resources', resources_xml,
|
||||||
source_dir : '.',
|
source_dir : '.',
|
||||||
c_name : 'wdisplays_resources')
|
c_name : 'wdisplays_resources')
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<gresources>
|
<gresources>
|
||||||
<gresource prefix="/">
|
<gresource prefix="@resource_prefix@">
|
||||||
<file compressed="true" preprocess="xml-stripblanks">wdisplays.ui</file>
|
<file compressed="true" preprocess="xml-stripblanks">wdisplays.ui</file>
|
||||||
<file compressed="true" preprocess="xml-stripblanks">head.ui</file>
|
<file compressed="true" preprocess="xml-stripblanks">head.ui</file>
|
||||||
<file compressed="true">style.css</file>
|
<file compressed="true">style.css</file>
|
@ -6,5 +6,6 @@
|
|||||||
|
|
||||||
#define WDISPLAYS_APP_ID "@app_id@"
|
#define WDISPLAYS_APP_ID "@app_id@"
|
||||||
#define WDISPLAYS_VERSION "@version@"
|
#define WDISPLAYS_VERSION "@version@"
|
||||||
|
#define WDISPLAYS_RESOURCE_PREFIX "@resource_prefix@"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
393
src/headform.c
Normal file
393
src/headform.c
Normal file
@ -0,0 +1,393 @@
|
|||||||
|
/* SPDX-FileCopyrightText: 2020 Jason Francis <jason@cycles.network>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||||
|
|
||||||
|
#include "headform.h"
|
||||||
|
#include "wdisplays.h"
|
||||||
|
|
||||||
|
typedef struct _WdHeadFormPrivate {
|
||||||
|
GtkWidget *enabled;
|
||||||
|
GtkWidget *description;
|
||||||
|
GtkWidget *physical_size;
|
||||||
|
GtkWidget *scale;
|
||||||
|
GtkWidget *pos_x;
|
||||||
|
GtkWidget *pos_y;
|
||||||
|
GtkWidget *width;
|
||||||
|
GtkWidget *height;
|
||||||
|
GtkWidget *refresh;
|
||||||
|
GtkWidget *mode_button;
|
||||||
|
GtkWidget *rotate_button;
|
||||||
|
GtkWidget *flipped;
|
||||||
|
|
||||||
|
GAction *mode_action;
|
||||||
|
GAction *rotate_action;
|
||||||
|
} WdHeadFormPrivate;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CHANGED,
|
||||||
|
LAST_SIGNAL
|
||||||
|
};
|
||||||
|
|
||||||
|
static guint signals[LAST_SIGNAL];
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_WITH_CODE(WdHeadForm, wd_head_form, GTK_TYPE_GRID,
|
||||||
|
G_ADD_PRIVATE(WdHeadForm))
|
||||||
|
|
||||||
|
static const char *HEAD_PREFIX = "head";
|
||||||
|
static const char *MODE_PREFIX = "mode";
|
||||||
|
static const char *ROTATE_PREFIX = "rotate";
|
||||||
|
|
||||||
|
static void head_form_update_sensitivity(WdHeadForm *form) {
|
||||||
|
WdHeadFormPrivate *priv = wd_head_form_get_instance_private(form);
|
||||||
|
|
||||||
|
bool enabled_toggled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->enabled));
|
||||||
|
|
||||||
|
g_autoptr(GList) children = gtk_container_get_children(GTK_CONTAINER(form));
|
||||||
|
for (GList *child = children; child != NULL; child = child->next) {
|
||||||
|
GtkWidget *widget = GTK_WIDGET(child->data);
|
||||||
|
if (widget != priv->enabled) {
|
||||||
|
gtk_widget_set_sensitive(widget, enabled_toggled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct vid_mode {
|
||||||
|
int32_t width;
|
||||||
|
int32_t height;
|
||||||
|
int32_t refresh;
|
||||||
|
};
|
||||||
|
|
||||||
|
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 enabled_toggled(GtkToggleButton *toggle, gpointer data) {
|
||||||
|
WdHeadForm *form = WD_HEAD_FORM(data);
|
||||||
|
head_form_update_sensitivity(form);
|
||||||
|
g_signal_emit(form, signals[CHANGED], 0, WD_FIELD_ENABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mode_spin_changed(GtkSpinButton *spin_button, gpointer data) {
|
||||||
|
WdHeadForm *form = WD_HEAD_FORM(data);
|
||||||
|
WdHeadFormPrivate *priv = wd_head_form_get_instance_private(form);
|
||||||
|
struct vid_mode mode;
|
||||||
|
GVariant *value = g_action_get_state(priv->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(priv->mode_action, create_mode_variant(mode.width, mode.height, mode.refresh));
|
||||||
|
g_signal_emit(form, signals[CHANGED], 0, WD_FIELD_MODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void position_spin_changed(GtkSpinButton *spin_button, gpointer data) {
|
||||||
|
WdHeadForm *form = WD_HEAD_FORM(data);
|
||||||
|
g_signal_emit(form, signals[CHANGED], 0, WD_FIELD_POSITION);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void flipped_toggled(GtkToggleButton *toggle, gpointer data) {
|
||||||
|
WdHeadForm *form = WD_HEAD_FORM(data);
|
||||||
|
g_signal_emit(form, signals[CHANGED], 0, WD_FIELD_TRANSFORM);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wd_head_form_class_init(WdHeadFormClass *class) {
|
||||||
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(class);
|
||||||
|
|
||||||
|
signals[CHANGED] = g_signal_new("changed",
|
||||||
|
G_OBJECT_CLASS_TYPE(class),
|
||||||
|
G_SIGNAL_RUN_LAST,
|
||||||
|
G_STRUCT_OFFSET(WdHeadFormClass, changed),
|
||||||
|
NULL, NULL, NULL,
|
||||||
|
G_TYPE_NONE, 1, G_TYPE_INT);
|
||||||
|
|
||||||
|
gtk_widget_class_set_template_from_resource(widget_class,
|
||||||
|
WDISPLAYS_RESOURCE_PREFIX "/head.ui");
|
||||||
|
|
||||||
|
gtk_widget_class_bind_template_callback(widget_class, enabled_toggled);
|
||||||
|
gtk_widget_class_bind_template_callback(widget_class, mode_spin_changed);
|
||||||
|
gtk_widget_class_bind_template_callback(widget_class, position_spin_changed);
|
||||||
|
gtk_widget_class_bind_template_callback(widget_class, flipped_toggled);
|
||||||
|
|
||||||
|
gtk_widget_class_bind_template_child_private(widget_class, WdHeadForm, enabled);
|
||||||
|
gtk_widget_class_bind_template_child_private(widget_class, WdHeadForm, description);
|
||||||
|
gtk_widget_class_bind_template_child_private(widget_class, WdHeadForm, physical_size);
|
||||||
|
gtk_widget_class_bind_template_child_private(widget_class, WdHeadForm, scale);
|
||||||
|
gtk_widget_class_bind_template_child_private(widget_class, WdHeadForm, pos_x);
|
||||||
|
gtk_widget_class_bind_template_child_private(widget_class, WdHeadForm, pos_y);
|
||||||
|
gtk_widget_class_bind_template_child_private(widget_class, WdHeadForm, width);
|
||||||
|
gtk_widget_class_bind_template_child_private(widget_class, WdHeadForm, height);
|
||||||
|
gtk_widget_class_bind_template_child_private(widget_class, WdHeadForm, refresh);
|
||||||
|
gtk_widget_class_bind_template_child_private(widget_class, WdHeadForm, mode_button);
|
||||||
|
gtk_widget_class_bind_template_child_private(widget_class, WdHeadForm, rotate_button);
|
||||||
|
gtk_widget_class_bind_template_child_private(widget_class, WdHeadForm, flipped);
|
||||||
|
gtk_widget_class_set_css_name(widget_class, "wd-head-form");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t get_rotate_value(enum wl_output_transform transform) {
|
||||||
|
if (transform == WL_OUTPUT_TRANSFORM_90 || transform == WL_OUTPUT_TRANSFORM_FLIPPED_90) {
|
||||||
|
return 90;
|
||||||
|
} else if (transform == WL_OUTPUT_TRANSFORM_180 || transform == WL_OUTPUT_TRANSFORM_FLIPPED_180) {
|
||||||
|
return 180;
|
||||||
|
} else if (transform == WL_OUTPUT_TRANSFORM_270 || transform == WL_OUTPUT_TRANSFORM_FLIPPED_270) {
|
||||||
|
return 270;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rotate_selected(GSimpleAction *action, GVariant *param, gpointer data) {
|
||||||
|
WdHeadForm *form = data;
|
||||||
|
WdHeadFormPrivate *priv = wd_head_form_get_instance_private(form);
|
||||||
|
GMenuModel *menu = gtk_menu_button_get_menu_model(GTK_MENU_BUTTON(priv->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(priv->rotate_button), g_variant_get_string(label, NULL));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_simple_action_set_state(action, param);
|
||||||
|
g_signal_emit(form, signals[CHANGED], 0, WD_FIELD_TRANSFORM);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mode_selected(GSimpleAction *action, GVariant *param, gpointer data) {
|
||||||
|
WdHeadForm *form = data;
|
||||||
|
WdHeadFormPrivate *priv = wd_head_form_get_instance_private(form);
|
||||||
|
struct vid_mode mode;
|
||||||
|
unpack_mode_variant(param, &mode);
|
||||||
|
|
||||||
|
g_simple_action_set_state(action, param);
|
||||||
|
gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->width), mode.width);
|
||||||
|
gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->height), mode.height);
|
||||||
|
gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->refresh), mode.refresh / 1000.);
|
||||||
|
g_signal_emit(form, signals[CHANGED], 0, WD_FIELD_MODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wd_head_form_init(WdHeadForm *form) {
|
||||||
|
gtk_widget_init_template(GTK_WIDGET(form));
|
||||||
|
WdHeadFormPrivate *priv = wd_head_form_get_instance_private(form);
|
||||||
|
|
||||||
|
GSimpleActionGroup *head_actions = g_simple_action_group_new();
|
||||||
|
gtk_widget_insert_action_group(priv->mode_button, HEAD_PREFIX, G_ACTION_GROUP(head_actions));
|
||||||
|
gtk_widget_insert_action_group(priv->rotate_button, HEAD_PREFIX, G_ACTION_GROUP(head_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(priv->rotate_button), G_MENU_MODEL(rotate_menu));
|
||||||
|
|
||||||
|
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_unref(action);
|
||||||
|
priv->mode_action = G_ACTION(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_unref(action);
|
||||||
|
priv->rotate_action = G_ACTION(action);
|
||||||
|
|
||||||
|
g_object_unref(head_actions);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wd_head_form_update(WdHeadForm *form, const struct wd_head *head,
|
||||||
|
enum wd_head_fields fields) {
|
||||||
|
g_return_if_fail(form);
|
||||||
|
g_return_if_fail(head);
|
||||||
|
|
||||||
|
WdHeadFormPrivate *priv = wd_head_form_get_instance_private(form);
|
||||||
|
if (!fields)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (fields & WD_FIELD_DESCRIPTION)
|
||||||
|
gtk_label_set_text(GTK_LABEL(priv->description), head->description);
|
||||||
|
if (fields & WD_FIELD_PHYSICAL_SIZE) {
|
||||||
|
g_autofree gchar *physical_str = g_strdup_printf("%dmm × %dmm", head->phys_width, head->phys_height);
|
||||||
|
gtk_label_set_text(GTK_LABEL(priv->physical_size), physical_str);
|
||||||
|
}
|
||||||
|
if (fields & WD_FIELD_ENABLED)
|
||||||
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->enabled), head->enabled);
|
||||||
|
if (fields & WD_FIELD_SCALE)
|
||||||
|
gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->scale), head->scale);
|
||||||
|
if (fields & WD_FIELD_POSITION) {
|
||||||
|
gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->pos_x), head->x);
|
||||||
|
gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->pos_y), head->y);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fields & WD_FIELD_MODE) {
|
||||||
|
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.);
|
||||||
|
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(priv->mode_button), G_MENU_MODEL(mode_menu));
|
||||||
|
// Mode entries
|
||||||
|
int w = head->custom_mode.width;
|
||||||
|
int h = head->custom_mode.height;
|
||||||
|
int r = head->custom_mode.refresh;
|
||||||
|
if (head->enabled && head->mode != NULL) {
|
||||||
|
w = head->mode->width;
|
||||||
|
h = head->mode->height;
|
||||||
|
r = head->mode->refresh;
|
||||||
|
} else if (!head->enabled && w == 0 && h == 0) {
|
||||||
|
struct wd_mode *mode;
|
||||||
|
wl_list_for_each(mode, &head->modes, link) {
|
||||||
|
if (mode->preferred) {
|
||||||
|
w = mode->width;
|
||||||
|
h = mode->height;
|
||||||
|
r = mode->refresh;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_action_change_state(priv->mode_action, create_mode_variant(w, h, r));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fields & WD_FIELD_TRANSFORM) {
|
||||||
|
int active_rotate = get_rotate_value(head->transform);
|
||||||
|
g_action_change_state(priv->rotate_action, g_variant_new_int32(active_rotate));
|
||||||
|
|
||||||
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->flipped),
|
||||||
|
head->transform == WL_OUTPUT_TRANSFORM_FLIPPED
|
||||||
|
|| head->transform == WL_OUTPUT_TRANSFORM_FLIPPED_90
|
||||||
|
|| head->transform == WL_OUTPUT_TRANSFORM_FLIPPED_180
|
||||||
|
|| head->transform == WL_OUTPUT_TRANSFORM_FLIPPED_270);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync state
|
||||||
|
if (fields & WD_FIELD_ENABLED) {
|
||||||
|
head_form_update_sensitivity(form);
|
||||||
|
}
|
||||||
|
g_signal_emit(form, signals[CHANGED], 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWidget *wd_head_form_new(void) {
|
||||||
|
return gtk_widget_new(WD_TYPE_HEAD_FORM, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean wd_head_form_get_enabled(WdHeadForm *form) {
|
||||||
|
WdHeadFormPrivate *priv = wd_head_form_get_instance_private(form);
|
||||||
|
return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->enabled));
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean wd_head_form_has_changes(WdHeadForm *form, const struct wd_head *head) {
|
||||||
|
g_return_val_if_fail(form, FALSE);
|
||||||
|
|
||||||
|
WdHeadFormPrivate *priv = wd_head_form_get_instance_private(form);
|
||||||
|
if (head->enabled != gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->enabled))) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
double old_scale = round(head->scale * 100.) / 100.;
|
||||||
|
double new_scale = round(gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->scale)) * 100.) / 100.;
|
||||||
|
if (old_scale != new_scale) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
if (head->x != gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->pos_x))) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
if (head->y != gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->pos_y))) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
int w = head->mode != NULL ? head->mode->width : head->custom_mode.width;
|
||||||
|
if (w != gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->width))) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
int h = head->mode != NULL ? head->mode->height : head->custom_mode.height;
|
||||||
|
if (h != gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->height))) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
int r = head->mode != NULL ? head->mode->refresh : head->custom_mode.refresh;
|
||||||
|
if (r / 1000. != gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->refresh))) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
if (g_variant_get_int32(g_action_get_state(priv->rotate_action)) != get_rotate_value(head->transform)) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
bool flipped = head->transform == WL_OUTPUT_TRANSFORM_FLIPPED
|
||||||
|
|| head->transform == WL_OUTPUT_TRANSFORM_FLIPPED_90
|
||||||
|
|| head->transform == WL_OUTPUT_TRANSFORM_FLIPPED_180
|
||||||
|
|| head->transform == WL_OUTPUT_TRANSFORM_FLIPPED_270;
|
||||||
|
if (flipped != gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->flipped))) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wd_head_form_fill_config(WdHeadForm *form, struct wd_head_config *output) {
|
||||||
|
g_return_if_fail(form);
|
||||||
|
g_return_if_fail(output);
|
||||||
|
|
||||||
|
WdHeadFormPrivate *priv = wd_head_form_get_instance_private(form);
|
||||||
|
output->enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->enabled));
|
||||||
|
output->scale = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->scale));
|
||||||
|
output->x = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->pos_x));
|
||||||
|
output->y = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->pos_y));
|
||||||
|
output->width = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->width));
|
||||||
|
output->height = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->height));
|
||||||
|
output->refresh = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->refresh)) * 1000.;
|
||||||
|
int32_t rotate = g_variant_get_int32(g_action_get_state(priv->rotate_action));
|
||||||
|
gboolean flipped = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->flipped));
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void wd_head_form_get_dimensions(WdHeadForm *form, WdHeadDimensions *dimensions) {
|
||||||
|
g_return_if_fail(form);
|
||||||
|
g_return_if_fail(dimensions);
|
||||||
|
|
||||||
|
WdHeadFormPrivate *priv = wd_head_form_get_instance_private(form);
|
||||||
|
|
||||||
|
dimensions->x = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->pos_x));
|
||||||
|
dimensions->y = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->pos_y));
|
||||||
|
dimensions->w = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->width));
|
||||||
|
dimensions->h = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->height));
|
||||||
|
dimensions->scale = gtk_spin_button_get_value(GTK_SPIN_BUTTON(priv->scale));
|
||||||
|
dimensions->rotation_id = g_variant_get_int32(g_action_get_state(priv->rotate_action)) / 90;
|
||||||
|
dimensions->flipped = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->flipped));
|
||||||
|
}
|
||||||
|
|
||||||
|
void wd_head_form_set_position(WdHeadForm *form, double x, double y) {
|
||||||
|
g_return_if_fail(form);
|
||||||
|
WdHeadFormPrivate *priv = wd_head_form_get_instance_private(form);
|
||||||
|
gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->pos_x), x);
|
||||||
|
gtk_spin_button_set_value(GTK_SPIN_BUTTON(priv->pos_y), y);
|
||||||
|
}
|
59
src/headform.h
Normal file
59
src/headform.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/* SPDX-FileCopyrightText: 2020 Jason Francis <jason@cycles.network>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||||
|
|
||||||
|
#ifndef WDISPLAY_HEADFORM_H
|
||||||
|
#define WDISPLAY_HEADFORM_H
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
enum wd_head_fields {
|
||||||
|
WD_FIELD_NAME = 1 << 0,
|
||||||
|
WD_FIELD_ENABLED = 1 << 1,
|
||||||
|
WD_FIELD_DESCRIPTION = 1 << 2,
|
||||||
|
WD_FIELD_PHYSICAL_SIZE = 1 << 3,
|
||||||
|
WD_FIELD_SCALE = 1 << 4,
|
||||||
|
WD_FIELD_POSITION = 1 << 5,
|
||||||
|
WD_FIELD_MODE = 1 << 6,
|
||||||
|
WD_FIELD_TRANSFORM = 1 << 7,
|
||||||
|
WD_FIELDS_ALL = (1 << 8) - 1
|
||||||
|
};
|
||||||
|
|
||||||
|
#define WD_TYPE_HEAD_FORM (wd_head_form_get_type())
|
||||||
|
G_DECLARE_DERIVABLE_TYPE(
|
||||||
|
WdHeadForm, wd_head_form, WD, HEAD_FORM, GtkGrid)
|
||||||
|
|
||||||
|
struct _WdHeadFormClass {
|
||||||
|
GtkGridClass parent_class;
|
||||||
|
|
||||||
|
void (*changed)(WdHeadForm *form, enum wd_head_fields fields);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wd_head;
|
||||||
|
struct wd_head_config;
|
||||||
|
|
||||||
|
typedef struct _WdHeadDimensions {
|
||||||
|
gdouble x;
|
||||||
|
gdouble y;
|
||||||
|
gdouble w;
|
||||||
|
gdouble h;
|
||||||
|
gdouble scale;
|
||||||
|
int rotation_id;
|
||||||
|
gboolean flipped;
|
||||||
|
} WdHeadDimensions;
|
||||||
|
|
||||||
|
GtkWidget *wd_head_form_new(void);
|
||||||
|
|
||||||
|
gboolean wd_head_form_get_enabled(WdHeadForm *form);
|
||||||
|
gboolean wd_head_form_has_changes(WdHeadForm *form, const struct wd_head *head);
|
||||||
|
void wd_head_form_update(WdHeadForm *form, const struct wd_head *head,
|
||||||
|
enum wd_head_fields fields);
|
||||||
|
void wd_head_form_fill_config(WdHeadForm *form, struct wd_head_config *output);
|
||||||
|
void wd_head_form_get_dimensions(WdHeadForm *form, WdHeadDimensions *dimensions);
|
||||||
|
void wd_head_form_set_position(WdHeadForm *form, double x, double y);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
458
src/main.c
458
src/main.c
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "wdisplays.h"
|
#include "wdisplays.h"
|
||||||
#include "glviewport.h"
|
#include "glviewport.h"
|
||||||
|
#include "headform.h"
|
||||||
|
|
||||||
__attribute__((noreturn)) void wd_fatal_error(int status, const char *message) {
|
__attribute__((noreturn)) void wd_fatal_error(int status, const char *message) {
|
||||||
GtkWindow *parent = gtk_application_get_active_window(GTK_APPLICATION(g_application_get_default()));
|
GtkWindow *parent = gtk_application_get_active_window(GTK_APPLICATION(g_application_get_default()));
|
||||||
@ -20,86 +21,16 @@ __attribute__((noreturn)) void wd_fatal_error(int status, const char *message) {
|
|||||||
#define MAX_ZOOM 1000.
|
#define MAX_ZOOM 1000.
|
||||||
#define CANVAS_MARGIN 40
|
#define CANVAS_MARGIN 40
|
||||||
|
|
||||||
static const char *HEAD_PREFIX = "head";
|
|
||||||
static const char *MODE_PREFIX = "mode";
|
|
||||||
static const char *ROTATE_PREFIX = "rotate";
|
|
||||||
static const char *APP_PREFIX = "app";
|
static const char *APP_PREFIX = "app";
|
||||||
|
|
||||||
static int32_t get_rotate_value(enum wl_output_transform transform) {
|
|
||||||
if (transform == WL_OUTPUT_TRANSFORM_90 || transform == WL_OUTPUT_TRANSFORM_FLIPPED_90) {
|
|
||||||
return 90;
|
|
||||||
} else if (transform == WL_OUTPUT_TRANSFORM_180 || transform == WL_OUTPUT_TRANSFORM_FLIPPED_180) {
|
|
||||||
return 180;
|
|
||||||
} else if (transform == WL_OUTPUT_TRANSFORM_270 || transform == WL_OUTPUT_TRANSFORM_FLIPPED_270) {
|
|
||||||
return 270;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool has_changes(const struct wd_state *state) {
|
static bool has_changes(const struct wd_state *state) {
|
||||||
g_autoptr(GList) forms = gtk_container_get_children(GTK_CONTAINER(state->stack));
|
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) {
|
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"));
|
if (wd_head_form_has_changes(WD_HEAD_FORM(form_iter->data),
|
||||||
const struct wd_head *head = g_object_get_data(G_OBJECT(form_iter->data), "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"));
|
return true;
|
||||||
if (head->enabled != gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "enabled")))) {
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
double old_scale = round(head->scale * 100.) / 100.;
|
|
||||||
double new_scale = round(gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "scale"))) * 100.) / 100.;
|
|
||||||
if (old_scale != new_scale) {
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
if (head->x != gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "pos_x")))) {
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
if (head->y != gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "pos_y")))) {
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
int w = head->mode != NULL ? head->mode->width : head->custom_mode.width;
|
|
||||||
if (w != gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "width")))) {
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
int h = head->mode != NULL ? head->mode->height : head->custom_mode.height;
|
|
||||||
if (h != gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "height")))) {
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
int r = head->mode != NULL ? head->mode->refresh : head->custom_mode.refresh;
|
|
||||||
if (r / 1000. != gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "refresh")))) {
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
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
|
|
||||||
|| head->transform == WL_OUTPUT_TRANSFORM_FLIPPED_180
|
|
||||||
|| head->transform == WL_OUTPUT_TRANSFORM_FLIPPED_270;
|
|
||||||
if (flipped != gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "flipped")))) {
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fill_output_from_form(struct wd_head_config *output, GtkWidget *form) {
|
|
||||||
GtkBuilder *builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form), "builder"));
|
|
||||||
output->head = g_object_get_data(G_OBJECT(form), "head");
|
|
||||||
output->enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "enabled")));
|
|
||||||
output->scale = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "scale")));
|
|
||||||
output->x = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "pos_x")));
|
|
||||||
output->y = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "pos_y")));
|
|
||||||
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")));
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean send_apply(gpointer data) {
|
static gboolean send_apply(gpointer data) {
|
||||||
@ -111,7 +42,8 @@ static gboolean send_apply(gpointer data) {
|
|||||||
for (GList *form_iter = forms; form_iter != NULL; form_iter = form_iter->next) {
|
for (GList *form_iter = forms; form_iter != NULL; form_iter = form_iter->next) {
|
||||||
struct wd_head_config *output = calloc(1, sizeof(*output));
|
struct wd_head_config *output = calloc(1, sizeof(*output));
|
||||||
wl_list_insert(outputs, &output->link);
|
wl_list_insert(outputs, &output->link);
|
||||||
fill_output_from_form(output, GTK_WIDGET(form_iter->data));
|
output->head = g_object_get_data(G_OBJECT(form_iter->data), "head");
|
||||||
|
wd_head_form_fill_config(WD_HEAD_FORM(form_iter->data), output);
|
||||||
}
|
}
|
||||||
GdkWindow *window = gtk_widget_get_window(state->stack);
|
GdkWindow *window = gtk_widget_get_window(state->stack);
|
||||||
GdkDisplay *display = gdk_window_get_display(window);
|
GdkDisplay *display = gdk_window_get_display(window);
|
||||||
@ -184,23 +116,21 @@ static void update_canvas_size(struct wd_state *state) {
|
|||||||
|
|
||||||
g_autoptr(GList) forms = gtk_container_get_children(GTK_CONTAINER(state->stack));
|
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) {
|
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"));
|
WdHeadForm *form = WD_HEAD_FORM(form_iter->data);
|
||||||
gboolean enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "enabled")));
|
if (wd_head_form_get_enabled(form)) {
|
||||||
if (enabled) {
|
WdHeadDimensions dim;
|
||||||
int x1 = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "pos_x")));
|
wd_head_form_get_dimensions(form, &dim);
|
||||||
int y1 = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "pos_y")));
|
int h = dim.h;
|
||||||
int w = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "width")));
|
int w = dim.w;
|
||||||
int h = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "height")));
|
int x2 = dim.x + w;
|
||||||
int x2 = x1 + w;
|
int y2 = dim.x + h;
|
||||||
int y2 = y1 + w;
|
if (dim.scale > 0.) {
|
||||||
double scale = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "scale")));
|
w /= dim.scale;
|
||||||
if (scale > 0.) {
|
h /= dim.scale;
|
||||||
w /= scale;
|
|
||||||
h /= scale;
|
|
||||||
}
|
}
|
||||||
xmin = MIN(xmin, x1);
|
xmin = MIN(xmin, dim.x);
|
||||||
xmax = MAX(xmax, x2);
|
xmax = MAX(xmax, x2);
|
||||||
ymin = MIN(ymin, y1);
|
ymin = MIN(ymin, dim.y);
|
||||||
ymax = MAX(ymax, y2);
|
ymax = MAX(ymax, y2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -319,11 +249,6 @@ static inline void color_to_float_array(GtkStyleContext *ctx,
|
|||||||
out[3] = color.alpha;
|
out[3] = color.alpha;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned form_get_rotation(GtkWidget *form) {
|
|
||||||
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; }
|
#define SWAP(_type, _a, _b) { _type _tmp = (_a); (_a) = (_b); (_b) = _tmp; }
|
||||||
|
|
||||||
static void queue_canvas_draw(struct wd_state *state) {
|
static void queue_canvas_draw(struct wd_state *state) {
|
||||||
@ -341,14 +266,13 @@ static void queue_canvas_draw(struct wd_state *state) {
|
|||||||
|
|
||||||
g_autoptr(GList) forms = gtk_container_get_children(GTK_CONTAINER(state->stack));
|
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) {
|
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"));
|
WdHeadForm *form = WD_HEAD_FORM(form_iter->data);
|
||||||
gboolean enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "enabled")));
|
if (wd_head_form_get_enabled(form)) {
|
||||||
if (enabled) {
|
WdHeadDimensions dim;
|
||||||
int x = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "pos_x")));
|
wd_head_form_get_dimensions(form, &dim);
|
||||||
int y = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "pos_y")));
|
double w = dim.w;
|
||||||
int w = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "width")));
|
double h = dim.h;
|
||||||
int h = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "height")));
|
double scale = dim.scale;
|
||||||
double scale = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "scale")));
|
|
||||||
if (scale <= 0.)
|
if (scale <= 0.)
|
||||||
scale = 1.;
|
scale = 1.;
|
||||||
|
|
||||||
@ -358,13 +282,13 @@ static void queue_canvas_draw(struct wd_state *state) {
|
|||||||
wl_list_insert(&state->render.heads, &head->render->link);
|
wl_list_insert(&state->render.heads, &head->render->link);
|
||||||
}
|
}
|
||||||
struct wd_render_head_data *render = head->render;
|
struct wd_render_head_data *render = head->render;
|
||||||
render->queued.rotation = form_get_rotation(GTK_WIDGET(form_iter->data));
|
render->queued.rotation = dim.rotation_id;
|
||||||
if (render->queued.rotation & 1) {
|
if (render->queued.rotation & 1) {
|
||||||
SWAP(int, w, h);
|
SWAP(int, w, h);
|
||||||
}
|
}
|
||||||
render->queued.x_invert = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "flipped")));
|
render->queued.x_invert = dim.flipped;
|
||||||
render->x1 = floor(x * state->zoom - state->render.scroll_x - state->render.x_origin);
|
render->x1 = floor(dim.x * state->zoom - state->render.scroll_x - state->render.x_origin);
|
||||||
render->y1 = floor(y * state->zoom - state->render.scroll_y - state->render.y_origin);
|
render->y1 = floor(dim.y * state->zoom - state->render.scroll_y - state->render.y_origin);
|
||||||
render->x2 = floor(render->x1 + w * state->zoom / scale);
|
render->x2 = floor(render->x1 + w * state->zoom / scale);
|
||||||
render->y2 = floor(render->y1 + h * state->zoom / scale);
|
render->y2 = floor(render->y1 + h * state->zoom / scale);
|
||||||
}
|
}
|
||||||
@ -372,7 +296,6 @@ static void queue_canvas_draw(struct wd_state *state) {
|
|||||||
gtk_gl_area_queue_render(GTK_GL_AREA(state->canvas));
|
gtk_gl_area_queue_render(GTK_GL_AREA(state->canvas));
|
||||||
}
|
}
|
||||||
|
|
||||||
// BEGIN FORM CALLBACKS
|
|
||||||
static void show_apply(struct wd_state *state) {
|
static void show_apply(struct wd_state *state) {
|
||||||
const gchar *page = "title";
|
const gchar *page = "title";
|
||||||
if (has_changes(state)) {
|
if (has_changes(state)) {
|
||||||
@ -385,189 +308,14 @@ static void show_apply(struct wd_state *state) {
|
|||||||
gtk_stack_set_visible_child_name(GTK_STACK(state->header_stack), page);
|
gtk_stack_set_visible_child_name(GTK_STACK(state->header_stack), page);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_ui(struct wd_state *state) {
|
static void update_ui(WdHeadForm *form, enum wd_head_fields fields,
|
||||||
|
gpointer data) {
|
||||||
|
struct wd_state *state = data;
|
||||||
show_apply(state);
|
show_apply(state);
|
||||||
update_canvas_size(state);
|
update_canvas_size(state);
|
||||||
queue_canvas_draw(state);
|
queue_canvas_draw(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_sensitivity(GtkWidget *form) {
|
|
||||||
GtkBuilder *builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form), "builder"));
|
|
||||||
GtkWidget *enabled = GTK_WIDGET(gtk_builder_get_object(builder, "enabled"));
|
|
||||||
bool enabled_toggled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(enabled));
|
|
||||||
|
|
||||||
g_autoptr(GList) children = gtk_container_get_children(GTK_CONTAINER(form));
|
|
||||||
for (GList *child = children; child != NULL; child = child->next) {
|
|
||||||
GtkWidget *widget = GTK_WIDGET(child->data);
|
|
||||||
if (widget != enabled) {
|
|
||||||
gtk_widget_set_sensitive(widget, enabled_toggled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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"));
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g_simple_action_set_state(action, param);
|
|
||||||
update_ui(head->state);
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct vid_mode {
|
|
||||||
int32_t width;
|
|
||||||
int32_t height;
|
|
||||||
int32_t refresh;
|
|
||||||
};
|
|
||||||
|
|
||||||
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");
|
|
||||||
GtkBuilder *builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form), "builder"));
|
|
||||||
struct vid_mode mode;
|
|
||||||
unpack_mode_variant(param, &mode);
|
|
||||||
|
|
||||||
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 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"));
|
|
||||||
GtkWidget *physical_size = GTK_WIDGET(gtk_builder_get_object(builder, "physical_size"));
|
|
||||||
GtkWidget *enabled = GTK_WIDGET(gtk_builder_get_object(builder, "enabled"));
|
|
||||||
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_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);
|
|
||||||
}
|
|
||||||
if (fields & WD_FIELD_DESCRIPTION) {
|
|
||||||
gtk_label_set_text(GTK_LABEL(description), head->description);
|
|
||||||
}
|
|
||||||
if (fields & WD_FIELD_PHYSICAL_SIZE) {
|
|
||||||
g_autofree gchar *physical_str = g_strdup_printf("%dmm × %dmm", head->phys_width, head->phys_height);
|
|
||||||
gtk_label_set_text(GTK_LABEL(physical_size), physical_str);
|
|
||||||
}
|
|
||||||
if (fields & WD_FIELD_ENABLED) {
|
|
||||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(enabled), head->enabled);
|
|
||||||
}
|
|
||||||
if (fields & WD_FIELD_SCALE) {
|
|
||||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(scale), head->scale);
|
|
||||||
}
|
|
||||||
if (fields & WD_FIELD_POSITION) {
|
|
||||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(pos_x), head->x);
|
|
||||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(pos_y), head->y);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fields & WD_FIELD_MODE) {
|
|
||||||
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.);
|
|
||||||
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;
|
|
||||||
int r = head->custom_mode.refresh;
|
|
||||||
if (head->enabled && head->mode != NULL) {
|
|
||||||
w = head->mode->width;
|
|
||||||
h = head->mode->height;
|
|
||||||
r = head->mode->refresh;
|
|
||||||
} else if (!head->enabled && w == 0 && h == 0) {
|
|
||||||
struct wd_mode *mode;
|
|
||||||
wl_list_for_each(mode, &head->modes, link) {
|
|
||||||
if (mode->preferred) {
|
|
||||||
w = mode->width;
|
|
||||||
h = mode->height;
|
|
||||||
r = mode->refresh;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g_action_change_state(mode_action, create_mode_variant(w, h, r));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fields & WD_FIELD_TRANSFORM) {
|
|
||||||
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
|
|
||||||
|| head->transform == WL_OUTPUT_TRANSFORM_FLIPPED_90
|
|
||||||
|| head->transform == WL_OUTPUT_TRANSFORM_FLIPPED_180
|
|
||||||
|| head->transform == WL_OUTPUT_TRANSFORM_FLIPPED_270);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sync state
|
|
||||||
if (fields & WD_FIELD_ENABLED) {
|
|
||||||
update_sensitivity(form);
|
|
||||||
}
|
|
||||||
update_ui(head->state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void wd_ui_reset_heads(struct wd_state *state) {
|
void wd_ui_reset_heads(struct wd_state *state) {
|
||||||
if (state->stack == NULL) {
|
if (state->stack == NULL) {
|
||||||
return;
|
return;
|
||||||
@ -578,68 +326,19 @@ void wd_ui_reset_heads(struct wd_state *state) {
|
|||||||
struct wd_head *head;
|
struct wd_head *head;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
wl_list_for_each(head, &state->heads, link) {
|
wl_list_for_each(head, &state->heads, link) {
|
||||||
GtkBuilder *builder;
|
|
||||||
GtkWidget *form;
|
|
||||||
if (form_iter == NULL) {
|
if (form_iter == NULL) {
|
||||||
builder = gtk_builder_new_from_resource("/head.ui");
|
GtkWidget *form = wd_head_form_new();;
|
||||||
form = GTK_WIDGET(gtk_builder_get_object(builder, "form"));
|
|
||||||
g_object_set_data(G_OBJECT(form), "builder", builder);
|
|
||||||
g_object_set_data(G_OBJECT(form), "head", head);
|
g_object_set_data(G_OBJECT(form), "head", head);
|
||||||
|
g_signal_connect(form, "changed", G_CALLBACK(update_ui), state);
|
||||||
g_autofree gchar *page_name = g_strdup_printf("%d", i);
|
g_autofree gchar *page_name = g_strdup_printf("%d", i);
|
||||||
gtk_stack_add_titled(GTK_STACK(state->stack), form, page_name, head->name);
|
gtk_stack_add_titled(GTK_STACK(state->stack), form, page_name, head->name);
|
||||||
|
wd_head_form_update(WD_HEAD_FORM(form), head, WD_FIELDS_ALL);
|
||||||
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 *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));
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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, "flipped"), "toggled", G_CALLBACK(update_ui), state);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
form = form_iter->data;
|
GtkWidget *form = GTK_WIDGET(form_iter->data);
|
||||||
if (head != g_object_get_data(G_OBJECT(form), "head")) {
|
if (head != g_object_get_data(G_OBJECT(form), "head")) {
|
||||||
g_object_set_data(G_OBJECT(form), "head", head);
|
g_object_set_data(G_OBJECT(form), "head", head);
|
||||||
update_head_form(form, WD_FIELDS_ALL);
|
gtk_container_child_set(GTK_CONTAINER(state->stack), form, "title", head->name, NULL);
|
||||||
|
wd_head_form_update(WD_HEAD_FORM(form), head, WD_FIELDS_ALL);
|
||||||
}
|
}
|
||||||
form_iter = form_iter->next;
|
form_iter = form_iter->next;
|
||||||
}
|
}
|
||||||
@ -647,23 +346,24 @@ void wd_ui_reset_heads(struct wd_state *state) {
|
|||||||
}
|
}
|
||||||
// remove everything else
|
// remove everything else
|
||||||
for (; form_iter != NULL; form_iter = form_iter->next) {
|
for (; form_iter != NULL; form_iter = form_iter->next) {
|
||||||
GtkBuilder *builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form_iter->data), "builder"));
|
|
||||||
g_object_unref(builder);
|
|
||||||
gtk_container_remove(GTK_CONTAINER(state->stack), GTK_WIDGET(form_iter->data));
|
gtk_container_remove(GTK_CONTAINER(state->stack), GTK_WIDGET(form_iter->data));
|
||||||
}
|
}
|
||||||
update_canvas_size(state);
|
update_canvas_size(state);
|
||||||
queue_canvas_draw(state);
|
queue_canvas_draw(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void wd_ui_reset_head(const struct wd_head *head, unsigned int fields) {
|
void wd_ui_reset_head(const struct wd_head *head, enum wd_head_fields fields) {
|
||||||
if (head->state->stack == NULL) {
|
if (head->state->stack == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
g_autoptr(GList) forms = gtk_container_get_children(GTK_CONTAINER(head->state->stack));
|
g_autoptr(GList) forms = gtk_container_get_children(GTK_CONTAINER(head->state->stack));
|
||||||
for (GList *form_iter = forms; form_iter != NULL; form_iter = form_iter->next) {
|
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");
|
GtkWidget *form = GTK_WIDGET(form_iter->data);
|
||||||
|
const struct wd_head *other = g_object_get_data(G_OBJECT(form), "head");
|
||||||
if (head == other) {
|
if (head == other) {
|
||||||
update_head_form(GTK_WIDGET(form_iter->data), fields);
|
if (fields & WD_FIELD_NAME)
|
||||||
|
gtk_container_child_set(GTK_CONTAINER(head->state->stack), form, "title", head->name, NULL);
|
||||||
|
wd_head_form_update(WD_HEAD_FORM(form), head, fields);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -675,7 +375,10 @@ void wd_ui_reset_all(struct wd_state *state) {
|
|||||||
wd_ui_reset_heads(state);
|
wd_ui_reset_heads(state);
|
||||||
g_autoptr(GList) forms = gtk_container_get_children(GTK_CONTAINER(state->stack));
|
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) {
|
for (GList *form_iter = forms; form_iter != NULL; form_iter = form_iter->next) {
|
||||||
update_head_form(GTK_WIDGET(form_iter->data), WD_FIELDS_ALL);
|
GtkWidget *form = GTK_WIDGET(form_iter->data);
|
||||||
|
struct wd_head *head = g_object_get_data(G_OBJECT(form), "head");
|
||||||
|
gtk_container_child_set(GTK_CONTAINER(state->stack), form, "title", head->name, NULL);
|
||||||
|
wd_head_form_update(WD_HEAD_FORM(form_iter->data), head, WD_FIELDS_ALL);
|
||||||
}
|
}
|
||||||
update_canvas_size(state);
|
update_canvas_size(state);
|
||||||
queue_canvas_draw(state);
|
queue_canvas_draw(state);
|
||||||
@ -950,28 +653,25 @@ static void canvas_drag1_update(GtkGestureDrag *drag,
|
|||||||
|
|
||||||
if (state->clicked == NULL)
|
if (state->clicked == NULL)
|
||||||
return;
|
return;
|
||||||
GtkWidget *form = NULL;
|
WdHeadForm *form = NULL;
|
||||||
g_autoptr(GList) forms = gtk_container_get_children(GTK_CONTAINER(state->stack));
|
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) {
|
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");
|
const struct wd_head *other = g_object_get_data(G_OBJECT(form_iter->data), "head");
|
||||||
if (state->clicked == other->render) {
|
if (state->clicked == other->render) {
|
||||||
form = form_iter->data;
|
form = WD_HEAD_FORM(form_iter->data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (form != NULL) {
|
if (!form)
|
||||||
GtkBuilder *builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form), "builder"));
|
return;
|
||||||
struct wd_point size = {
|
WdHeadDimensions dim;
|
||||||
.x = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "width"))),
|
wd_head_form_get_dimensions(form, &dim);
|
||||||
.y = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "height"))),
|
struct wd_point size = { .x = dim.w, .y = dim.h };
|
||||||
};
|
if (dim.scale > 0.) {
|
||||||
double scale = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "scale")));
|
size.x /= dim.scale;
|
||||||
if (scale > 0.) {
|
size.y /= dim.scale;
|
||||||
size.x /= scale;
|
|
||||||
size.y /= scale;
|
|
||||||
}
|
}
|
||||||
unsigned rot = form_get_rotation(form);
|
if (dim.rotation_id & 1) {
|
||||||
if (rot & 1) {
|
|
||||||
SWAP(int, size.x, size.y);
|
SWAP(int, size.x, size.y);
|
||||||
}
|
}
|
||||||
struct wd_point tl = { /* top left */
|
struct wd_point tl = { /* top left */
|
||||||
@ -993,20 +693,20 @@ static void canvas_drag1_update(GtkGestureDrag *drag,
|
|||||||
|
|
||||||
/* snapping */
|
/* snapping */
|
||||||
for (GList *form_iter = forms; form_iter != NULL; form_iter = form_iter->next) {
|
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");
|
WdHeadForm *other_form = WD_HEAD_FORM(form_iter->data);
|
||||||
|
const struct wd_head *other = g_object_get_data(G_OBJECT(other_form), "head");
|
||||||
if (other->render != state->clicked && !(mod_state & GDK_SHIFT_MASK)) {
|
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"));
|
WdHeadDimensions other_dim;
|
||||||
double x1 = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(other_builder, "pos_x")));
|
wd_head_form_get_dimensions(other_form, &other_dim);
|
||||||
double y1 = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(other_builder, "pos_y")));
|
double x1 = other_dim.x;
|
||||||
double w = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(other_builder, "width")));
|
double y1 = other_dim.y;
|
||||||
double h = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(other_builder, "height")));
|
double w = other_dim.w;
|
||||||
scale = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(other_builder, "scale")));
|
double h = other_dim.h;
|
||||||
if (scale > 0.) {
|
if (other_dim.scale > 0.) {
|
||||||
w /= scale;
|
w /= other_dim.scale;
|
||||||
h /= scale;
|
h /= other_dim.scale;
|
||||||
}
|
}
|
||||||
rot = form_get_rotation(GTK_WIDGET(form_iter->data));
|
if (other_dim.rotation_id & 1) {
|
||||||
if (rot & 1) {
|
|
||||||
SWAP(int, w, h);
|
SWAP(int, w, h);
|
||||||
}
|
}
|
||||||
double x2 = x1 + w;
|
double x2 = x1 + w;
|
||||||
@ -1038,11 +738,7 @@ static void canvas_drag1_update(GtkGestureDrag *drag,
|
|||||||
new_pos.y = y2;
|
new_pos.y = y2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GtkWidget *pos_x = GTK_WIDGET(gtk_builder_get_object(builder, "pos_x"));
|
wd_head_form_set_position(form, new_pos.x, new_pos.y);
|
||||||
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,
|
static void canvas_drag1_end(GtkGestureDrag *drag,
|
||||||
@ -1230,7 +926,8 @@ static void activate(GtkApplication* app, gpointer user_data) {
|
|||||||
state->reset_idle = -1;
|
state->reset_idle = -1;
|
||||||
|
|
||||||
GtkCssProvider *css_provider = gtk_css_provider_new();
|
GtkCssProvider *css_provider = gtk_css_provider_new();
|
||||||
gtk_css_provider_load_from_resource(css_provider, "/style.css");
|
gtk_css_provider_load_from_resource(css_provider,
|
||||||
|
WDISPLAYS_RESOURCE_PREFIX "/style.css");
|
||||||
gtk_style_context_add_provider_for_screen(gdk_screen_get_default(), GTK_STYLE_PROVIDER(css_provider),
|
gtk_style_context_add_provider_for_screen(gdk_screen_get_default(), GTK_STYLE_PROVIDER(css_provider),
|
||||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||||
|
|
||||||
@ -1238,7 +935,8 @@ static void activate(GtkApplication* app, gpointer user_data) {
|
|||||||
state->grabbing_cursor = gdk_cursor_new_from_name(gdk_display, "grabbing");
|
state->grabbing_cursor = gdk_cursor_new_from_name(gdk_display, "grabbing");
|
||||||
state->move_cursor = gdk_cursor_new_from_name(gdk_display, "move");
|
state->move_cursor = gdk_cursor_new_from_name(gdk_display, "move");
|
||||||
|
|
||||||
GtkBuilder *builder = gtk_builder_new_from_resource("/wdisplays.ui");
|
GtkBuilder *builder = gtk_builder_new_from_resource(
|
||||||
|
WDISPLAYS_RESOURCE_PREFIX "/wdisplays.ui");
|
||||||
GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "heads_window"));
|
GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "heads_window"));
|
||||||
state->main_box = GTK_WIDGET(gtk_builder_get_object(builder, "main_box"));
|
state->main_box = GTK_WIDGET(gtk_builder_get_object(builder, "main_box"));
|
||||||
state->header_stack = GTK_WIDGET(gtk_builder_get_object(builder, "header_stack"));
|
state->header_stack = GTK_WIDGET(gtk_builder_get_object(builder, "header_stack"));
|
||||||
|
@ -15,10 +15,11 @@ executable(
|
|||||||
'wdisplays',
|
'wdisplays',
|
||||||
[
|
[
|
||||||
'main.c',
|
'main.c',
|
||||||
'outputs.c',
|
|
||||||
'render.c',
|
|
||||||
'glviewport.c',
|
'glviewport.c',
|
||||||
|
'headform.c',
|
||||||
|
'outputs.c',
|
||||||
'overlay.c',
|
'overlay.c',
|
||||||
|
'render.c',
|
||||||
resources,
|
resources,
|
||||||
],
|
],
|
||||||
dependencies : [
|
dependencies : [
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
|
|
||||||
|
#include "headform.h"
|
||||||
|
|
||||||
struct zxdg_output_v1;
|
struct zxdg_output_v1;
|
||||||
struct zxdg_output_manager_v1;
|
struct zxdg_output_manager_v1;
|
||||||
struct zwlr_output_mode_v1;
|
struct zwlr_output_mode_v1;
|
||||||
@ -39,18 +41,6 @@ typedef struct _GdkCursor GdkCursor;
|
|||||||
struct _cairo_surface;
|
struct _cairo_surface;
|
||||||
typedef struct _cairo_surface cairo_surface_t;
|
typedef struct _cairo_surface cairo_surface_t;
|
||||||
|
|
||||||
enum wd_head_fields {
|
|
||||||
WD_FIELD_NAME = 1 << 0,
|
|
||||||
WD_FIELD_ENABLED = 1 << 1,
|
|
||||||
WD_FIELD_DESCRIPTION = 1 << 2,
|
|
||||||
WD_FIELD_PHYSICAL_SIZE = 1 << 3,
|
|
||||||
WD_FIELD_SCALE = 1 << 4,
|
|
||||||
WD_FIELD_POSITION = 1 << 5,
|
|
||||||
WD_FIELD_MODE = 1 << 6,
|
|
||||||
WD_FIELD_TRANSFORM = 1 << 7,
|
|
||||||
WD_FIELDS_ALL = (1 << 8) - 1
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wd_output {
|
struct wd_output {
|
||||||
struct wd_state *state;
|
struct wd_state *state;
|
||||||
struct zxdg_output_v1 *xdg_output;
|
struct zxdg_output_v1 *xdg_output;
|
||||||
@ -301,7 +291,7 @@ void wd_ui_reset_heads(struct wd_state *state);
|
|||||||
* Updates the UI form for a single head. Useful for when the compositor
|
* Updates the UI form for a single head. Useful for when the compositor
|
||||||
* notifies us of updated configuration caused by another program.
|
* notifies us of updated configuration caused by another program.
|
||||||
*/
|
*/
|
||||||
void wd_ui_reset_head(const struct wd_head *head, unsigned int fields);
|
void wd_ui_reset_head(const struct wd_head *head, enum wd_head_fields fields);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Updates the stack and all forms to the last known server state.
|
* Updates the stack and all forms to the last known server state.
|
||||||
|
Loading…
Reference in New Issue
Block a user