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 |
|
|
|