]> code.delx.au - pulseaudio/blob - src/modules/module-switch-on-port-available.c
alsa-mixer: Make speaker get available=no when headphones are plugged in
[pulseaudio] / src / modules / module-switch-on-port-available.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2006 Lennart Poettering
5 Copyright 2011 Canonical Ltd
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <pulsecore/core.h>
28 #include <pulsecore/device-port.h>
29 #include <pulsecore/hashmap.h>
30
31 #include "module-switch-on-port-available-symdef.h"
32
33 struct userdata {
34 pa_hook_slot *callback_slot;
35 };
36
37 static pa_device_port* find_best_port(pa_hashmap *ports) {
38 void *state;
39 pa_device_port* port, *result = NULL;
40
41 PA_HASHMAP_FOREACH(port, ports, state) {
42 if (result == NULL ||
43 result->available == PA_PORT_AVAILABLE_NO ||
44 (port->available != PA_PORT_AVAILABLE_NO && port->priority > result->priority)) {
45 result = port;
46 }
47 }
48
49 return result;
50 }
51
52 static pa_bool_t try_to_switch_profile(pa_card *card, pa_device_port *port) {
53 pa_card_profile *best_profile = NULL, *profile;
54 void *state;
55
56 pa_log_debug("Finding best profile");
57
58 if (port->profiles)
59 PA_HASHMAP_FOREACH(profile, port->profiles, state) {
60 if (best_profile && best_profile->priority >= profile->priority)
61 continue;
62
63 /* We make a best effort to keep other direction unchanged */
64 if (card->active_profile && !port->is_input) {
65 if (card->active_profile->n_sources != profile->n_sources)
66 continue;
67
68 if (card->active_profile->max_source_channels != profile->max_source_channels)
69 continue;
70 }
71
72 if (card->active_profile && !port->is_output) {
73 if (card->active_profile->n_sinks != profile->n_sinks)
74 continue;
75
76 if (card->active_profile->max_sink_channels != profile->max_sink_channels)
77 continue;
78 }
79
80 best_profile = profile;
81 }
82
83 if (!best_profile) {
84 pa_log_debug("No suitable profile found");
85 return FALSE;
86 }
87
88 if (pa_card_set_profile(card, best_profile->name, FALSE) != 0) {
89 pa_log_debug("Could not set profile %s", best_profile->name);
90 return FALSE;
91 }
92
93 return TRUE;
94 }
95
96 static void find_sink_and_source(pa_card *card, pa_device_port *port, pa_sink **si, pa_source **so)
97 {
98 pa_sink *sink = NULL;
99 pa_source *source = NULL;
100 uint32_t state;
101
102 if (port->is_output)
103 PA_IDXSET_FOREACH(sink, card->sinks, state)
104 if (sink->ports && port == pa_hashmap_get(sink->ports, port->name))
105 break;
106
107 if (port->is_input)
108 PA_IDXSET_FOREACH(source, card->sources, state)
109 if (source->ports && port == pa_hashmap_get(source->ports, port->name))
110 break;
111
112 *si = sink;
113 *so = source;
114 }
115
116 static pa_hook_result_t port_available_hook_callback(pa_core *c, pa_device_port *port, void* userdata) {
117 uint32_t state;
118 pa_card* card;
119 pa_sink *sink;
120 pa_source *source;
121 pa_bool_t is_active_profile, is_active_port;
122
123 if (port->available == PA_PORT_AVAILABLE_UNKNOWN)
124 return PA_HOOK_OK;
125
126 pa_log_debug("finding port %s", port->name);
127
128 PA_IDXSET_FOREACH(card, c->cards, state)
129 if (card->ports && port == pa_hashmap_get(card->ports, port->name))
130 break;
131
132 if (!card) {
133 pa_log_warn("Did not find port %s in array of cards", port->name);
134 return PA_HOOK_OK;
135 }
136
137 find_sink_and_source(card, port, &sink, &source);
138
139 is_active_profile = port->profiles && card->active_profile &&
140 card->active_profile == pa_hashmap_get(port->profiles, card->active_profile->name);
141 is_active_port = (sink && sink->active_port == port) || (source && source->active_port == port);
142
143 if (port->available == PA_PORT_AVAILABLE_NO && !is_active_port)
144 return PA_HOOK_OK;
145
146 if (port->available == PA_PORT_AVAILABLE_YES) {
147 if (is_active_port)
148 return PA_HOOK_OK;
149
150 if (!is_active_profile) {
151 if (!try_to_switch_profile(card, port))
152 return PA_HOOK_OK;
153
154 pa_assert(card->active_profile == pa_hashmap_get(port->profiles, card->active_profile->name));
155
156 /* Now that profile has changed, our sink and source pointers must be updated */
157 find_sink_and_source(card, port, &sink, &source);
158 }
159
160 if (source)
161 pa_source_set_port(source, port->name, FALSE);
162 if (sink)
163 pa_sink_set_port(sink, port->name, FALSE);
164 }
165
166 if (port->available == PA_PORT_AVAILABLE_NO) {
167 if (sink) {
168 pa_device_port *p2 = find_best_port(sink->ports);
169
170 if (p2 && p2->available != PA_PORT_AVAILABLE_NO)
171 pa_sink_set_port(sink, p2->name, FALSE);
172 else {
173 /* Maybe try to switch to another profile? */
174 }
175 }
176
177 if (source) {
178 pa_device_port *p2 = find_best_port(source->ports);
179
180 if (p2 && p2->available != PA_PORT_AVAILABLE_NO)
181 pa_source_set_port(source, p2->name, FALSE);
182 else {
183 /* Maybe try to switch to another profile? */
184 }
185 }
186 }
187
188 return PA_HOOK_OK;
189 }
190
191 static void handle_all_unavailable(pa_core *core) {
192 pa_card *card;
193 uint32_t state;
194
195 PA_IDXSET_FOREACH(card, core->cards, state) {
196 pa_device_port *port;
197 void *state2;
198
199 if (!card->ports)
200 continue;
201
202 PA_HASHMAP_FOREACH(port, card->ports, state2) {
203 if (port->available == PA_PORT_AVAILABLE_NO)
204 port_available_hook_callback(core, port, NULL);
205 }
206 }
207 }
208
209 int pa__init(pa_module*m) {
210 struct userdata *u;
211
212 pa_assert(m);
213
214 m->userdata = u = pa_xnew(struct userdata, 1);
215
216 u->callback_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED],
217 PA_HOOK_LATE, (pa_hook_cb_t) port_available_hook_callback, u);
218
219 handle_all_unavailable(m->core);
220
221 return 0;
222 }
223
224 void pa__done(pa_module*m) {
225 struct userdata *u;
226
227 pa_assert(m);
228
229 if (!(u = m->userdata))
230 return;
231
232 if (u->callback_slot)
233 pa_hook_slot_free(u->callback_slot);
234
235 pa_xfree(u);
236 }