Sun, 28 Aug 2022 21:53:47 -0500
Set volume on audio test pipeline at startup
If you change the volume slider in prefs, then the Test Audio button does not respect that initially. If you move the slider around with the test playing, then the volume jumps to whatever you have. But stopping and starting the test again goes to full volume.
Testing Done:
Opened prefs, set to test source, made volume really low, and hit Test Audio. Output volume was low immediately.
Reviewed at https://reviews.imfreedom.org/r/1670/
| 41320 | 1 | /* |
| 2 | * Pidgin - Internet Messenger | |
| 3 | * Copyright (C) Pidgin Developers <devel@pidgin.im> | |
| 4 | * | |
| 5 | * Pidgin is the legal property of its developers, whose names are too numerous | |
| 6 | * to list here. Please refer to the COPYRIGHT file distributed with this | |
| 7 | * source distribution. | |
| 8 | * | |
| 9 | * This program is free software; you can redistribute it and/or modify | |
| 10 | * it under the terms of the GNU General Public License as published by | |
| 11 | * the Free Software Foundation; either version 2 of the License, or | |
| 12 | * (at your option) any later version. | |
| 13 | * | |
| 14 | * This program is distributed in the hope that it will be useful, | |
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 17 | * GNU General Public License for more details. | |
| 18 | * | |
| 19 | * You should have received a copy of the GNU General Public License | |
| 20 | * along with this program; if not, see <https://www.gnu.org/licenses/>. | |
| 21 | */ | |
| 22 | ||
| 23 | #include <glib/gi18n-lib.h> | |
| 24 | ||
| 25 | #include <gtk/gtk.h> | |
| 26 | ||
| 27 | #include <purple.h> | |
| 28 | ||
| 29 | #include "pidginstatusbox.h" | |
| 30 | #include "pidginiconname.h" | |
| 31 | ||
| 32 | #define PRIMITIVE_FORMAT "primitive_%d" | |
| 33 | #define SAVEDSTATUS_FORMAT "savedstatus_%lu" | |
| 34 | ||
| 35 | typedef enum { | |
| 36 | PIDGIN_STATUS_BOX_TYPE_SEPARATOR, | |
| 37 | PIDGIN_STATUS_BOX_TYPE_PRIMITIVE, | |
| 38 | PIDGIN_STATUS_BOX_TYPE_POPULAR, | |
| 39 | PIDGIN_STATUS_BOX_TYPE_ACTION, | |
| 40 | PIDGIN_STATUS_BOX_NUM_TYPES | |
| 41 | } PidginStatusBoxItemType; | |
| 42 | ||
| 43 | struct _PidginStatusBox { | |
| 44 | GtkBox parent; | |
| 45 | ||
| 46 | GtkListStore *model; | |
| 47 | GtkWidget *combo; | |
| 48 | ||
| 49 | /* This is used to flipback to the correct status when one of the actions | |
| 50 | * items is selected. | |
| 51 | */ | |
| 52 | gchar *active_id; | |
| 53 | }; | |
| 54 | ||
| 55 | enum { | |
| 56 | ID_COLUMN, | |
| 57 | TYPE_COLUMN, /* PidginStatusBoxItemType */ | |
| 58 | ICON_NAME_COLUMN, | |
| 59 | PRIMITIVE_COLUMN, | |
| 60 | TEXT_COLUMN, | |
| 61 | /* This value depends on TYPE_COLUMN. For POPULAR types, this is the | |
| 62 | * creation time. | |
| 63 | */ | |
| 64 | DATA_COLUMN, | |
| 65 | EMBLEM_VISIBLE_COLUMN, | |
| 66 | NUM_COLUMNS | |
| 67 | }; | |
| 68 | ||
| 69 | G_DEFINE_TYPE(PidginStatusBox, pidgin_status_box, GTK_TYPE_BOX) | |
| 70 | ||
| 71 | /* This prototype is necessary so we can block this signal handler when we are | |
| 72 | * manually updating the combobox. | |
| 73 | */ | |
| 74 | static void pidgin_status_box_combo_changed_cb(GtkComboBox *combo, gpointer user_data); | |
| 75 | ||
| 76 | /****************************************************************************** | |
| 77 | * Helpers | |
| 78 | *****************************************************************************/ | |
| 79 | static void | |
| 80 | pidgin_status_box_update_to_status(PidginStatusBox *status_box, | |
| 81 | PurpleSavedStatus *status) | |
| 82 | { | |
| 83 | gchar *id = NULL; | |
| 84 | gboolean set = FALSE; | |
| 85 | time_t creation_time = 0; | |
| 86 | ||
| 87 | /* Try to set the combo box to the saved status. */ | |
| 88 | creation_time = purple_savedstatus_get_creation_time(status); | |
| 89 | id = g_strdup_printf(SAVEDSTATUS_FORMAT, creation_time); | |
| 90 | ||
| 91 | set = gtk_combo_box_set_active_id(GTK_COMBO_BOX(status_box->combo), id); | |
| 92 | g_free(id); | |
| 93 | ||
| 94 | /* If we failed to set via the savedstatus, fallback to the primitive. */ | |
| 95 | if(!set) { | |
| 96 | PurpleStatusPrimitive primitive; | |
| 97 | ||
| 98 | primitive = purple_savedstatus_get_primitive_type(status); | |
| 99 | id = g_strdup_printf(PRIMITIVE_FORMAT, primitive); | |
| 100 | ||
|
41337
a8b8e1703239
Remove a dead assignment that scanbuild found
Gary Kramlich <grim@reaperworld.com>
parents:
41320
diff
changeset
|
101 | gtk_combo_box_set_active_id(GTK_COMBO_BOX(status_box->combo), id); |
| 41320 | 102 | g_free(id); |
| 103 | } | |
| 104 | } | |
| 105 | ||
| 106 | static void | |
| 107 | pidgin_status_box_add(PidginStatusBox *status_box, | |
| 108 | PidginStatusBoxItemType type, | |
| 109 | PurpleStatusPrimitive primitive, const gchar *text, | |
| 110 | gpointer data) | |
| 111 | { | |
| 112 | GtkTreeIter iter; | |
| 113 | gchar *id = NULL, *escaped_text = NULL; | |
| 114 | const gchar *icon_name = NULL; | |
| 115 | gboolean emblem_visible = FALSE; | |
| 116 | ||
| 117 | escaped_text = g_markup_escape_text(text, -1); | |
| 118 | ||
| 119 | if(type == PIDGIN_STATUS_BOX_TYPE_POPULAR) { | |
| 120 | PurpleSavedStatus *saved_status = NULL; | |
| 121 | time_t creation_time = GPOINTER_TO_INT(data); | |
| 122 | ||
| 123 | saved_status = purple_savedstatus_find_by_creation_time(creation_time); | |
| 124 | ||
| 125 | if(saved_status != NULL) { | |
| 126 | id = g_strdup_printf(SAVEDSTATUS_FORMAT, creation_time); | |
| 127 | ||
| 128 | if(!purple_savedstatus_is_transient(saved_status)) { | |
| 129 | emblem_visible = TRUE; | |
| 130 | } | |
| 131 | } | |
| 132 | } | |
| 133 | ||
| 134 | if(id == NULL && primitive != PURPLE_STATUS_UNSET) { | |
| 135 | id = g_strdup_printf(PRIMITIVE_FORMAT, primitive); | |
| 136 | } | |
| 137 | ||
| 138 | icon_name = pidgin_icon_name_from_status_primitive(primitive, NULL); | |
| 139 | ||
| 140 | gtk_list_store_append(status_box->model, &iter); | |
| 141 | gtk_list_store_set(status_box->model, &iter, | |
| 142 | ID_COLUMN, id, | |
| 143 | TYPE_COLUMN, type, | |
| 144 | ICON_NAME_COLUMN, icon_name, | |
| 145 | PRIMITIVE_COLUMN, primitive, | |
| 146 | TEXT_COLUMN, escaped_text, | |
| 147 | DATA_COLUMN, data, | |
| 148 | EMBLEM_VISIBLE_COLUMN, emblem_visible, | |
| 149 | -1); | |
| 150 | ||
| 151 | g_free(escaped_text); | |
| 152 | g_free(id); | |
| 153 | } | |
| 154 | ||
| 155 | static gboolean | |
| 156 | pidgin_status_box_row_separator_func(GtkTreeModel *model, GtkTreeIter *iter, | |
| 157 | G_GNUC_UNUSED gpointer data) | |
| 158 | { | |
| 159 | PidginStatusBoxItemType type; | |
| 160 | ||
| 161 | gtk_tree_model_get(model, iter, TYPE_COLUMN, &type, -1); | |
| 162 | ||
| 163 | return type == PIDGIN_STATUS_BOX_TYPE_SEPARATOR; | |
| 164 | } | |
| 165 | ||
| 166 | static void | |
| 167 | pidgin_status_box_add_separator(PidginStatusBox *status_box) { | |
| 168 | GtkTreeIter iter; | |
| 169 | ||
| 170 | gtk_list_store_append(status_box->model, &iter); | |
| 171 | gtk_list_store_set(status_box->model, &iter, | |
| 172 | TYPE_COLUMN, PIDGIN_STATUS_BOX_TYPE_SEPARATOR, | |
| 173 | -1); | |
| 174 | } | |
| 175 | ||
| 176 | static void | |
| 177 | pidgin_status_box_update_saved_statuses(PidginStatusBox *status_box) { | |
| 178 | GList *list, *cur; | |
| 179 | ||
| 180 | list = purple_savedstatuses_get_popular(6); | |
| 181 | if (list == NULL) { | |
| 182 | /* Odd... oh well, nothing we can do about it. */ | |
| 183 | return; | |
| 184 | } | |
| 185 | ||
| 186 | for(cur = list; cur != NULL; cur = cur->next) { | |
| 187 | PurpleSavedStatus *saved = cur->data; | |
| 188 | GString *text = NULL; | |
| 189 | time_t creation_time; | |
| 190 | ||
| 191 | text = g_string_new(purple_savedstatus_get_title(saved)); | |
| 192 | ||
| 193 | if(!purple_savedstatus_is_transient(saved)) { | |
| 194 | /* | |
| 195 | * Transient statuses do not have a title, so the savedstatus | |
| 196 | * API returns the message when purple_savedstatus_get_title() is | |
| 197 | * called, so we don't need to get the message a second time. | |
| 198 | */ | |
| 199 | const gchar *message = NULL; | |
| 200 | ||
| 201 | message = purple_savedstatus_get_message(saved); | |
| 202 | if(message != NULL) { | |
| 203 | gchar *stripped = purple_markup_strip_html(message); | |
| 204 | ||
| 205 | purple_util_chrreplace(stripped, '\n', ' '); | |
| 206 | g_string_append_printf(text, " - %s", stripped); | |
| 207 | g_free(stripped); | |
| 208 | } | |
| 209 | } | |
| 210 | ||
| 211 | creation_time = purple_savedstatus_get_creation_time(saved); | |
| 212 | pidgin_status_box_add(status_box, PIDGIN_STATUS_BOX_TYPE_POPULAR, | |
| 213 | purple_savedstatus_get_primitive_type(saved), | |
| 214 | text->str, GINT_TO_POINTER(creation_time)); | |
| 215 | ||
| 216 | g_string_free(text, TRUE); | |
| 217 | } | |
| 218 | ||
| 219 | g_list_free(list); | |
| 220 | ||
| 221 | pidgin_status_box_add_separator(status_box); | |
| 222 | } | |
| 223 | ||
| 224 | static void | |
| 225 | pidgin_status_box_populate(PidginStatusBox *status_box) { | |
| 226 | pidgin_status_box_add(status_box, PIDGIN_STATUS_BOX_TYPE_PRIMITIVE, | |
| 227 | PURPLE_STATUS_AVAILABLE, _("Available"), NULL); | |
| 228 | pidgin_status_box_add(status_box, PIDGIN_STATUS_BOX_TYPE_PRIMITIVE, | |
| 229 | PURPLE_STATUS_AWAY, _("Away"), NULL); | |
| 230 | pidgin_status_box_add(status_box, PIDGIN_STATUS_BOX_TYPE_PRIMITIVE, | |
| 231 | PURPLE_STATUS_UNAVAILABLE, _("Do not disturb"), | |
| 232 | NULL); | |
| 233 | pidgin_status_box_add(status_box, PIDGIN_STATUS_BOX_TYPE_PRIMITIVE, | |
| 234 | PURPLE_STATUS_INVISIBLE, _("Invisible"), NULL); | |
| 235 | pidgin_status_box_add(status_box, PIDGIN_STATUS_BOX_TYPE_PRIMITIVE, | |
| 236 | PURPLE_STATUS_OFFLINE, _("Offline"), NULL); | |
| 237 | ||
| 238 | pidgin_status_box_add_separator(status_box); | |
| 239 | ||
| 240 | pidgin_status_box_update_saved_statuses(status_box); | |
| 241 | ||
| 242 | pidgin_status_box_add(status_box, PIDGIN_STATUS_BOX_TYPE_ACTION, | |
| 243 | PURPLE_STATUS_UNSET, _("New Status..."), | |
| 244 | "new-status"); | |
| 245 | pidgin_status_box_add(status_box, PIDGIN_STATUS_BOX_TYPE_ACTION, | |
| 246 | PURPLE_STATUS_UNSET, _("Saved Statuses..."), | |
| 247 | "status-manager"); | |
| 248 | } | |
| 249 | ||
| 250 | /****************************************************************************** | |
| 251 | * Callbacks | |
| 252 | *****************************************************************************/ | |
| 253 | static void | |
| 254 | pidgin_status_box_combo_changed_cb(GtkComboBox *combo, gpointer user_data) { | |
| 255 | PidginStatusBox *status_box = user_data; | |
| 256 | PidginStatusBoxItemType type; | |
| 257 | PurpleSavedStatus *saved_status = NULL; | |
| 258 | PurpleStatusPrimitive primitive; | |
| 259 | GtkTreeIter iter; | |
| 260 | gchar *id = NULL; | |
| 261 | gpointer data; | |
| 262 | ||
| 263 | if(!gtk_combo_box_get_active_iter(combo, &iter)) { | |
| 264 | return; | |
| 265 | } | |
| 266 | ||
| 267 | gtk_tree_model_get(GTK_TREE_MODEL(status_box->model), &iter, | |
| 268 | ID_COLUMN, &id, | |
| 269 | TYPE_COLUMN, &type, | |
| 270 | PRIMITIVE_COLUMN, &primitive, | |
| 271 | DATA_COLUMN, &data, | |
| 272 | -1); | |
| 273 | ||
| 274 | if(type == PIDGIN_STATUS_BOX_TYPE_PRIMITIVE) { | |
| 275 | saved_status = purple_savedstatus_find_transient_by_type_and_message(primitive, NULL); | |
| 276 | if(saved_status == NULL) { | |
| 277 | saved_status = purple_savedstatus_new(NULL, primitive); | |
| 278 | } | |
| 279 | } else if(type == PIDGIN_STATUS_BOX_TYPE_POPULAR) { | |
| 280 | time_t creation_time = GPOINTER_TO_INT(data); | |
| 281 | ||
| 282 | saved_status = purple_savedstatus_find_by_creation_time(creation_time); | |
| 283 | } else if(type == PIDGIN_STATUS_BOX_TYPE_ACTION) { | |
| 284 | GApplication *application = NULL; | |
| 285 | const gchar *action_name = (const gchar *)data; | |
| 286 | ||
| 287 | application = g_application_get_default(); | |
| 288 | ||
| 289 | g_action_group_activate_action(G_ACTION_GROUP(application), | |
| 290 | action_name, NULL); | |
| 291 | ||
| 292 | gtk_combo_box_set_active_id(combo, status_box->active_id); | |
| 293 | } | |
| 294 | ||
| 295 | if(saved_status != NULL) { | |
| 296 | if(saved_status != purple_savedstatus_get_current()) { | |
| 297 | purple_savedstatus_activate(saved_status); | |
| 298 | } | |
| 299 | ||
| 300 | g_free(status_box->active_id); | |
| 301 | status_box->active_id = id; | |
| 302 | } else { | |
| 303 | g_free(id); | |
| 304 | } | |
| 305 | } | |
| 306 | ||
| 307 | static void | |
| 308 | pidgin_status_box_savedstatus_changed_cb(PurpleSavedStatus *now, | |
| 309 | G_GNUC_UNUSED PurpleSavedStatus *old, | |
| 310 | gpointer data) | |
| 311 | { | |
| 312 | PidginStatusBox *status_box = data; | |
| 313 | ||
| 314 | /* If we don't have a status, we have to bail. */ | |
| 315 | if(now == NULL) { | |
| 316 | return; | |
| 317 | } | |
| 318 | ||
| 319 | pidgin_status_box_update_to_status(status_box, now); | |
| 320 | } | |
| 321 | ||
| 322 | static void | |
| 323 | pidgin_status_box_savedstatus_updated_cb(G_GNUC_UNUSED PurpleSavedStatus *status, | |
| 324 | gpointer data) | |
| 325 | { | |
| 326 | PidginStatusBox *status_box = data; | |
| 327 | PurpleSavedStatus *current = NULL; | |
| 328 | static gboolean getting_current = FALSE; | |
| 329 | ||
| 330 | /* purple_status_get_current will create a new status if this is a brand | |
| 331 | * new install or the setting wasn't found. This leads to this handler | |
| 332 | * getting stuck in a loop until it segfaults because the stack smashed | |
| 333 | * into the heap. Anyways, we use this static boolean to check when this | |
| 334 | * function is called by purple_status_get_current so we can bail out and | |
| 335 | * break the loop. | |
| 336 | */ | |
| 337 | if(getting_current) { | |
| 338 | return; | |
| 339 | } | |
| 340 | ||
| 341 | gtk_list_store_clear(status_box->model); | |
| 342 | pidgin_status_box_populate(status_box); | |
| 343 | ||
| 344 | getting_current = TRUE; | |
| 345 | current = purple_savedstatus_get_current(); | |
| 346 | pidgin_status_box_update_to_status(status_box, current); | |
| 347 | getting_current = FALSE; | |
| 348 | } | |
| 349 | ||
| 350 | /****************************************************************************** | |
| 351 | * GObject Implementation | |
| 352 | *****************************************************************************/ | |
| 353 | static void | |
| 354 | pidgin_status_box_finalize(GObject *obj) { | |
| 355 | PidginStatusBox *status_box = PIDGIN_STATUS_BOX(obj); | |
| 356 | ||
| 357 | purple_signals_disconnect_by_handle(status_box); | |
| 358 | ||
| 359 | g_free(status_box->active_id); | |
| 360 | ||
| 361 | G_OBJECT_CLASS(pidgin_status_box_parent_class)->finalize(obj); | |
| 362 | } | |
| 363 | ||
| 364 | static void | |
| 365 | pidgin_status_box_init(PidginStatusBox *status_box) { | |
| 366 | gpointer handle; | |
| 367 | ||
| 368 | gtk_widget_init_template(GTK_WIDGET(status_box)); | |
| 369 | ||
| 370 | gtk_combo_box_set_row_separator_func(GTK_COMBO_BOX(status_box->combo), | |
| 371 | pidgin_status_box_row_separator_func, | |
| 372 | NULL, NULL); | |
| 373 | ||
| 374 | pidgin_status_box_populate(status_box); | |
| 375 | ||
| 376 | handle = purple_savedstatuses_get_handle(); | |
| 377 | purple_signal_connect(handle, "savedstatus-changed", status_box, | |
| 378 | G_CALLBACK(pidgin_status_box_savedstatus_changed_cb), | |
| 379 | status_box); | |
| 380 | purple_signal_connect(handle, "savedstatus-added", status_box, | |
| 381 | G_CALLBACK(pidgin_status_box_savedstatus_updated_cb), | |
| 382 | status_box); | |
| 383 | purple_signal_connect(handle, "savedstatus-deleted", status_box, | |
| 384 | G_CALLBACK(pidgin_status_box_savedstatus_updated_cb), | |
| 385 | status_box); | |
| 386 | purple_signal_connect(handle, "savedstatus-modified", status_box, | |
| 387 | G_CALLBACK(pidgin_status_box_savedstatus_updated_cb), | |
| 388 | status_box); | |
| 389 | } | |
| 390 | ||
| 391 | static void | |
| 392 | pidgin_status_box_constructed(GObject *obj) { | |
| 393 | PurpleSavedStatus *status = NULL; | |
| 394 | ||
| 395 | G_OBJECT_CLASS(pidgin_status_box_parent_class)->constructed(obj); | |
| 396 | ||
| 397 | status = purple_savedstatus_get_current(); | |
| 398 | ||
| 399 | pidgin_status_box_update_to_status(PIDGIN_STATUS_BOX(obj), status); | |
| 400 | } | |
| 401 | ||
| 402 | static void | |
| 403 | pidgin_status_box_class_init(PidginStatusBoxClass *klass) { | |
| 404 | GObjectClass *obj_class = G_OBJECT_CLASS(klass); | |
| 405 | GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); | |
| 406 | ||
| 407 | obj_class->finalize = pidgin_status_box_finalize; | |
| 408 | obj_class->constructed = pidgin_status_box_constructed; | |
| 409 | ||
| 410 | gtk_widget_class_set_template_from_resource( | |
| 411 | widget_class, | |
| 412 | "/im/pidgin/Pidgin3/Status/box.ui" | |
| 413 | ); | |
| 414 | ||
| 415 | gtk_widget_class_bind_template_child(widget_class, PidginStatusBox, | |
| 416 | model); | |
| 417 | gtk_widget_class_bind_template_child(widget_class, PidginStatusBox, | |
| 418 | combo); | |
| 419 | ||
| 420 | gtk_widget_class_bind_template_callback(widget_class, | |
| 421 | pidgin_status_box_combo_changed_cb); | |
| 422 | } | |
| 423 | ||
| 424 | /****************************************************************************** | |
| 425 | * Public API | |
| 426 | *****************************************************************************/ | |
| 427 | GtkWidget * | |
| 428 | pidgin_status_box_new(void) { | |
| 429 | return g_object_new(PIDGIN_TYPE_STATUS_BOX, NULL); | |
| 430 | } |