bluetooth-player.c 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. *
  4. * BlueZ - Bluetooth protocol stack for Linux
  5. *
  6. * Copyright (C) 2012 Intel Corporation. All rights reserved.
  7. *
  8. *
  9. */
  10. #ifdef HAVE_CONFIG_H
  11. #include <config.h>
  12. #endif
  13. #define _GNU_SOURCE
  14. #include <stdio.h>
  15. #include <stdbool.h>
  16. #include <errno.h>
  17. #include <unistd.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <glib.h>
  21. #include "gdbus/gdbus.h"
  22. #include "src/shared/shell.h"
  23. /* String display constants */
  24. #define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF
  25. #define COLORED_CHG COLOR_YELLOW "CHG" COLOR_OFF
  26. #define COLORED_DEL COLOR_RED "DEL" COLOR_OFF
  27. #define PROMPT_ON COLOR_BLUE "[bluetooth]" COLOR_OFF "# "
  28. #define PROMPT_OFF "[bluetooth]# "
  29. #define BLUEZ_MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1"
  30. #define BLUEZ_MEDIA_FOLDER_INTERFACE "org.bluez.MediaFolder1"
  31. #define BLUEZ_MEDIA_ITEM_INTERFACE "org.bluez.MediaItem1"
  32. static DBusConnection *dbus_conn;
  33. static GDBusProxy *default_player;
  34. static GList *players = NULL;
  35. static GList *folders = NULL;
  36. static GList *items = NULL;
  37. static void connect_handler(DBusConnection *connection, void *user_data)
  38. {
  39. bt_shell_attach(fileno(stdin));
  40. bt_shell_set_prompt(PROMPT_ON);
  41. }
  42. static void disconnect_handler(DBusConnection *connection, void *user_data)
  43. {
  44. bt_shell_detach();
  45. bt_shell_set_prompt(PROMPT_OFF);
  46. }
  47. static bool check_default_player(void)
  48. {
  49. if (!default_player) {
  50. bt_shell_printf("No default player available\n");
  51. return FALSE;
  52. }
  53. return TRUE;
  54. }
  55. static char *generic_generator(const char *text, int state, GList *source)
  56. {
  57. static int index = 0;
  58. if (!state) {
  59. index = 0;
  60. }
  61. return g_dbus_proxy_path_lookup(source, &index, text);
  62. }
  63. static char *player_generator(const char *text, int state)
  64. {
  65. return generic_generator(text, state, players);
  66. }
  67. static char *item_generator(const char *text, int state)
  68. {
  69. return generic_generator(text, state, items);
  70. }
  71. static void play_reply(DBusMessage *message, void *user_data)
  72. {
  73. DBusError error;
  74. dbus_error_init(&error);
  75. if (dbus_set_error_from_message(&error, message) == TRUE) {
  76. bt_shell_printf("Failed to play: %s\n", error.name);
  77. dbus_error_free(&error);
  78. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  79. }
  80. bt_shell_printf("Play successful\n");
  81. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  82. }
  83. static void cmd_play(int argc, char *argv[])
  84. {
  85. GDBusProxy *proxy;
  86. if (argc > 1) {
  87. proxy = g_dbus_proxy_lookup(items, NULL, argv[1],
  88. BLUEZ_MEDIA_ITEM_INTERFACE);
  89. if (proxy == NULL) {
  90. bt_shell_printf("Item %s not available\n", argv[1]);
  91. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  92. }
  93. } else {
  94. if (!check_default_player())
  95. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  96. proxy = default_player;
  97. }
  98. if (g_dbus_proxy_method_call(proxy, "Play", NULL, play_reply,
  99. NULL, NULL) == FALSE) {
  100. bt_shell_printf("Failed to play\n");
  101. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  102. }
  103. bt_shell_printf("Attempting to play %s\n", argv[1] ? : "");
  104. }
  105. static void pause_reply(DBusMessage *message, void *user_data)
  106. {
  107. DBusError error;
  108. dbus_error_init(&error);
  109. if (dbus_set_error_from_message(&error, message) == TRUE) {
  110. bt_shell_printf("Failed to pause: %s\n", error.name);
  111. dbus_error_free(&error);
  112. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  113. }
  114. bt_shell_printf("Pause successful\n");
  115. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  116. }
  117. static void cmd_pause(int argc, char *argv[])
  118. {
  119. if (!check_default_player())
  120. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  121. if (g_dbus_proxy_method_call(default_player, "Pause", NULL,
  122. pause_reply, NULL, NULL) == FALSE) {
  123. bt_shell_printf("Failed to play\n");
  124. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  125. }
  126. bt_shell_printf("Attempting to pause\n");
  127. }
  128. static void stop_reply(DBusMessage *message, void *user_data)
  129. {
  130. DBusError error;
  131. dbus_error_init(&error);
  132. if (dbus_set_error_from_message(&error, message) == TRUE) {
  133. bt_shell_printf("Failed to stop: %s\n", error.name);
  134. dbus_error_free(&error);
  135. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  136. }
  137. bt_shell_printf("Stop successful\n");
  138. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  139. }
  140. static void cmd_stop(int argc, char *argv[])
  141. {
  142. if (!check_default_player())
  143. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  144. if (g_dbus_proxy_method_call(default_player, "Stop", NULL, stop_reply,
  145. NULL, NULL) == FALSE) {
  146. bt_shell_printf("Failed to stop\n");
  147. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  148. }
  149. bt_shell_printf("Attempting to stop\n");
  150. }
  151. static void next_reply(DBusMessage *message, void *user_data)
  152. {
  153. DBusError error;
  154. dbus_error_init(&error);
  155. if (dbus_set_error_from_message(&error, message) == TRUE) {
  156. bt_shell_printf("Failed to jump to next: %s\n", error.name);
  157. dbus_error_free(&error);
  158. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  159. }
  160. bt_shell_printf("Next successful\n");
  161. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  162. }
  163. static void cmd_next(int argc, char *argv[])
  164. {
  165. if (!check_default_player())
  166. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  167. if (g_dbus_proxy_method_call(default_player, "Next", NULL, next_reply,
  168. NULL, NULL) == FALSE) {
  169. bt_shell_printf("Failed to jump to next\n");
  170. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  171. }
  172. bt_shell_printf("Attempting to jump to next\n");
  173. }
  174. static void previous_reply(DBusMessage *message, void *user_data)
  175. {
  176. DBusError error;
  177. dbus_error_init(&error);
  178. if (dbus_set_error_from_message(&error, message) == TRUE) {
  179. bt_shell_printf("Failed to jump to previous: %s\n", error.name);
  180. dbus_error_free(&error);
  181. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  182. }
  183. bt_shell_printf("Previous successful\n");
  184. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  185. }
  186. static void cmd_previous(int argc, char *argv[])
  187. {
  188. if (!check_default_player())
  189. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  190. if (g_dbus_proxy_method_call(default_player, "Previous", NULL,
  191. previous_reply, NULL, NULL) == FALSE) {
  192. bt_shell_printf("Failed to jump to previous\n");
  193. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  194. }
  195. bt_shell_printf("Attempting to jump to previous\n");
  196. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  197. }
  198. static void fast_forward_reply(DBusMessage *message, void *user_data)
  199. {
  200. DBusError error;
  201. dbus_error_init(&error);
  202. if (dbus_set_error_from_message(&error, message) == TRUE) {
  203. bt_shell_printf("Failed to fast forward: %s\n", error.name);
  204. dbus_error_free(&error);
  205. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  206. }
  207. bt_shell_printf("FastForward successful\n");
  208. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  209. }
  210. static void cmd_fast_forward(int argc, char *argv[])
  211. {
  212. if (!check_default_player())
  213. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  214. if (g_dbus_proxy_method_call(default_player, "FastForward", NULL,
  215. fast_forward_reply, NULL, NULL) == FALSE) {
  216. bt_shell_printf("Failed to jump to previous\n");
  217. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  218. }
  219. bt_shell_printf("Fast forward playback\n");
  220. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  221. }
  222. static void rewind_reply(DBusMessage *message, void *user_data)
  223. {
  224. DBusError error;
  225. dbus_error_init(&error);
  226. if (dbus_set_error_from_message(&error, message) == TRUE) {
  227. bt_shell_printf("Failed to rewind: %s\n", error.name);
  228. dbus_error_free(&error);
  229. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  230. }
  231. bt_shell_printf("Rewind successful\n");
  232. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  233. }
  234. static void cmd_rewind(int argc, char *argv[])
  235. {
  236. if (!check_default_player())
  237. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  238. if (g_dbus_proxy_method_call(default_player, "Rewind", NULL,
  239. rewind_reply, NULL, NULL) == FALSE) {
  240. bt_shell_printf("Failed to rewind\n");
  241. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  242. }
  243. bt_shell_printf("Rewind playback\n");
  244. }
  245. static void generic_callback(const DBusError *error, void *user_data)
  246. {
  247. char *str = user_data;
  248. if (dbus_error_is_set(error)) {
  249. bt_shell_printf("Failed to set %s: %s\n", str, error->name);
  250. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  251. } else {
  252. bt_shell_printf("Changing %s succeeded\n", str);
  253. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  254. }
  255. }
  256. static void cmd_equalizer(int argc, char *argv[])
  257. {
  258. char *value;
  259. DBusMessageIter iter;
  260. if (!check_default_player())
  261. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  262. if (!g_dbus_proxy_get_property(default_player, "Equalizer", &iter)) {
  263. bt_shell_printf("Operation not supported\n");
  264. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  265. }
  266. value = g_strdup(argv[1]);
  267. if (g_dbus_proxy_set_property_basic(default_player, "Equalizer",
  268. DBUS_TYPE_STRING, &value,
  269. generic_callback, value,
  270. g_free) == FALSE) {
  271. bt_shell_printf("Failed to setting equalizer\n");
  272. g_free(value);
  273. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  274. }
  275. bt_shell_printf("Attempting to set equalizer\n");
  276. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  277. }
  278. static void cmd_repeat(int argc, char *argv[])
  279. {
  280. char *value;
  281. DBusMessageIter iter;
  282. if (!check_default_player())
  283. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  284. if (!g_dbus_proxy_get_property(default_player, "Repeat", &iter)) {
  285. bt_shell_printf("Operation not supported\n");
  286. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  287. }
  288. value = g_strdup(argv[1]);
  289. if (g_dbus_proxy_set_property_basic(default_player, "Repeat",
  290. DBUS_TYPE_STRING, &value,
  291. generic_callback, value,
  292. g_free) == FALSE) {
  293. bt_shell_printf("Failed to set repeat\n");
  294. g_free(value);
  295. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  296. }
  297. bt_shell_printf("Attempting to set repeat\n");
  298. }
  299. static void cmd_shuffle(int argc, char *argv[])
  300. {
  301. char *value;
  302. DBusMessageIter iter;
  303. if (!check_default_player())
  304. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  305. if (!g_dbus_proxy_get_property(default_player, "Shuffle", &iter)) {
  306. bt_shell_printf("Operation not supported\n");
  307. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  308. }
  309. value = g_strdup(argv[1]);
  310. if (g_dbus_proxy_set_property_basic(default_player, "Shuffle",
  311. DBUS_TYPE_STRING, &value,
  312. generic_callback, value,
  313. g_free) == FALSE) {
  314. bt_shell_printf("Failed to set shuffle\n");
  315. g_free(value);
  316. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  317. }
  318. bt_shell_printf("Attempting to set shuffle\n");
  319. }
  320. static void cmd_scan(int argc, char *argv[])
  321. {
  322. char *value;
  323. DBusMessageIter iter;
  324. if (!check_default_player())
  325. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  326. if (!g_dbus_proxy_get_property(default_player, "Shuffle", &iter)) {
  327. bt_shell_printf("Operation not supported\n");
  328. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  329. }
  330. value = g_strdup(argv[1]);
  331. if (g_dbus_proxy_set_property_basic(default_player, "Shuffle",
  332. DBUS_TYPE_STRING, &value,
  333. generic_callback, value,
  334. g_free) == FALSE) {
  335. bt_shell_printf("Failed to set scan\n");
  336. g_free(value);
  337. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  338. }
  339. bt_shell_printf("Attempting to set scan\n");
  340. }
  341. static char *proxy_description(GDBusProxy *proxy, const char *title,
  342. const char *description)
  343. {
  344. const char *path;
  345. path = g_dbus_proxy_get_path(proxy);
  346. return g_strdup_printf("%s%s%s%s %s ",
  347. description ? "[" : "",
  348. description ? : "",
  349. description ? "] " : "",
  350. title, path);
  351. }
  352. static void print_player(GDBusProxy *proxy, const char *description)
  353. {
  354. char *str;
  355. str = proxy_description(proxy, "Player", description);
  356. bt_shell_printf("%s%s\n", str,
  357. default_player == proxy ? "[default]" : "");
  358. g_free(str);
  359. }
  360. static void cmd_list(int argc, char *arg[])
  361. {
  362. GList *l;
  363. for (l = players; l; l = g_list_next(l)) {
  364. GDBusProxy *proxy = l->data;
  365. print_player(proxy, NULL);
  366. }
  367. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  368. }
  369. static void print_iter(const char *label, const char *name,
  370. DBusMessageIter *iter)
  371. {
  372. dbus_bool_t valbool;
  373. dbus_uint32_t valu32;
  374. dbus_uint16_t valu16;
  375. dbus_int16_t vals16;
  376. const char *valstr;
  377. DBusMessageIter subiter;
  378. if (iter == NULL) {
  379. bt_shell_printf("%s%s is nil\n", label, name);
  380. return;
  381. }
  382. switch (dbus_message_iter_get_arg_type(iter)) {
  383. case DBUS_TYPE_INVALID:
  384. bt_shell_printf("%s%s is invalid\n", label, name);
  385. break;
  386. case DBUS_TYPE_STRING:
  387. case DBUS_TYPE_OBJECT_PATH:
  388. dbus_message_iter_get_basic(iter, &valstr);
  389. bt_shell_printf("%s%s: %s\n", label, name, valstr);
  390. break;
  391. case DBUS_TYPE_BOOLEAN:
  392. dbus_message_iter_get_basic(iter, &valbool);
  393. bt_shell_printf("%s%s: %s\n", label, name,
  394. valbool == TRUE ? "yes" : "no");
  395. break;
  396. case DBUS_TYPE_UINT32:
  397. dbus_message_iter_get_basic(iter, &valu32);
  398. bt_shell_printf("%s%s: 0x%06x\n", label, name, valu32);
  399. break;
  400. case DBUS_TYPE_UINT16:
  401. dbus_message_iter_get_basic(iter, &valu16);
  402. bt_shell_printf("%s%s: 0x%04x\n", label, name, valu16);
  403. break;
  404. case DBUS_TYPE_INT16:
  405. dbus_message_iter_get_basic(iter, &vals16);
  406. bt_shell_printf("%s%s: %d\n", label, name, vals16);
  407. break;
  408. case DBUS_TYPE_VARIANT:
  409. dbus_message_iter_recurse(iter, &subiter);
  410. print_iter(label, name, &subiter);
  411. break;
  412. case DBUS_TYPE_ARRAY:
  413. dbus_message_iter_recurse(iter, &subiter);
  414. while (dbus_message_iter_get_arg_type(&subiter) !=
  415. DBUS_TYPE_INVALID) {
  416. print_iter(label, name, &subiter);
  417. dbus_message_iter_next(&subiter);
  418. }
  419. break;
  420. case DBUS_TYPE_DICT_ENTRY:
  421. dbus_message_iter_recurse(iter, &subiter);
  422. dbus_message_iter_get_basic(&subiter, &valstr);
  423. dbus_message_iter_next(&subiter);
  424. print_iter(label, valstr, &subiter);
  425. break;
  426. default:
  427. bt_shell_printf("%s%s has unsupported type\n", label, name);
  428. break;
  429. }
  430. }
  431. static void print_property(GDBusProxy *proxy, const char *name)
  432. {
  433. DBusMessageIter iter;
  434. if (g_dbus_proxy_get_property(proxy, name, &iter) == FALSE)
  435. return;
  436. print_iter("\t", name, &iter);
  437. }
  438. static void cmd_show_item(int argc, char *argv[])
  439. {
  440. GDBusProxy *proxy;
  441. proxy = g_dbus_proxy_lookup(items, NULL, argv[1],
  442. BLUEZ_MEDIA_ITEM_INTERFACE);
  443. if (!proxy) {
  444. bt_shell_printf("Item %s not available\n", argv[1]);
  445. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  446. }
  447. bt_shell_printf("Item %s\n", g_dbus_proxy_get_path(proxy));
  448. print_property(proxy, "Player");
  449. print_property(proxy, "Name");
  450. print_property(proxy, "Type");
  451. print_property(proxy, "FolderType");
  452. print_property(proxy, "Playable");
  453. print_property(proxy, "Metadata");
  454. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  455. }
  456. static void cmd_show(int argc, char *argv[])
  457. {
  458. GDBusProxy *proxy;
  459. GDBusProxy *folder;
  460. GDBusProxy *item;
  461. DBusMessageIter iter;
  462. const char *path;
  463. if (argc < 2) {
  464. if (check_default_player() == FALSE)
  465. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  466. proxy = default_player;
  467. } else {
  468. proxy = g_dbus_proxy_lookup(players, NULL, argv[1],
  469. BLUEZ_MEDIA_PLAYER_INTERFACE);
  470. if (!proxy) {
  471. bt_shell_printf("Player %s not available\n", argv[1]);
  472. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  473. }
  474. }
  475. bt_shell_printf("Player %s\n", g_dbus_proxy_get_path(proxy));
  476. print_property(proxy, "Name");
  477. print_property(proxy, "Repeat");
  478. print_property(proxy, "Equalizer");
  479. print_property(proxy, "Shuffle");
  480. print_property(proxy, "Scan");
  481. print_property(proxy, "Status");
  482. print_property(proxy, "Position");
  483. print_property(proxy, "Track");
  484. folder = g_dbus_proxy_lookup(folders, NULL,
  485. g_dbus_proxy_get_path(proxy),
  486. BLUEZ_MEDIA_FOLDER_INTERFACE);
  487. if (folder == NULL)
  488. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  489. bt_shell_printf("Folder %s\n", g_dbus_proxy_get_path(proxy));
  490. print_property(folder, "Name");
  491. print_property(folder, "NumberOfItems");
  492. if (!g_dbus_proxy_get_property(proxy, "Playlist", &iter))
  493. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  494. dbus_message_iter_get_basic(&iter, &path);
  495. item = g_dbus_proxy_lookup(items, NULL, path,
  496. BLUEZ_MEDIA_ITEM_INTERFACE);
  497. if (item == NULL)
  498. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  499. bt_shell_printf("Playlist %s\n", path);
  500. print_property(item, "Name");
  501. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  502. }
  503. static void cmd_select(int argc, char *argv[])
  504. {
  505. GDBusProxy *proxy;
  506. proxy = g_dbus_proxy_lookup(players, NULL, argv[1],
  507. BLUEZ_MEDIA_PLAYER_INTERFACE);
  508. if (proxy == NULL) {
  509. bt_shell_printf("Player %s not available\n", argv[1]);
  510. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  511. }
  512. if (default_player == proxy)
  513. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  514. default_player = proxy,
  515. print_player(proxy, NULL);
  516. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  517. }
  518. static void change_folder_reply(DBusMessage *message, void *user_data)
  519. {
  520. DBusError error;
  521. dbus_error_init(&error);
  522. if (dbus_set_error_from_message(&error, message) == TRUE) {
  523. bt_shell_printf("Failed to change folder: %s\n", error.name);
  524. dbus_error_free(&error);
  525. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  526. }
  527. bt_shell_printf("ChangeFolder successful\n");
  528. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  529. }
  530. static void change_folder_setup(DBusMessageIter *iter, void *user_data)
  531. {
  532. const char *path = user_data;
  533. dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
  534. }
  535. static void cmd_change_folder(int argc, char *argv[])
  536. {
  537. GDBusProxy *proxy;
  538. if (dbus_validate_path(argv[1], NULL) == FALSE) {
  539. bt_shell_printf("Not a valid path\n");
  540. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  541. }
  542. if (check_default_player() == FALSE)
  543. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  544. proxy = g_dbus_proxy_lookup(folders, NULL,
  545. g_dbus_proxy_get_path(default_player),
  546. BLUEZ_MEDIA_FOLDER_INTERFACE);
  547. if (proxy == NULL) {
  548. bt_shell_printf("Operation not supported\n");
  549. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  550. }
  551. if (g_dbus_proxy_method_call(proxy, "ChangeFolder", change_folder_setup,
  552. change_folder_reply, argv[1], NULL) == FALSE) {
  553. bt_shell_printf("Failed to change current folder\n");
  554. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  555. }
  556. bt_shell_printf("Attempting to change folder\n");
  557. }
  558. struct list_items_args {
  559. int start;
  560. int end;
  561. };
  562. static void list_items_setup(DBusMessageIter *iter, void *user_data)
  563. {
  564. struct list_items_args *args = user_data;
  565. DBusMessageIter dict;
  566. dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
  567. DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
  568. DBUS_TYPE_STRING_AS_STRING
  569. DBUS_TYPE_VARIANT_AS_STRING
  570. DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
  571. &dict);
  572. if (args->start < 0)
  573. goto done;
  574. g_dbus_dict_append_entry(&dict, "Start",
  575. DBUS_TYPE_UINT32, &args->start);
  576. if (args->end < 0)
  577. goto done;
  578. g_dbus_dict_append_entry(&dict, "End", DBUS_TYPE_UINT32, &args->end);
  579. done:
  580. dbus_message_iter_close_container(iter, &dict);
  581. }
  582. static void list_items_reply(DBusMessage *message, void *user_data)
  583. {
  584. DBusError error;
  585. dbus_error_init(&error);
  586. if (dbus_set_error_from_message(&error, message) == TRUE) {
  587. bt_shell_printf("Failed to list items: %s\n", error.name);
  588. dbus_error_free(&error);
  589. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  590. }
  591. bt_shell_printf("ListItems successful\n");
  592. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  593. }
  594. static void cmd_list_items(int argc, char *argv[])
  595. {
  596. GDBusProxy *proxy;
  597. struct list_items_args *args;
  598. if (check_default_player() == FALSE)
  599. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  600. proxy = g_dbus_proxy_lookup(folders, NULL,
  601. g_dbus_proxy_get_path(default_player),
  602. BLUEZ_MEDIA_FOLDER_INTERFACE);
  603. if (proxy == NULL) {
  604. bt_shell_printf("Operation not supported\n");
  605. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  606. }
  607. args = g_new0(struct list_items_args, 1);
  608. args->start = -1;
  609. args->end = -1;
  610. if (argc < 2)
  611. goto done;
  612. errno = 0;
  613. args->start = strtol(argv[1], NULL, 10);
  614. if (errno != 0) {
  615. bt_shell_printf("%s(%d)\n", strerror(errno), errno);
  616. g_free(args);
  617. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  618. }
  619. if (argc < 3)
  620. goto done;
  621. errno = 0;
  622. args->end = strtol(argv[2], NULL, 10);
  623. if (errno != 0) {
  624. bt_shell_printf("%s(%d)\n", strerror(errno), errno);
  625. g_free(args);
  626. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  627. }
  628. done:
  629. if (g_dbus_proxy_method_call(proxy, "ListItems", list_items_setup,
  630. list_items_reply, args, g_free) == FALSE) {
  631. bt_shell_printf("Failed to change current folder\n");
  632. g_free(args);
  633. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  634. }
  635. bt_shell_printf("Attempting to list items\n");
  636. }
  637. static void search_setup(DBusMessageIter *iter, void *user_data)
  638. {
  639. char *string = user_data;
  640. DBusMessageIter dict;
  641. dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &string);
  642. dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
  643. DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
  644. DBUS_TYPE_STRING_AS_STRING
  645. DBUS_TYPE_VARIANT_AS_STRING
  646. DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
  647. &dict);
  648. dbus_message_iter_close_container(iter, &dict);
  649. }
  650. static void search_reply(DBusMessage *message, void *user_data)
  651. {
  652. DBusError error;
  653. dbus_error_init(&error);
  654. if (dbus_set_error_from_message(&error, message) == TRUE) {
  655. bt_shell_printf("Failed to search: %s\n", error.name);
  656. dbus_error_free(&error);
  657. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  658. }
  659. bt_shell_printf("Search successful\n");
  660. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  661. }
  662. static void cmd_search(int argc, char *argv[])
  663. {
  664. GDBusProxy *proxy;
  665. char *string;
  666. if (check_default_player() == FALSE)
  667. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  668. proxy = g_dbus_proxy_lookup(folders, NULL,
  669. g_dbus_proxy_get_path(default_player),
  670. BLUEZ_MEDIA_FOLDER_INTERFACE);
  671. if (proxy == NULL) {
  672. bt_shell_printf("Operation not supported\n");
  673. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  674. }
  675. string = g_strdup(argv[1]);
  676. if (g_dbus_proxy_method_call(proxy, "Search", search_setup,
  677. search_reply, string, g_free) == FALSE) {
  678. bt_shell_printf("Failed to search\n");
  679. g_free(string);
  680. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  681. }
  682. bt_shell_printf("Attempting to search\n");
  683. }
  684. static void add_to_nowplaying_reply(DBusMessage *message, void *user_data)
  685. {
  686. DBusError error;
  687. dbus_error_init(&error);
  688. if (dbus_set_error_from_message(&error, message) == TRUE) {
  689. bt_shell_printf("Failed to queue: %s\n", error.name);
  690. dbus_error_free(&error);
  691. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  692. }
  693. bt_shell_printf("AddToNowPlaying successful\n");
  694. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  695. }
  696. static void cmd_queue(int argc, char *argv[])
  697. {
  698. GDBusProxy *proxy;
  699. proxy = g_dbus_proxy_lookup(items, NULL, argv[1],
  700. BLUEZ_MEDIA_ITEM_INTERFACE);
  701. if (proxy == NULL) {
  702. bt_shell_printf("Item %s not available\n", argv[1]);
  703. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  704. }
  705. if (g_dbus_proxy_method_call(proxy, "AddtoNowPlaying", NULL,
  706. add_to_nowplaying_reply, NULL,
  707. NULL) == FALSE) {
  708. bt_shell_printf("Failed to play\n");
  709. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  710. }
  711. bt_shell_printf("Attempting to queue %s\n", argv[1]);
  712. }
  713. static const struct bt_shell_menu main_menu = {
  714. .name = "main",
  715. .entries = {
  716. { "list", NULL, cmd_list, "List available players" },
  717. { "show", "[player]", cmd_show, "Player information",
  718. player_generator},
  719. { "select", "<player>", cmd_select, "Select default player",
  720. player_generator},
  721. { "play", "[item]", cmd_play, "Start playback",
  722. item_generator},
  723. { "pause", NULL, cmd_pause, "Pause playback" },
  724. { "stop", NULL, cmd_stop, "Stop playback" },
  725. { "next", NULL, cmd_next, "Jump to next item" },
  726. { "previous", NULL, cmd_previous, "Jump to previous item" },
  727. { "fast-forward", NULL, cmd_fast_forward,
  728. "Fast forward playback" },
  729. { "rewind", NULL, cmd_rewind, "Rewind playback" },
  730. { "equalizer", "<on/off>", cmd_equalizer,
  731. "Enable/Disable equalizer"},
  732. { "repeat", "<singletrack/alltrack/group/off>", cmd_repeat,
  733. "Set repeat mode"},
  734. { "shuffle", "<alltracks/group/off>", cmd_shuffle,
  735. "Set shuffle mode"},
  736. { "scan", "<alltracks/group/off>", cmd_scan,
  737. "Set scan mode"},
  738. { "change-folder", "<item>", cmd_change_folder,
  739. "Change current folder",
  740. item_generator},
  741. { "list-items", "[start] [end]", cmd_list_items,
  742. "List items of current folder" },
  743. { "search", "<string>", cmd_search,
  744. "Search items containing string" },
  745. { "queue", "<item>", cmd_queue, "Add item to playlist queue",
  746. item_generator},
  747. { "show-item", "<item>", cmd_show_item, "Show item information",
  748. item_generator},
  749. {} },
  750. };
  751. static void player_added(GDBusProxy *proxy)
  752. {
  753. players = g_list_append(players, proxy);
  754. if (default_player == NULL)
  755. default_player = proxy;
  756. print_player(proxy, COLORED_NEW);
  757. }
  758. static void print_folder(GDBusProxy *proxy, const char *description)
  759. {
  760. const char *path;
  761. path = g_dbus_proxy_get_path(proxy);
  762. bt_shell_printf("%s%s%sFolder %s\n", description ? "[" : "",
  763. description ? : "",
  764. description ? "] " : "",
  765. path);
  766. }
  767. static void folder_added(GDBusProxy *proxy)
  768. {
  769. folders = g_list_append(folders, proxy);
  770. print_folder(proxy, COLORED_NEW);
  771. }
  772. static void print_item(GDBusProxy *proxy, const char *description)
  773. {
  774. const char *path, *name;
  775. DBusMessageIter iter;
  776. path = g_dbus_proxy_get_path(proxy);
  777. if (g_dbus_proxy_get_property(proxy, "Name", &iter))
  778. dbus_message_iter_get_basic(&iter, &name);
  779. else
  780. name = "<unknown>";
  781. bt_shell_printf("%s%s%sItem %s %s\n", description ? "[" : "",
  782. description ? : "",
  783. description ? "] " : "",
  784. path, name);
  785. }
  786. static void item_added(GDBusProxy *proxy)
  787. {
  788. items = g_list_append(items, proxy);
  789. print_item(proxy, COLORED_NEW);
  790. }
  791. static void proxy_added(GDBusProxy *proxy, void *user_data)
  792. {
  793. const char *interface;
  794. interface = g_dbus_proxy_get_interface(proxy);
  795. if (!strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE))
  796. player_added(proxy);
  797. else if (!strcmp(interface, BLUEZ_MEDIA_FOLDER_INTERFACE))
  798. folder_added(proxy);
  799. else if (!strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE))
  800. item_added(proxy);
  801. }
  802. static void player_removed(GDBusProxy *proxy)
  803. {
  804. print_player(proxy, COLORED_DEL);
  805. if (default_player == proxy)
  806. default_player = NULL;
  807. players = g_list_remove(players, proxy);
  808. }
  809. static void folder_removed(GDBusProxy *proxy)
  810. {
  811. folders = g_list_remove(folders, proxy);
  812. print_folder(proxy, COLORED_DEL);
  813. }
  814. static void item_removed(GDBusProxy *proxy)
  815. {
  816. items = g_list_remove(items, proxy);
  817. print_item(proxy, COLORED_DEL);
  818. }
  819. static void proxy_removed(GDBusProxy *proxy, void *user_data)
  820. {
  821. const char *interface;
  822. interface = g_dbus_proxy_get_interface(proxy);
  823. if (!strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE))
  824. player_removed(proxy);
  825. if (!strcmp(interface, BLUEZ_MEDIA_FOLDER_INTERFACE))
  826. folder_removed(proxy);
  827. if (!strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE))
  828. item_removed(proxy);
  829. }
  830. static void player_property_changed(GDBusProxy *proxy, const char *name,
  831. DBusMessageIter *iter)
  832. {
  833. char *str;
  834. str = proxy_description(proxy, "Player", COLORED_CHG);
  835. print_iter(str, name, iter);
  836. g_free(str);
  837. }
  838. static void folder_property_changed(GDBusProxy *proxy, const char *name,
  839. DBusMessageIter *iter)
  840. {
  841. char *str;
  842. str = proxy_description(proxy, "Folder", COLORED_CHG);
  843. print_iter(str, name, iter);
  844. g_free(str);
  845. }
  846. static void item_property_changed(GDBusProxy *proxy, const char *name,
  847. DBusMessageIter *iter)
  848. {
  849. char *str;
  850. str = proxy_description(proxy, "Item", COLORED_CHG);
  851. print_iter(str, name, iter);
  852. g_free(str);
  853. }
  854. static void property_changed(GDBusProxy *proxy, const char *name,
  855. DBusMessageIter *iter, void *user_data)
  856. {
  857. const char *interface;
  858. interface = g_dbus_proxy_get_interface(proxy);
  859. if (!strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE))
  860. player_property_changed(proxy, name, iter);
  861. else if (!strcmp(interface, BLUEZ_MEDIA_FOLDER_INTERFACE))
  862. folder_property_changed(proxy, name, iter);
  863. else if (!strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE))
  864. item_property_changed(proxy, name, iter);
  865. }
  866. int main(int argc, char *argv[])
  867. {
  868. GDBusClient *client;
  869. int status;
  870. bt_shell_init(argc, argv, NULL);
  871. bt_shell_set_menu(&main_menu);
  872. bt_shell_set_prompt(PROMPT_OFF);
  873. dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
  874. client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
  875. g_dbus_client_set_connect_watch(client, connect_handler, NULL);
  876. g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL);
  877. g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
  878. property_changed, NULL);
  879. status = bt_shell_run();
  880. g_dbus_client_unref(client);
  881. dbus_connection_unref(dbus_conn);
  882. return status;
  883. }