diff --git a/meson.build b/meson.build index 2f6ced2..cc08453 100644 --- a/meson.build +++ b/meson.build @@ -4,13 +4,49 @@ project( default_options: ['warning_level=3'], ) -deps = [ - dependency('libpulse', version: '>= 15.0'), -] + +wayland_protos = dependency('wayland-protocols') +wayland_client = dependency('wayland-client', version: '>=1.14.91') + +wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir') + +wayland_scanner = find_program('wayland-scanner') +wayland_scanner_code = generator( + wayland_scanner, + output: '@BASENAME@-protocol.c', + arguments: ['private-code', '@INPUT@', '@OUTPUT@'], +) +wayland_scanner_client = generator( + wayland_scanner, + output: '@BASENAME@-client-protocol.h', + arguments: ['client-header', '@INPUT@', '@OUTPUT@'], +) + +xml = join_paths([wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml']) +client_protos_src = wayland_scanner_code.process(xml) +client_protos_headers = wayland_scanner_client.process(xml) + +lib_client_protos = static_library( + 'client_protos', + [ client_protos_src, client_protos_headers ], +) + +client_protos = declare_dependency( + link_with: lib_client_protos, + sources: client_protos_headers, +) executable( 'audioListener', - './src/main.cpp', - dependencies: deps, + [ + './src/main.cpp', + './src/idle.cpp', + ], + dependencies: [ + dependency('libpulse', version: '>= 15.0'), + wayland_protos, + wayland_client, + client_protos, + ], install: true, ) diff --git a/src/idle.cpp b/src/idle.cpp new file mode 100644 index 0000000..1ff9778 --- /dev/null +++ b/src/idle.cpp @@ -0,0 +1,81 @@ +#include +#include + +#include + +#include "idle-inhibit-unstable-v1-client-protocol.h" + +using namespace std; + +class Idle { + struct wl_compositor *compositor = NULL; + struct zwp_idle_inhibit_manager_v1 *wl_idle_inhibit_manager = NULL; + struct wl_surface *surface = NULL; + struct wl_display *display = NULL; + struct zwp_idle_inhibitor_v1 *idle = NULL; + + static void global_add(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t) { + Idle *idle = (Idle *)data; + if (strcmp(interface, wl_compositor_interface.name) == 0) { + idle->compositor = (wl_compositor *)wl_registry_bind( + registry, name, &wl_compositor_interface, 1); + } else if (strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) == + 0) { + idle->wl_idle_inhibit_manager = + (zwp_idle_inhibit_manager_v1 *)wl_registry_bind( + registry, name, &zwp_idle_inhibit_manager_v1_interface, 1); + } + } + + static void global_remove(void *, struct wl_registry *, uint32_t) { + // Do nothing + } + + public: + Idle() { + display = wl_display_connect(NULL); + if (display == NULL) { + fprintf(stderr, "failed to connect to wl_display\n"); + exit(1); + } + + const struct wl_registry_listener registry_listener = { + .global = global_add, + .global_remove = global_remove, + }; + + struct wl_registry *registry = wl_display_get_registry(display); + wl_registry_add_listener(registry, ®istry_listener, this); + wl_display_roundtrip(display); + + if (wl_idle_inhibit_manager == NULL) { + fprintf(stderr, "wl_idle_inhibit_manager is NULL\n"); + exit(1); + } + if (compositor == NULL) { + fprintf(stderr, "compositor is NULL\n"); + exit(1); + } + + surface = wl_compositor_create_surface(compositor); + } + + void update(bool isRunning) { + if (isRunning) { + if (idle == NULL) { + idle = zwp_idle_inhibit_manager_v1_create_inhibitor( + wl_idle_inhibit_manager, surface); + wl_display_roundtrip(display); + cout << "IDLE INHIBITED" << endl; + } + } else { + if (idle != NULL) { + zwp_idle_inhibitor_v1_destroy(idle); + idle = NULL; + wl_display_roundtrip(display); + cout << "NOT IDLE INHIBITED" << endl; + } + } + } +}; diff --git a/src/main.cpp b/src/main.cpp index ee6eaec..cde1d34 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,18 +5,22 @@ #include +#include "./idle.cpp" + using namespace std; enum SubscriptionType { - SUBSCRIPTION_TYPE_SINK, - SUBSCRIPTION_TYPE_SOURCE, - SUBSCRIPTION_TYPE_BOTH + SUBSCRIPTION_TYPE_IDLE, + SUBSCRIPTION_TYPE_DRY_BOTH, + SUBSCRIPTION_TYPE_DRY_SINK, + SUBSCRIPTION_TYPE_DRY_SOURCE, }; enum EventType { - EVENT_TYPE_SINK, - EVENT_TYPE_SOURCE, - EVENT_TYPE_BOTH, - EVENT_TYPE_NONE + EVENT_TYPE_IDLE, + EVENT_TYPE_DRY_BOTH, + EVENT_TYPE_DRY_SINK, + EVENT_TYPE_DRY_SOURCE, + EVENT_TYPE_NONE, }; struct Data { @@ -30,16 +34,18 @@ struct Data { SubscriptionType subscriptionType; pa_subscription_mask_t pa_subscriptionType; - bool json; + + Idle *idle = NULL; Data(pa_threaded_mainloop *mainloop, pa_mainloop_api *mainloop_api, SubscriptionType subscriptionType, - pa_subscription_mask_t pa_subscriptionType, bool json) { + pa_subscription_mask_t pa_subscriptionType) { this->mainloop = mainloop; this->mainloop_api = mainloop_api; this->subscriptionType = subscriptionType; this->pa_subscriptionType = pa_subscriptionType; - this->json = json; + + if (subscriptionType == SUBSCRIPTION_TYPE_IDLE) idle = new Idle(); } void quit(int returnValue = 0) { @@ -48,19 +54,26 @@ struct Data { pa_threaded_mainloop_free(mainloop); } - void printResult() { - bool isRunning = false; + void handleAction() { switch (subscriptionType) { - case SUBSCRIPTION_TYPE_BOTH: - isRunning = activeSink || activeSource; + case SUBSCRIPTION_TYPE_IDLE: + if (!idle) idle = new Idle(); + idle->update(activeSink || activeSource); + break; + case SUBSCRIPTION_TYPE_DRY_BOTH: + this->print(activeSink || activeSource); break; - case SUBSCRIPTION_TYPE_SINK: - isRunning = activeSink; + case SUBSCRIPTION_TYPE_DRY_SINK: + this->print(activeSink); break; - case SUBSCRIPTION_TYPE_SOURCE: - isRunning = activeSource; + case SUBSCRIPTION_TYPE_DRY_SOURCE: + this->print(activeSource); break; } + } + + private: + void print(bool isRunning) { cout << (isRunning ? "RUNNING" : "NOT RUNNING") << endl; } }; @@ -83,12 +96,12 @@ void getRunning(EventType eventType, Data *data, pa_context *context) { pa_threaded_mainloop_lock(data->mainloop); pa_operation *op = NULL; switch (eventType) { - case EVENT_TYPE_SINK: + case EVENT_TYPE_DRY_SINK: data->activeSink = false; op = pa_context_get_sink_input_info_list(context, sink_input_info_callback, data); break; - case EVENT_TYPE_SOURCE: + case EVENT_TYPE_DRY_SOURCE: data->activeSource = false; op = pa_context_get_source_output_info_list( context, source_output_info_callback, data); @@ -107,13 +120,13 @@ void getRunning(EventType eventType, Data *data, pa_context *context) { void subscribe_callback(pa_context *, pa_subscription_event_type_t type, uint32_t, void *userdata) { Data *data = (Data *)userdata; - bool isBoth = data->subscriptionType == SUBSCRIPTION_TYPE_BOTH; + bool isBoth = data->subscriptionType == SUBSCRIPTION_TYPE_IDLE; switch (type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { case PA_SUBSCRIPTION_EVENT_SINK: - data->eventCalled = isBoth ? EVENT_TYPE_BOTH : EVENT_TYPE_SINK; + data->eventCalled = isBoth ? EVENT_TYPE_IDLE : EVENT_TYPE_DRY_SINK; break; case PA_SUBSCRIPTION_EVENT_SOURCE: - data->eventCalled = isBoth ? EVENT_TYPE_BOTH : EVENT_TYPE_SOURCE; + data->eventCalled = isBoth ? EVENT_TYPE_IDLE : EVENT_TYPE_DRY_SOURCE; break; default: return; @@ -147,9 +160,9 @@ void context_state_callback(pa_context *c, void *userdata) { void connect(pa_threaded_mainloop *mainloop, pa_mainloop_api *mainloop_api, SubscriptionType subscriptionType, - pa_subscription_mask_t pa_subscriptionType, bool json) { - Data *data = new Data(mainloop, mainloop_api, subscriptionType, - pa_subscriptionType, json); + pa_subscription_mask_t pa_subscriptionType) { + Data *data = + new Data(mainloop, mainloop_api, subscriptionType, pa_subscriptionType); pa_context *context = pa_context_new(mainloop_api, "PulseAudio Test"); if (!context) { @@ -174,31 +187,35 @@ void connect(pa_threaded_mainloop *mainloop, pa_mainloop_api *mainloop_api, // print when started switch (subscriptionType) { - case SUBSCRIPTION_TYPE_BOTH: - data->eventCalled = EVENT_TYPE_BOTH; + case SUBSCRIPTION_TYPE_IDLE: + data->eventCalled = EVENT_TYPE_IDLE; break; - case SUBSCRIPTION_TYPE_SINK: - data->eventCalled = EVENT_TYPE_SINK; + case SUBSCRIPTION_TYPE_DRY_BOTH: + data->eventCalled = EVENT_TYPE_DRY_BOTH; break; - case SUBSCRIPTION_TYPE_SOURCE: - data->eventCalled = EVENT_TYPE_SOURCE; + case SUBSCRIPTION_TYPE_DRY_SINK: + data->eventCalled = EVENT_TYPE_DRY_SINK; + break; + case SUBSCRIPTION_TYPE_DRY_SOURCE: + data->eventCalled = EVENT_TYPE_DRY_SOURCE; break; } for (;;) { switch (data->eventCalled) { - case EVENT_TYPE_BOTH: - getRunning(EVENT_TYPE_SINK, data, context); - getRunning(EVENT_TYPE_SOURCE, data, context); - data->printResult(); + case EVENT_TYPE_IDLE: + case EVENT_TYPE_DRY_BOTH: + getRunning(EVENT_TYPE_DRY_SINK, data, context); + getRunning(EVENT_TYPE_DRY_SOURCE, data, context); + data->handleAction(); break; - case EVENT_TYPE_SINK: + case EVENT_TYPE_DRY_SINK: getRunning(data->eventCalled, data, context); - data->printResult(); + data->handleAction(); break; - case EVENT_TYPE_SOURCE: + case EVENT_TYPE_DRY_SOURCE: getRunning(data->eventCalled, data, context); - data->printResult(); + data->handleAction(); break; default: continue; @@ -207,6 +224,16 @@ void connect(pa_threaded_mainloop *mainloop, pa_mainloop_api *mainloop_api, } } +void showHelp(char **argv) { + cout << "Usage:" << endl; + cout << "\t" << argv[0] << "