card_select.c (5786B)
1 /* 2 * card_select.c - select a card by list or device name 3 * Copyright (c) Clemens Ladisch <clemens@ladisch.de> 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU 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 "aconfig.h" 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <assert.h> 23 #include <alsa/asoundlib.h> 24 #include <menu.h> 25 #include "gettext_curses.h" 26 #include "die.h" 27 #include "mem.h" 28 #include "utils.h" 29 #include "colors.h" 30 #include "widget.h" 31 #include "mixer_widget.h" 32 #include "device_name.h" 33 #include "card_select.h" 34 35 struct card { 36 struct card *next; 37 char *indexstr; 38 char *name; 39 char *device_name; 40 }; 41 42 static struct widget list_widget; 43 static struct card first_card; 44 static ITEM **items; 45 static MENU *menu; 46 static ITEM *initial_item; 47 48 static void on_key_enter(void) 49 { 50 ITEM *item = current_item(menu); 51 if (item) { 52 struct card *card = item_userptr(item); 53 if (card->device_name) { 54 if (select_card_by_name(card->device_name)) 55 list_widget.close(); 56 } else { 57 create_device_name_form(); 58 } 59 } 60 } 61 62 static void on_menu_key(int key) 63 { 64 static const struct { 65 int key; 66 int request; 67 } key_map[] = { 68 { KEY_DOWN, REQ_DOWN_ITEM }, 69 { KEY_UP, REQ_UP_ITEM }, 70 { KEY_HOME, REQ_FIRST_ITEM }, 71 { KEY_NPAGE, REQ_SCR_DPAGE }, 72 { KEY_PPAGE, REQ_SCR_UPAGE }, 73 { KEY_BEG, REQ_FIRST_ITEM }, 74 { KEY_END, REQ_LAST_ITEM }, 75 }; 76 unsigned int i; 77 78 for (i = 0; i < ARRAY_SIZE(key_map); ++i) 79 if (key_map[i].key == key) { 80 menu_driver(menu, key_map[i].request); 81 break; 82 } 83 } 84 85 static void on_handle_key(int key) 86 { 87 switch (key) { 88 case 27: 89 case KEY_CANCEL: 90 case 'q': 91 case 'Q': 92 list_widget.close(); 93 break; 94 case 10: 95 case 13: 96 case KEY_ENTER: 97 on_key_enter(); 98 break; 99 default: 100 on_menu_key(key); 101 break; 102 } 103 } 104 105 static bool create(void) 106 { 107 int rows, columns; 108 const char *title; 109 110 if (screen_lines < 3 || screen_cols < 10) { 111 beep(); 112 list_widget.close(); 113 return FALSE; 114 } 115 scale_menu(menu, &rows, &columns); 116 rows += 2; 117 columns += 2; 118 if (rows > screen_lines) 119 rows = screen_lines; 120 if (columns > screen_cols) 121 columns = screen_cols; 122 123 widget_init(&list_widget, rows, columns, SCREEN_CENTER, SCREEN_CENTER, 124 attr_menu, WIDGET_BORDER | WIDGET_SUBWINDOW); 125 126 title = _("Sound Card"); 127 mvwprintw(list_widget.window, 0, (columns - 2 - get_mbs_width(title)) / 2, " %s ", title); 128 set_menu_win(menu, list_widget.window); 129 set_menu_sub(menu, list_widget.subwindow); 130 return TRUE; 131 } 132 133 static void on_window_size_changed(void) 134 { 135 unpost_menu(menu); 136 if (!create()) 137 return; 138 post_menu(menu); 139 } 140 141 static void on_close(void) 142 { 143 unsigned int i; 144 struct card *card, *next_card; 145 146 unpost_menu(menu); 147 free_menu(menu); 148 for (i = 0; items[i]; ++i) 149 free_item(items[i]); 150 free(items); 151 for (card = first_card.next; card; card = next_card) { 152 next_card = card->next; 153 free(card->indexstr); 154 free(card->name); 155 free(card->device_name); 156 free(card); 157 } 158 widget_free(&list_widget); 159 } 160 161 void close_card_select_list(void) 162 { 163 on_close(); 164 } 165 166 static struct widget list_widget = { 167 .handle_key = on_handle_key, 168 .window_size_changed = on_window_size_changed, 169 .close = on_close, 170 }; 171 172 static int get_cards(void) 173 { 174 int count, number, err; 175 snd_ctl_t *ctl; 176 snd_ctl_card_info_t *info; 177 char buf[16]; 178 struct card *card, *prev_card; 179 180 first_card.indexstr = "-"; 181 first_card.name = _("(default)"); 182 first_card.device_name = "default"; 183 count = 1; 184 185 snd_ctl_card_info_alloca(&info); 186 prev_card = &first_card; 187 number = -1; 188 for (;;) { 189 err = snd_card_next(&number); 190 if (err < 0) 191 fatal_alsa_error(_("cannot enumerate sound cards"), err); 192 if (number < 0) 193 break; 194 sprintf(buf, "hw:%d", number); 195 err = snd_ctl_open(&ctl, buf, 0); 196 if (err < 0) 197 continue; 198 err = snd_ctl_card_info(ctl, info); 199 snd_ctl_close(ctl); 200 if (err < 0) 201 continue; 202 card = ccalloc(1, sizeof *card); 203 sprintf(buf, "%d", number); 204 card->indexstr = cstrdup(buf); 205 card->name = cstrdup(snd_ctl_card_info_get_name(info)); 206 sprintf(buf, "hw:%d", number); 207 card->device_name = cstrdup(buf); 208 prev_card->next = card; 209 prev_card = card; 210 ++count; 211 } 212 213 card = ccalloc(1, sizeof *card); 214 card->indexstr = cstrdup(" "); 215 card->name = cstrdup(_("enter device name...")); 216 prev_card->next = card; 217 ++count; 218 219 return count; 220 } 221 222 static void create_list_items(int cards) 223 { 224 int i; 225 struct card *card; 226 ITEM *item; 227 228 initial_item = NULL; 229 items = ccalloc(cards + 1, sizeof(ITEM*)); 230 i = 0; 231 for (card = &first_card; card; card = card->next) { 232 item = new_item(card->indexstr, card->name); 233 if (!item) 234 fatal_error("cannot create menu item"); 235 set_item_userptr(item, card); 236 items[i++] = item; 237 if (!initial_item && 238 mixer_device_name && 239 (!card->device_name || 240 !strcmp(card->device_name, mixer_device_name))) 241 initial_item = item; 242 } 243 assert(i == cards); 244 } 245 246 void create_card_select_list(void) 247 { 248 int cards; 249 250 cards = get_cards(); 251 create_list_items(cards); 252 253 menu = new_menu(items); 254 if (!menu) 255 fatal_error("cannot create menu"); 256 set_menu_fore(menu, attr_menu_selected); 257 set_menu_back(menu, attr_menu); 258 set_menu_mark(menu, NULL); 259 if (initial_item) 260 set_current_item(menu, initial_item); 261 set_menu_spacing(menu, 2, 1, 1); 262 menu_opts_on(menu, O_SHOWDESC); 263 264 if (!create()) 265 return; 266 267 post_menu(menu); 268 }