@@ -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,17 +68,9 @@ 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 ) ) {
i f ( g_variant_get_int32 ( g_action_get_state ( rotate_action ) ) ! = get_rotate_value ( head - > transform ) ) {
return TRUE ;
}
break ;
}
}
bool flipped = head - > transform = = WL_OUTPUT_TRANSFORM_FLIPPED
| | head - > transform = = WL_OUTPUT_TRANSFORM_FLIPPED_90
| | head - > transform = = WL_OUTPUT_TRANSFORM_FLIPPED_180
@@ -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 ) {
switch ( rotate ) {
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 ;
}
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 ( 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 " ) ) ;
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 ) ;
}
}
}
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 " ) ;
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 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_mo de * mo de = g_object_get_data ( G_OBJECT ( action ) , " mo de" ) ;
GtkBuil der * buil der = GTK_BUILDER ( g_object_get_data( G_OBJECT ( form ) , " buil der " ) ) ;
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 - > refres h) ;
g_simple_action_set_state ( action , param ) ;
gtk_spin_button_set_value ( GTK_SPIN_BUTTON ( gtk_builder_get_object ( builder , " width " ) ) , mode . widt h) ;
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_op tion( 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_ac tion, 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_in ser t_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 ) ;
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 * st ate ) {
static void zoom_out ( GSimpleAction * action , GVariant * param , gpointer d ata ) {
struct wd_state * state = data ;
zoom_to ( state , state - > zoom * 0.75 ) ;
}
static void zoom_reset ( struct wd_state * st ate ) {
static void zoom_reset ( GSimpleAction * action , GVariant * param , gpointer d ata ) {
struct wd_state * state = data ;
zoom_to ( state , DEFAULT_ZOOM ) ;
}
static void zoom_in ( struct wd_state * st ate ) {
static void zoom_in ( GSimpleAction * action , GVariant * param , gpointer d ata ) {
struct wd_state * state = data ;
zoom_to ( state , state - > zoom / 0.75 ) ;
}
@@ -923,21 +904,20 @@ 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_offse t. x = event - > button . x - render - > x1 ;
state - > click_offse t. y = event - > button . y - render - > y1 ;
state - > drag_star t. x = mouse_x ;
state - > drag_star t. 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 ;
}
}
@@ -960,45 +940,16 @@ static gboolean canvas_click(GtkWidget *widget, GdkEvent *event,
}
}
}
} else if ( event - > button . button = = 2 ) {
state - > panning = TRUE ;
state - > pan_last . x = event - > button . x ;
state - > pan_last . y = event - > button . y ;
}
}
return TRUE ;
}
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 ) {
static void canvas_drag1_update ( GtkGestureDrag * drag ,
gdouble delta_x , gdouble delta_y , 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 ) {
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 ) {
@@ -1023,22 +974,27 @@ static gboolean canvas_motion(GtkWidget *widget, GdkEvent *event,
if ( rot & 1 ) {
SWAP ( int , size . x , size . y ) ;
}
struct wd_point tl = {
. x = ( event - > motion . x - state - > click_offset . x
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 = ( event - > motion . y - state - > click_offset . y
. 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 = {
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 & & ! ( event - > motion . 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 " ) ) ;
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 " ) ) ) ;
@@ -1088,24 +1044,63 @@ static gboolean canvas_motion(GtkWidget *widget, GdkEvent *event,
gtk_spin_button_set_value ( GTK_SPIN_BUTTON ( pos_y ) , new_pos . y ) ;
}
}
update_hovered ( state ) ;
return TRUE ;
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 gboolean canvas_enter ( GtkWidget * widget , GdkEvent * event ,
gpointer data ) {
static void canvas_drag2_begin ( GtkGestureDrag * drag ,
gdouble mouse_x , gdouble mouse_y , gpointer data ) {
struct wd_state * state = data ;
if ( ! ( event - > crossing . state & GDK_BUTTON1_MASK ) ) {
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 ,
static void canvas_leave ( GtkEventControllerMotion * controller ,
gpointer data ) {
struct wd_state * state = data ;
struct wd_render_head_data * render ;
@@ -1113,56 +1108,28 @@ static gboolean canvas_leave(GtkWidget *widget, GdkEvent *event,
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, " activ ate" , 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-st ate" , 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 , " activ ate" , G_CALLBACK ( capture_selected ) , state ) ;
g_signal_connect ( capture_action , " change-st ate" , 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 , " activ ate" , G_CALLBACK ( overlay_selected ) , state ) ;
g_signal_connect ( overlay_action , " change-st ate" , 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 ) ;