GCC Code Coverage Report


Directory: ./
File: panels/sound/cc-output-test-wheel.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 122 0.0%
Functions: 0 12 0.0%
Branches: 0 41 0.0%

Line Branch Exec Source
1 /*
2 * Copyright (C) 2018 Canonical Ltd.
3 * Copyright (C) 2022 Marco Melorio
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <glib/gi18n.h>
20
21 #include "cc-speaker-test-button.h"
22 #include "cc-output-test-wheel.h"
23
24 struct _CcOutputTestWheel
25 {
26 GtkWidget parent_instance;
27
28 GtkWidget *label;
29 GtkWidget *front_center_speaker_button;
30 GtkWidget *front_left_speaker_button;
31 GtkWidget *front_left_of_center_speaker_button;
32 GtkWidget *front_right_of_center_speaker_button;
33 GtkWidget *front_right_speaker_button;
34 GtkWidget *lfe_speaker_button;
35 GtkWidget *rear_center_speaker_button;
36 GtkWidget *rear_left_speaker_button;
37 GtkWidget *rear_right_speaker_button;
38 GtkWidget *side_left_speaker_button;
39 GtkWidget *side_right_speaker_button;
40
41 GSoundContext *context;
42 };
43
44 G_DEFINE_TYPE (CcOutputTestWheel, cc_output_test_wheel, GTK_TYPE_WIDGET)
45
46 static void
47 load_custom_css (CcOutputTestWheel *self)
48 {
49 g_autoptr(GtkCssProvider) provider = NULL;
50
51 provider = gtk_css_provider_new ();
52 gtk_css_provider_load_from_resource (provider, "/org/gnome/control-center/sound/output-test-wheel.css");
53 gtk_style_context_add_provider_for_display (gdk_display_get_default (),
54 GTK_STYLE_PROVIDER (provider),
55 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
56 }
57
58 static GtkWidget*
59 create_speaker_button (CcOutputTestWheel *self,
60 pa_channel_position_t position)
61 {
62 GtkWidget *button;
63
64 button = cc_speaker_test_button_new (self->context, position);
65 gtk_widget_add_css_class (button, "circular");
66 gtk_widget_add_css_class (button, "opaque");
67 gtk_widget_set_parent (button, GTK_WIDGET (self));
68
69 return button;
70 }
71
72 static void
73 allocate_speaker_button (GtkWidget *button,
74 int width,
75 int height,
76 double angle)
77 {
78 double rad, norm_x, norm_y;
79 GtkRequisition nat;
80 GtkAllocation allocation;
81
82 rad = angle * G_PI / 180;
83 norm_x = -(cos(rad) - 1) / 2;
84 norm_y = -(sin(rad) - 1) / 2;
85
86 gtk_widget_get_preferred_size (button, NULL, &nat);
87
88 allocation.width = nat.width;
89 allocation.height = nat.height;
90 allocation.x = (norm_x * width) - (allocation.width / 2);
91 allocation.y = (norm_y * height) - (allocation.height / 2);
92
93 gtk_widget_size_allocate (button, &allocation, -1);
94 }
95
96 static void
97 cc_output_test_wheel_measure (GtkWidget *widget,
98 GtkOrientation orientation,
99 int for_size,
100 int *minimum,
101 int *natural,
102 int *minimum_baseline,
103 int *natural_baseline)
104 {
105 CcOutputTestWheel *self = CC_OUTPUT_TEST_WHEEL (widget);
106
107 /* Just measure the label to make GTK not complain about allocating children
108 * without measuring anything. Measuring all the buttons is an overkill
109 * because we expect this widget to have the minimum size overwritten by CSS
110 * either way.
111 */
112 gtk_widget_measure (self->label, orientation, for_size,
113 minimum, natural, NULL, NULL);
114 }
115
116 static void
117 cc_output_test_wheel_size_allocate (GtkWidget *widget,
118 int width,
119 int height,
120 int baseline)
121 {
122 CcOutputTestWheel *self = CC_OUTPUT_TEST_WHEEL (widget);
123 GtkAllocation allocation;
124 GtkRequisition natural_size;
125
126 gtk_widget_allocate (self->label, width, height, baseline, NULL);
127
128 allocate_speaker_button (self->side_left_speaker_button, width, height, 0);
129 allocate_speaker_button (self->front_left_speaker_button, width, height, 45);
130 allocate_speaker_button (self->front_left_of_center_speaker_button, width, height, 67.5);
131 allocate_speaker_button (self->front_center_speaker_button, width, height, 90);
132 allocate_speaker_button (self->front_right_of_center_speaker_button, width, height, 112.5);
133 allocate_speaker_button (self->front_right_speaker_button, width, height, 135);
134 allocate_speaker_button (self->side_right_speaker_button, width, height, 180);
135 allocate_speaker_button (self->rear_right_speaker_button, width, height, 225);
136 allocate_speaker_button (self->rear_center_speaker_button, width, height, 270);
137 allocate_speaker_button (self->rear_left_speaker_button, width, height, 315);
138
139 gtk_widget_get_preferred_size(self->lfe_speaker_button, NULL, &natural_size);
140 allocation.width = natural_size.width;
141 allocation.height = natural_size.height;
142 allocation.x = (width / 2) - (allocation.width / 2);
143 allocation.y = (height * 0.2) - (allocation.height / 2);
144
145 gtk_widget_size_allocate (self->lfe_speaker_button, &allocation, -1);
146 }
147
148 static void
149 cc_output_test_wheel_dispose (GObject *object)
150 {
151 CcOutputTestWheel *self = CC_OUTPUT_TEST_WHEEL (object);
152
153 g_clear_pointer (&self->label, gtk_widget_unparent);
154
155 g_clear_pointer (&self->front_center_speaker_button, gtk_widget_unparent);
156 g_clear_pointer (&self->front_left_speaker_button, gtk_widget_unparent);
157 g_clear_pointer (&self->front_left_of_center_speaker_button, gtk_widget_unparent);
158 g_clear_pointer (&self->front_right_of_center_speaker_button, gtk_widget_unparent);
159 g_clear_pointer (&self->front_right_speaker_button, gtk_widget_unparent);
160 g_clear_pointer (&self->lfe_speaker_button, gtk_widget_unparent);
161 g_clear_pointer (&self->rear_center_speaker_button, gtk_widget_unparent);
162 g_clear_pointer (&self->rear_left_speaker_button, gtk_widget_unparent);
163 g_clear_pointer (&self->rear_right_speaker_button, gtk_widget_unparent);
164 g_clear_pointer (&self->side_left_speaker_button, gtk_widget_unparent);
165 g_clear_pointer (&self->side_right_speaker_button, gtk_widget_unparent);
166
167 g_clear_object (&self->context);
168
169 G_OBJECT_CLASS (cc_output_test_wheel_parent_class)->dispose (object);
170 }
171
172 void
173 cc_output_test_wheel_class_init (CcOutputTestWheelClass *klass)
174 {
175 GObjectClass *object_class = G_OBJECT_CLASS (klass);
176 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
177
178 object_class->dispose = cc_output_test_wheel_dispose;
179
180 widget_class->measure = cc_output_test_wheel_measure;
181 widget_class->size_allocate = cc_output_test_wheel_size_allocate;
182
183 gtk_widget_class_set_css_name (widget_class, "wheel");
184 }
185
186 void
187 cc_output_test_wheel_init (CcOutputTestWheel *self)
188 {
189 GtkSettings *settings;
190 g_autofree gchar *theme_name = NULL;
191
192 self->context = gsound_context_new (NULL, NULL);
193 gsound_context_set_driver (self->context, "pulse", NULL);
194 gsound_context_set_attributes (self->context, NULL,
195 GSOUND_ATTR_APPLICATION_ID, "org.gnome.VolumeControl",
196 NULL);
197 settings = gtk_settings_get_for_display (gdk_display_get_default ());
198 g_object_get (G_OBJECT (settings),
199 "gtk-sound-theme-name", &theme_name,
200 NULL);
201 if (theme_name != NULL)
202 gsound_context_set_attributes (self->context, NULL,
203 GSOUND_ATTR_CANBERRA_XDG_THEME_NAME, theme_name,
204 NULL);
205
206 self->label = gtk_inscription_new (_("Select a Speaker"));
207 gtk_inscription_set_xalign (GTK_INSCRIPTION (self->label), 0.5);
208 gtk_widget_add_css_class (self->label, "heading");
209 gtk_widget_set_parent (self->label, GTK_WIDGET (self));
210
211 self->front_center_speaker_button = create_speaker_button (self, PA_CHANNEL_POSITION_FRONT_CENTER);
212 self->front_left_speaker_button = create_speaker_button (self, PA_CHANNEL_POSITION_FRONT_LEFT);
213 self->front_left_of_center_speaker_button = create_speaker_button (self, PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER);
214 self->front_right_of_center_speaker_button = create_speaker_button (self, PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER);
215 self->front_right_speaker_button = create_speaker_button (self, PA_CHANNEL_POSITION_FRONT_RIGHT);
216 self->lfe_speaker_button = create_speaker_button (self, PA_CHANNEL_POSITION_LFE);
217 self->rear_center_speaker_button = create_speaker_button (self, PA_CHANNEL_POSITION_REAR_CENTER);
218 self->rear_left_speaker_button = create_speaker_button (self, PA_CHANNEL_POSITION_REAR_LEFT);
219 self->rear_right_speaker_button = create_speaker_button (self, PA_CHANNEL_POSITION_REAR_RIGHT);
220 self->side_left_speaker_button = create_speaker_button (self, PA_CHANNEL_POSITION_SIDE_LEFT);
221 self->side_right_speaker_button = create_speaker_button (self, PA_CHANNEL_POSITION_SIDE_RIGHT);
222
223 load_custom_css (self);
224 }
225
226 void
227 cc_output_test_wheel_set_stream (CcOutputTestWheel *self,
228 GvcMixerStream *stream)
229 {
230 const GvcChannelMap *map = gvc_mixer_stream_get_channel_map (stream);
231
232 gtk_widget_set_visible (self->front_left_speaker_button, gvc_channel_map_has_position (map, PA_CHANNEL_POSITION_FRONT_LEFT));
233 gtk_widget_set_visible (self->front_left_of_center_speaker_button, gvc_channel_map_has_position (map, PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER));
234 gtk_widget_set_visible (self->front_right_of_center_speaker_button, gvc_channel_map_has_position (map, PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER));
235 gtk_widget_set_visible (self->front_right_speaker_button, gvc_channel_map_has_position (map, PA_CHANNEL_POSITION_FRONT_RIGHT));
236 gtk_widget_set_visible (self->lfe_speaker_button, gvc_channel_map_has_position (map, PA_CHANNEL_POSITION_LFE));
237 gtk_widget_set_visible (self->rear_center_speaker_button, gvc_channel_map_has_position (map, PA_CHANNEL_POSITION_REAR_CENTER));
238 gtk_widget_set_visible (self->rear_left_speaker_button, gvc_channel_map_has_position (map, PA_CHANNEL_POSITION_REAR_LEFT));
239 gtk_widget_set_visible (self->rear_right_speaker_button, gvc_channel_map_has_position (map, PA_CHANNEL_POSITION_REAR_RIGHT));
240 gtk_widget_set_visible (self->side_left_speaker_button, gvc_channel_map_has_position (map, PA_CHANNEL_POSITION_SIDE_LEFT));
241 gtk_widget_set_visible (self->side_right_speaker_button, gvc_channel_map_has_position (map, PA_CHANNEL_POSITION_SIDE_RIGHT));
242
243 /* Replace the center channel with a mono channel */
244 if (gvc_channel_map_has_position (map, PA_CHANNEL_POSITION_MONO))
245 {
246 if (gvc_channel_map_has_position (map, PA_CHANNEL_POSITION_FRONT_CENTER))
247 g_warning ("Testing output with both front center and mono channels - front center is hidden");
248
249 cc_speaker_test_button_set_channel_position (CC_SPEAKER_TEST_BUTTON (self->front_center_speaker_button),
250 PA_CHANNEL_POSITION_MONO);
251 gtk_widget_set_visible (GTK_WIDGET (self->front_center_speaker_button), TRUE);
252 }
253 else if (gvc_channel_map_has_position (map, PA_CHANNEL_POSITION_FRONT_CENTER))
254 {
255 cc_speaker_test_button_set_channel_position (CC_SPEAKER_TEST_BUTTON (self->front_center_speaker_button),
256 PA_CHANNEL_POSITION_FRONT_CENTER);
257 gtk_widget_set_visible (GTK_WIDGET (self->front_center_speaker_button), TRUE);
258 }
259 else
260 {
261 gtk_widget_set_visible (GTK_WIDGET (self->front_center_speaker_button), FALSE);
262 }
263 }
264