mirror of
https://github.com/rozniak/xfce-winxp-tc.git
synced 2026-05-01 03:31:33 +00:00
Enhancement: Fixes #76, Implement sound volume indicator / levels in notification area
This commit is contained in:
@@ -283,6 +283,10 @@ places/user-desktop-->desktop
|
||||
places/user-home-->folder
|
||||
places/user-trash-->bin
|
||||
status/ac-adapter-->ac_power
|
||||
status/audio-volume-high-->volume_on
|
||||
status/audio-volume-medium-->volume_on
|
||||
status/audio-volume-low-->volume_on
|
||||
status/audio-volume-muted-->volume_off
|
||||
status/battery-caution-->battery_critical
|
||||
status/battery-caution-charging-->battery_low_charging
|
||||
status/battery-empty-->battery_empty
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
bt,rt:glib2
|
||||
bt,rt:gtk3
|
||||
bt,rt:pulseaudio
|
||||
bt,rt:wintc-comgtk
|
||||
|
||||
@@ -19,6 +19,14 @@ typedef enum
|
||||
//
|
||||
// PUBLIC FUNCTIONS
|
||||
//
|
||||
GtkWidget* wintc_dpa_create_popup(
|
||||
GtkWidget* owner
|
||||
);
|
||||
void wintc_dpa_show_popup(
|
||||
GtkWidget* popup,
|
||||
GtkWidget* owner
|
||||
);
|
||||
|
||||
WinTCDisplayProtocol wintc_get_display_protocol_in_use(void);
|
||||
gboolean wintc_init_display_protocol_apis(void);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <gdk/gdk.h>
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
#include <glib.h>
|
||||
#include <wintc-comgtk.h>
|
||||
|
||||
#include "api.h"
|
||||
#include "impl-wayland.h"
|
||||
@@ -50,9 +51,119 @@ void (*wintc_wndmgmt_window_unminimize) (
|
||||
guint64 timestamp
|
||||
) = NULL;
|
||||
|
||||
//
|
||||
// FORWARD DECLARATIONS
|
||||
//
|
||||
static gboolean on_popup_window_focus_out(
|
||||
GtkWidget* widget,
|
||||
GdkEvent* event,
|
||||
gpointer user_data
|
||||
);
|
||||
|
||||
//
|
||||
// PUBLIC FUNCTIONS
|
||||
//
|
||||
GtkWidget* wintc_dpa_create_popup(
|
||||
GtkWidget* owner
|
||||
)
|
||||
{
|
||||
GtkWidget* popup;
|
||||
|
||||
// On GTK3, GtkPopovers are limited to the bounds of the parent when
|
||||
// running under X11 -- this sucks!! So here we do the job of creating
|
||||
// an appropriate pop-up widget based on display protocol
|
||||
//
|
||||
if (wintc_get_display_protocol_in_use() == WINTC_DISPPROTO_X11)
|
||||
{
|
||||
popup = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
|
||||
gtk_window_set_decorated(
|
||||
GTK_WINDOW(popup),
|
||||
FALSE
|
||||
);
|
||||
gtk_window_set_type_hint(
|
||||
GTK_WINDOW(popup),
|
||||
GDK_WINDOW_TYPE_HINT_POPUP_MENU
|
||||
);
|
||||
gtk_window_set_resizable(
|
||||
GTK_WINDOW(popup),
|
||||
FALSE
|
||||
);
|
||||
gtk_window_set_keep_above(
|
||||
GTK_WINDOW(popup),
|
||||
TRUE
|
||||
);
|
||||
gtk_window_set_skip_taskbar_hint(
|
||||
GTK_WINDOW(popup),
|
||||
TRUE
|
||||
);
|
||||
gtk_window_set_title(
|
||||
GTK_WINDOW(popup),
|
||||
"Popup"
|
||||
);
|
||||
gtk_widget_set_events(
|
||||
popup,
|
||||
GDK_FOCUS_CHANGE_MASK
|
||||
);
|
||||
|
||||
// Connect signals
|
||||
//
|
||||
g_signal_connect(
|
||||
popup,
|
||||
"focus-out-event",
|
||||
G_CALLBACK(on_popup_window_focus_out),
|
||||
NULL
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
popup = gtk_popover_new(owner);
|
||||
}
|
||||
|
||||
return popup;
|
||||
}
|
||||
|
||||
void wintc_dpa_show_popup(
|
||||
GtkWidget* popup,
|
||||
GtkWidget* owner
|
||||
)
|
||||
{
|
||||
gint height;
|
||||
gint x;
|
||||
gint y;
|
||||
|
||||
if (wintc_get_display_protocol_in_use() == WINTC_DISPPROTO_X11)
|
||||
{
|
||||
// FIXME: Position is UBER BROKEN here:
|
||||
// - Calculating height too early, should be done in map-event
|
||||
// - Not taking into account screen edges
|
||||
//
|
||||
gtk_window_present_with_time(
|
||||
GTK_WINDOW(popup),
|
||||
GDK_CURRENT_TIME
|
||||
);
|
||||
gtk_widget_show_all(popup);
|
||||
|
||||
height = gtk_widget_get_allocated_height(popup);
|
||||
|
||||
gdk_window_get_origin(
|
||||
gtk_widget_get_window(owner),
|
||||
&x,
|
||||
&y
|
||||
);
|
||||
|
||||
gtk_window_move(
|
||||
GTK_WINDOW(popup),
|
||||
x,
|
||||
y - height // FIXME: We're assuming the bottom of the screen here
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_widget_show_all(popup);
|
||||
}
|
||||
}
|
||||
|
||||
WinTCDisplayProtocol wintc_get_display_protocol_in_use(void)
|
||||
{
|
||||
return s_dispproto;
|
||||
@@ -149,3 +260,16 @@ gboolean wintc_init_display_protocol_apis(void)
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//
|
||||
// CALLBACKS
|
||||
//
|
||||
static gboolean on_popup_window_focus_out(
|
||||
GtkWidget* widget,
|
||||
WINTC_UNUSED(GdkEvent* event),
|
||||
WINTC_UNUSED(gpointer user_data)
|
||||
)
|
||||
{
|
||||
gtk_widget_hide(widget);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,14 @@ typedef enum
|
||||
//
|
||||
// PUBLIC FUNCTIONS
|
||||
//
|
||||
GtkWidget* wintc_dpa_create_popup(
|
||||
GtkWidget* owner
|
||||
);
|
||||
void wintc_dpa_show_popup(
|
||||
GtkWidget* popup,
|
||||
GtkWidget* owner
|
||||
);
|
||||
|
||||
WinTCDisplayProtocol wintc_get_display_protocol_in_use(void);
|
||||
gboolean wintc_init_display_protocol_apis(void);
|
||||
|
||||
|
||||
89
shared/sndapi/CMakeLists.txt
Normal file
89
shared/sndapi/CMakeLists.txt
Normal file
@@ -0,0 +1,89 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(
|
||||
libwintc-sndapi
|
||||
VERSION 1.0
|
||||
DESCRIPTION "Windows Total Conversion system audio abstracton library."
|
||||
LANGUAGES C
|
||||
)
|
||||
|
||||
set(PROJECT_ANYARCH false)
|
||||
set(PROJECT_FREESTATUS true)
|
||||
set(PROJECT_MAINTAINER "Rory Fewell <roryf@oddmatics.uk>")
|
||||
|
||||
set(PROJECT_ROOT ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
include(../../packaging/cmake-inc/common/CMakeLists.txt)
|
||||
include(../../packaging/cmake-inc/libraries/CMakeLists.txt)
|
||||
include(../../packaging/cmake-inc/linking/CMakeLists.txt)
|
||||
include(../../packaging/cmake-inc/packaging/CMakeLists.txt)
|
||||
|
||||
wintc_resolve_library(glib-2.0 GLIB)
|
||||
wintc_resolve_library(gtk+-3.0 GTK3)
|
||||
wintc_resolve_library(libpulse PULSEAUDIO)
|
||||
wintc_resolve_library(libpulse-mainloop-glib PULSEAUDIO_GLIB)
|
||||
wintc_resolve_library(wintc-comgtk WINTC_COMGTK)
|
||||
|
||||
add_library(
|
||||
libwintc-sndapi
|
||||
src/context.c
|
||||
src/context.h
|
||||
src/output.c
|
||||
src/output.h
|
||||
src/volcvt.c
|
||||
src/volcvt.h
|
||||
)
|
||||
|
||||
set_target_properties(
|
||||
libwintc-sndapi
|
||||
PROPERTIES
|
||||
PUBLIC_HEADER public/wintc-sndapi.h
|
||||
SOVERSION 1
|
||||
VERSION ${PROJECT_VERSION}
|
||||
)
|
||||
|
||||
target_compile_options(
|
||||
libwintc-sndapi
|
||||
PRIVATE ${WINTC_COMPILE_OPTIONS}
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
libwintc-sndapi
|
||||
SYSTEM
|
||||
PRIVATE ${GLIB_INCLUDE_DIRS}
|
||||
PRIVATE ${GTK3_INCLUDE_DIRS}
|
||||
PRIVATE ${PULSEAUDIO_INCLUDE_DIRS}
|
||||
PRIVATE ${PULSEAUDIO_GLIB_INCLUDE_DIRS}
|
||||
PRIVATE ${WINTC_COMGTK_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_directories(
|
||||
libwintc-sndapi
|
||||
PRIVATE ${GLIB_LIBRARY_DIRS}
|
||||
PRIVATE ${GTK3_LIBRARY_DIRS}
|
||||
PRIVATE ${PULSEAUDIO_LIBRARY_DIRS}
|
||||
PRIVATE ${PULSEAUDIO_GLIB_LIBRARY_DIRS}
|
||||
PRIVATE ${WINTC_COMGTK_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
libwintc-sndapi
|
||||
PRIVATE ${GLIB_LIBRARIES}
|
||||
PRIVATE ${GTK3_LIBRARIES}
|
||||
PRIVATE ${PULSEAUDIO_LIBRARIES}
|
||||
PRIVATE ${PULSEAUDIO_GLIB_LIBRARIES}
|
||||
PRIVATE ${WINTC_COMGTK_LIBRARIES}
|
||||
)
|
||||
|
||||
# Installation
|
||||
#
|
||||
wintc_configure_and_install_packaging()
|
||||
wintc_add_pkgconfig_install()
|
||||
|
||||
install(
|
||||
TARGETS libwintc-sndapi
|
||||
LIBRARY DESTINATION ${LIB_DIR}
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
)
|
||||
5
shared/sndapi/README.MD
Normal file
5
shared/sndapi/README.MD
Normal file
@@ -0,0 +1,5 @@
|
||||
# libwintc-sndapi
|
||||
This directory contains the source-code for the system audio abstraction library.
|
||||
|
||||
## Purpose
|
||||
This library exposes a simple, straight-forward API for managing the system audio. It hides away the underlying audio client/server communcation (eg. PulseAudio) to allow programs to get easy access to, and manage, audio devices.
|
||||
4
shared/sndapi/deps
Normal file
4
shared/sndapi/deps
Normal file
@@ -0,0 +1,4 @@
|
||||
bt,rt:glib2
|
||||
bt,rt:gtk3
|
||||
bt,rt:pulseaudio
|
||||
bt,rt:wintc-comgtk
|
||||
63
shared/sndapi/public/wintc-sndapi.h
Normal file
63
shared/sndapi/public/wintc-sndapi.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#ifndef __WINTC_SNDAPI_H__
|
||||
#define __WINTC_SNDAPI_H__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
//
|
||||
// Output
|
||||
//
|
||||
typedef struct _WinTCSndApiOutputClass WinTCSndApiOutputClass;
|
||||
typedef struct _WinTCSndApiOutput WinTCSndApiOutput;
|
||||
|
||||
#define TYPE_WINTC_SNDAPI_OUTPUT (wintc_sndapi_output_get_type())
|
||||
#define WINTC_SNDAPI_OUTPUT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_WINTC_SNDAPI_OUTPUT, WinTCSndApiOutput))
|
||||
#define WINTC_SNDAPI_OUTPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_WINTC_SNDAPI_OUTPUT, WinTCSndApiOutputClass))
|
||||
#define IS_WINTC_SNDAPI_OUTPUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_WINTC_SNDAPI_OUTPUT))
|
||||
#define IS_WINTC_SNDAPI_OUTPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_WINTC_SNDAPI_OUTPUT))
|
||||
#define WINTC_SNDAPI_OUTPUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_WINTC_SNDAPI_OUTPUT, WinTCSndApiOutput))
|
||||
|
||||
GType wintc_sndapi_output_get_type(void) G_GNUC_CONST;
|
||||
|
||||
gdouble wintc_sndapi_output_get_volume(
|
||||
WinTCSndApiOutput* output
|
||||
);
|
||||
gboolean wintc_sndapi_output_is_muted(
|
||||
WinTCSndApiOutput* output
|
||||
);
|
||||
void wintc_sndapi_output_set_muted(
|
||||
WinTCSndApiOutput* output,
|
||||
gboolean muted
|
||||
);
|
||||
void wintc_sndapi_output_set_volume(
|
||||
WinTCSndApiOutput* output,
|
||||
gdouble new_volume
|
||||
);
|
||||
|
||||
//
|
||||
// Context
|
||||
//
|
||||
typedef struct _WinTCSndApiContextClass WinTCSndApiContextClass;
|
||||
typedef struct _WinTCSndApiContext WinTCSndApiContext;
|
||||
|
||||
#define TYPE_WINTC_SNDAPI_CONTEXT (wintc_sndapi_context_get_type())
|
||||
#define WINTC_SNDAPI_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_WINTC_SNDAPI_CONTEXT, WinTCSndApiContext))
|
||||
#define WINTC_SNDAPI_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_WINTC_SNDAPI_CONTEXT, WinTCSndApiContextClass))
|
||||
#define IS_WINTC_SNDAPI_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_WINTC_SNDAPI_CONTEXT))
|
||||
#define IS_WINTC_SNDAPI_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_WINTC_SNDAPI_CONTEXT))
|
||||
#define WINTC_SNDAPI_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_WINTC_SNDAPI_CONTEXT, WinTCSndApiContext))
|
||||
|
||||
GType wintc_sndapi_context_get_type(void) G_GNUC_CONST;
|
||||
|
||||
WinTCSndApiContext* wintc_sndapi_context_new(void);
|
||||
|
||||
void wintc_sndapi_context_connect(
|
||||
WinTCSndApiContext* ctx
|
||||
);
|
||||
WinTCSndApiOutput* wintc_sndapi_context_get_default_output(
|
||||
WinTCSndApiContext* ctx
|
||||
);
|
||||
gboolean wintc_sndapi_context_is_connected(
|
||||
WinTCSndApiContext* ctx
|
||||
);
|
||||
|
||||
#endif
|
||||
427
shared/sndapi/src/context.c
Normal file
427
shared/sndapi/src/context.c
Normal file
@@ -0,0 +1,427 @@
|
||||
#include <glib.h>
|
||||
#include <pulse/pulseaudio.h>
|
||||
#include <pulse/glib-mainloop.h>
|
||||
#include <wintc-comgtk.h>
|
||||
|
||||
#include "context.h"
|
||||
#include "output.h"
|
||||
|
||||
//
|
||||
// PRIVATE ENUMS
|
||||
//
|
||||
enum
|
||||
{
|
||||
SIGNAL_CONNECTED_CHANGED = 0,
|
||||
SIGNAL_DEFAULT_OUTPUT_CHANGED,
|
||||
N_SIGNALS
|
||||
};
|
||||
|
||||
//
|
||||
// STATIC DATA
|
||||
//
|
||||
static gint wintc_sndapi_context_signals[N_SIGNALS] = { 0 };
|
||||
|
||||
//
|
||||
// GTK OOP CLASS/INSTANCE DEFINITIONS
|
||||
//
|
||||
struct _WinTCSndApiContextClass
|
||||
{
|
||||
GObjectClass __parent__;
|
||||
};
|
||||
|
||||
struct _WinTCSndApiContext
|
||||
{
|
||||
GObject __parent__;
|
||||
|
||||
// PulseAudio stuff
|
||||
//
|
||||
pa_context* pulse_context;
|
||||
pa_glib_mainloop* pulse_mainloop;
|
||||
|
||||
gboolean sink_default_changed;
|
||||
gchar* sink_default_name;
|
||||
WinTCSndApiOutput* sink_default;
|
||||
};
|
||||
|
||||
//
|
||||
// FORWARD DECLARATIONS
|
||||
//
|
||||
static void wintc_sndapi_context_dispose(
|
||||
GObject* object
|
||||
);
|
||||
static void wintc_sndapi_context_finalize(
|
||||
GObject* object
|
||||
);
|
||||
|
||||
static void wintc_sndapi_context_emit_connected_changed(
|
||||
WinTCSndApiContext* ctx
|
||||
);
|
||||
static void wintc_sndapi_context_update_from_pa(
|
||||
WinTCSndApiContext* ctx
|
||||
);
|
||||
|
||||
static void sndctx_pulse_server_info_cb(
|
||||
pa_context* c,
|
||||
const pa_server_info* i,
|
||||
void* userdata
|
||||
);
|
||||
static void sndctx_pulse_sink_info_update_default_cb(
|
||||
pa_context* c,
|
||||
const pa_sink_info* i,
|
||||
int eol,
|
||||
void* userdata
|
||||
);
|
||||
static void sndctx_pulse_state_cb(
|
||||
pa_context* c,
|
||||
void* userdata
|
||||
);
|
||||
static void sndctx_pulse_subscribe_cb(
|
||||
pa_context* c,
|
||||
pa_subscription_event_type_t t,
|
||||
uint32_t idx,
|
||||
void* userdata
|
||||
);
|
||||
|
||||
//
|
||||
// GTK TYPE DEFINITIONS & CTORS
|
||||
//
|
||||
G_DEFINE_TYPE(
|
||||
WinTCSndApiContext,
|
||||
wintc_sndapi_context,
|
||||
G_TYPE_OBJECT
|
||||
)
|
||||
|
||||
static void wintc_sndapi_context_class_init(
|
||||
WinTCSndApiContextClass* klass
|
||||
)
|
||||
{
|
||||
GObjectClass* object_class = G_OBJECT_CLASS(klass);
|
||||
|
||||
object_class->dispose = wintc_sndapi_context_dispose;
|
||||
object_class->finalize = wintc_sndapi_context_finalize;
|
||||
|
||||
wintc_sndapi_context_signals[SIGNAL_CONNECTED_CHANGED] =
|
||||
g_signal_new(
|
||||
"connected-changed",
|
||||
G_TYPE_FROM_CLASS(object_class),
|
||||
G_SIGNAL_RUN_FIRST,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
g_cclosure_marshal_VOID__BOOLEAN,
|
||||
G_TYPE_NONE,
|
||||
1,
|
||||
G_TYPE_BOOLEAN
|
||||
);
|
||||
wintc_sndapi_context_signals[SIGNAL_DEFAULT_OUTPUT_CHANGED] =
|
||||
g_signal_new(
|
||||
"default-output-changed",
|
||||
G_TYPE_FROM_CLASS(object_class),
|
||||
G_SIGNAL_RUN_FIRST,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
static void wintc_sndapi_context_init(
|
||||
WinTCSndApiContext* self
|
||||
)
|
||||
{
|
||||
// Set up PulseAudio stuff for connection later
|
||||
//
|
||||
self->pulse_mainloop = pa_glib_mainloop_new(NULL);
|
||||
self->pulse_context = pa_context_new(
|
||||
pa_glib_mainloop_get_api(self->pulse_mainloop),
|
||||
"wintc-sndapi" // FIXME: Should be client name?
|
||||
);
|
||||
|
||||
pa_context_set_state_callback(
|
||||
self->pulse_context,
|
||||
sndctx_pulse_state_cb,
|
||||
self
|
||||
);
|
||||
|
||||
// Initial stuff
|
||||
//
|
||||
self->sink_default_changed = FALSE;
|
||||
self->sink_default_name = NULL;
|
||||
self->sink_default = NULL;
|
||||
}
|
||||
|
||||
//
|
||||
// CLASS VIRTUAL METHODS
|
||||
//
|
||||
static void wintc_sndapi_context_dispose(
|
||||
GObject* object
|
||||
)
|
||||
{
|
||||
WinTCSndApiContext* ctx = WINTC_SNDAPI_CONTEXT(object);
|
||||
|
||||
g_clear_object(&(ctx->sink_default));
|
||||
|
||||
(G_OBJECT_CLASS(wintc_sndapi_context_parent_class))->dispose(object);
|
||||
}
|
||||
|
||||
static void wintc_sndapi_context_finalize(
|
||||
GObject* object
|
||||
)
|
||||
{
|
||||
WinTCSndApiContext* ctx = WINTC_SNDAPI_CONTEXT(object);
|
||||
|
||||
g_free(ctx->sink_default_name);
|
||||
|
||||
(G_OBJECT_CLASS(wintc_sndapi_context_parent_class))->finalize(object);
|
||||
}
|
||||
|
||||
//
|
||||
// PUBLIC FUNCTIONS
|
||||
//
|
||||
WinTCSndApiContext* wintc_sndapi_context_new()
|
||||
{
|
||||
return WINTC_SNDAPI_CONTEXT(
|
||||
g_object_new(
|
||||
TYPE_WINTC_SNDAPI_CONTEXT,
|
||||
NULL
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void wintc_sndapi_context_connect(
|
||||
WinTCSndApiContext* ctx
|
||||
)
|
||||
{
|
||||
if (wintc_sndapi_context_is_connected(ctx))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gint err =
|
||||
pa_context_connect(
|
||||
ctx->pulse_context,
|
||||
NULL,
|
||||
PA_CONTEXT_NOFAIL,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (err < 0)
|
||||
{
|
||||
g_warning("sndapi: PA connect failure: %s", pa_strerror(err));
|
||||
}
|
||||
}
|
||||
|
||||
WinTCSndApiOutput* wintc_sndapi_context_get_default_output(
|
||||
WinTCSndApiContext* ctx
|
||||
)
|
||||
{
|
||||
return ctx->sink_default;
|
||||
}
|
||||
|
||||
gboolean wintc_sndapi_context_is_connected(
|
||||
WinTCSndApiContext* ctx
|
||||
)
|
||||
{
|
||||
return pa_context_get_state(ctx->pulse_context) == PA_CONTEXT_READY;
|
||||
}
|
||||
|
||||
//
|
||||
// PRIVATE FUNCTIONS
|
||||
//
|
||||
static void wintc_sndapi_context_emit_connected_changed(
|
||||
WinTCSndApiContext* ctx
|
||||
)
|
||||
{
|
||||
g_signal_emit(
|
||||
ctx,
|
||||
wintc_sndapi_context_signals[SIGNAL_CONNECTED_CHANGED],
|
||||
0,
|
||||
wintc_sndapi_context_is_connected(ctx)
|
||||
);
|
||||
}
|
||||
|
||||
static void wintc_sndapi_context_update_from_pa(
|
||||
WinTCSndApiContext* ctx
|
||||
)
|
||||
{
|
||||
pa_operation* o;
|
||||
|
||||
if (
|
||||
!(o =
|
||||
pa_context_get_server_info(
|
||||
ctx->pulse_context,
|
||||
sndctx_pulse_server_info_cb,
|
||||
ctx
|
||||
)
|
||||
)
|
||||
)
|
||||
{
|
||||
g_error("%s", "Failed to get server info from PA.");
|
||||
return;
|
||||
}
|
||||
|
||||
pa_operation_unref(o);
|
||||
}
|
||||
|
||||
//
|
||||
// CALLBACKS
|
||||
//
|
||||
static void sndctx_pulse_server_info_cb(
|
||||
pa_context* c,
|
||||
const pa_server_info* i,
|
||||
void* userdata
|
||||
)
|
||||
{
|
||||
WinTCSndApiContext* ctx = WINTC_SNDAPI_CONTEXT(userdata);
|
||||
pa_operation* o;
|
||||
|
||||
if (g_strcmp0(i->default_sink_name, ctx->sink_default_name) != 0)
|
||||
{
|
||||
ctx->sink_default_changed = TRUE;
|
||||
|
||||
g_clear_object(&(ctx->sink_default));
|
||||
g_free(ctx->sink_default_name);
|
||||
ctx->sink_default_name = g_strdup(i->default_sink_name);
|
||||
}
|
||||
|
||||
if (ctx->sink_default_name == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!(o =
|
||||
pa_context_get_sink_info_by_name(
|
||||
c,
|
||||
ctx->sink_default_name,
|
||||
sndctx_pulse_sink_info_update_default_cb,
|
||||
ctx
|
||||
)
|
||||
)
|
||||
)
|
||||
{
|
||||
g_error("%s", "Failed to get default sink info from PA.");
|
||||
}
|
||||
|
||||
pa_operation_unref(o);
|
||||
}
|
||||
|
||||
static void sndctx_pulse_sink_info_update_default_cb(
|
||||
pa_context* c,
|
||||
const pa_sink_info* i,
|
||||
WINTC_UNUSED(int eol),
|
||||
void* userdata
|
||||
)
|
||||
{
|
||||
WinTCSndApiContext* ctx = WINTC_SNDAPI_CONTEXT(userdata);
|
||||
|
||||
if (i == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx->sink_default_changed)
|
||||
{
|
||||
ctx->sink_default = wintc_sndapi_output_new(c, i->index);
|
||||
|
||||
g_signal_emit(
|
||||
ctx,
|
||||
wintc_sndapi_context_signals[SIGNAL_DEFAULT_OUTPUT_CHANGED],
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
wintc_sndapi_output_update_from_sink_info(
|
||||
ctx->sink_default,
|
||||
i
|
||||
);
|
||||
}
|
||||
|
||||
static void sndctx_pulse_state_cb(
|
||||
pa_context* c,
|
||||
void* userdata
|
||||
)
|
||||
{
|
||||
WinTCSndApiContext* ctx = WINTC_SNDAPI_CONTEXT(userdata);
|
||||
|
||||
gboolean connected_changed = FALSE;
|
||||
|
||||
switch (pa_context_get_state(c))
|
||||
{
|
||||
case PA_CONTEXT_UNCONNECTED:
|
||||
WINTC_LOG_DEBUG("%s", "PA unconnected.");
|
||||
connected_changed = TRUE;
|
||||
break;
|
||||
|
||||
case PA_CONTEXT_CONNECTING:
|
||||
WINTC_LOG_DEBUG("%s", "SNDAPI: PA connecting...");
|
||||
break;
|
||||
|
||||
case PA_CONTEXT_AUTHORIZING:
|
||||
WINTC_LOG_DEBUG("%s", "SNDAPI: PA authorizing...");
|
||||
break;
|
||||
|
||||
case PA_CONTEXT_SETTING_NAME:
|
||||
WINTC_LOG_DEBUG("%s", "SNDAPI: PA setting name...");
|
||||
break;
|
||||
|
||||
case PA_CONTEXT_READY:
|
||||
WINTC_LOG_DEBUG("%s", "SNDAPI: PA ready.");
|
||||
connected_changed = TRUE;
|
||||
|
||||
pa_context_subscribe(
|
||||
c,
|
||||
PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SERVER,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
pa_context_set_subscribe_callback(
|
||||
c,
|
||||
sndctx_pulse_subscribe_cb,
|
||||
ctx
|
||||
);
|
||||
|
||||
wintc_sndapi_context_update_from_pa(ctx);
|
||||
|
||||
break;
|
||||
|
||||
case PA_CONTEXT_FAILED:
|
||||
WINTC_LOG_DEBUG("%s", "SNDAPI: PA failed.");
|
||||
connected_changed = TRUE;
|
||||
break;
|
||||
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
WINTC_LOG_DEBUG("%s", "SNDAPI: PA terminated.");
|
||||
connected_changed = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (connected_changed)
|
||||
{
|
||||
wintc_sndapi_context_emit_connected_changed(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void sndctx_pulse_subscribe_cb(
|
||||
WINTC_UNUSED(pa_context* c),
|
||||
pa_subscription_event_type_t t,
|
||||
WINTC_UNUSED(uint32_t idx),
|
||||
void* userdata
|
||||
)
|
||||
{
|
||||
WinTCSndApiContext* ctx = WINTC_SNDAPI_CONTEXT(userdata);
|
||||
|
||||
switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK)
|
||||
{
|
||||
case PA_SUBSCRIPTION_EVENT_SERVER:
|
||||
case PA_SUBSCRIPTION_EVENT_SINK:
|
||||
wintc_sndapi_context_update_from_pa(ctx);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_warning("%s", "sndapi: unknown event received from PulseAudio");
|
||||
break;
|
||||
}
|
||||
}
|
||||
38
shared/sndapi/src/context.h
Normal file
38
shared/sndapi/src/context.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef __CONTEXT_H__
|
||||
#define __CONTEXT_H__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "output.h"
|
||||
|
||||
//
|
||||
// GTK OOP BOILERPLATE
|
||||
//
|
||||
typedef struct _WinTCSndApiContextClass WinTCSndApiContextClass;
|
||||
typedef struct _WinTCSndApiContext WinTCSndApiContext;
|
||||
|
||||
#define TYPE_WINTC_SNDAPI_CONTEXT (wintc_sndapi_context_get_type())
|
||||
#define WINTC_SNDAPI_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_WINTC_SNDAPI_CONTEXT, WinTCSndApiContext))
|
||||
#define WINTC_SNDAPI_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_WINTC_SNDAPI_CONTEXT, WinTCSndApiContextClass))
|
||||
#define IS_WINTC_SNDAPI_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_WINTC_SNDAPI_CONTEXT))
|
||||
#define IS_WINTC_SNDAPI_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_WINTC_SNDAPI_CONTEXT))
|
||||
#define WINTC_SNDAPI_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_WINTC_SNDAPI_CONTEXT, WinTCSndApiContext))
|
||||
|
||||
GType wintc_sndapi_context_get_type(void) G_GNUC_CONST;
|
||||
|
||||
//
|
||||
// PUBLIC FUNCTIONS
|
||||
//
|
||||
WinTCSndApiContext* wintc_sndapi_context_new(void);
|
||||
|
||||
void wintc_sndapi_context_connect(
|
||||
WinTCSndApiContext* ctx
|
||||
);
|
||||
WinTCSndApiOutput* wintc_sndapi_context_get_default_output(
|
||||
WinTCSndApiContext* ctx
|
||||
);
|
||||
gboolean wintc_sndapi_context_is_connected(
|
||||
WinTCSndApiContext* ctx
|
||||
);
|
||||
|
||||
#endif
|
||||
519
shared/sndapi/src/output.c
Normal file
519
shared/sndapi/src/output.c
Normal file
@@ -0,0 +1,519 @@
|
||||
#include <glib.h>
|
||||
#include <pulse/pulseaudio.h>
|
||||
#include <wintc-comgtk.h>
|
||||
|
||||
#include "context.h"
|
||||
#include "output.h"
|
||||
#include "volcvt.h"
|
||||
|
||||
//
|
||||
// GTK OOP BOILERPLATE
|
||||
//
|
||||
enum
|
||||
{
|
||||
PROP_PA_CONTEXT = 1,
|
||||
PROP_PA_SINK_ID
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SIGNAL_VOLUME_CHANGED = 0,
|
||||
SIGNAL_MUTED_CHANGED,
|
||||
N_SIGNALS
|
||||
};
|
||||
|
||||
//
|
||||
// STATIC DATA
|
||||
//
|
||||
static gint wintc_sndapi_output_signals[N_SIGNALS] = { 0 };
|
||||
|
||||
//
|
||||
// GTK OOP CLASS/INSTANCE DEFINITIONS
|
||||
//
|
||||
struct _WinTCSndApiOutputClass
|
||||
{
|
||||
GObjectClass __parent__;
|
||||
};
|
||||
|
||||
struct _WinTCSndApiOutput
|
||||
{
|
||||
GObject __parent__;
|
||||
|
||||
pa_context* pulse_context;
|
||||
uint32_t sink_id;
|
||||
gchar* sink_name;
|
||||
|
||||
gboolean muted;
|
||||
gdouble volume;
|
||||
|
||||
gboolean next_muted;
|
||||
gdouble next_volume;
|
||||
};
|
||||
|
||||
//
|
||||
// FORWARD DECLARATIONS
|
||||
//
|
||||
static void wintc_sndapi_output_constructed(
|
||||
GObject* object
|
||||
);
|
||||
static void wintc_sndapi_output_finalize(
|
||||
GObject* object
|
||||
);
|
||||
static void wintc_sndapi_output_get_property(
|
||||
GObject* object,
|
||||
guint prop_id,
|
||||
GValue* value,
|
||||
GParamSpec* pspec
|
||||
);
|
||||
static void wintc_sndapi_output_set_property(
|
||||
GObject* object,
|
||||
guint prop_id,
|
||||
const GValue* value,
|
||||
GParamSpec* pspec
|
||||
);
|
||||
|
||||
static void wintc_sndapi_output_internal_set_muted(
|
||||
WinTCSndApiOutput* output,
|
||||
gboolean muted
|
||||
);
|
||||
static void wintc_sndapi_output_internal_set_volume(
|
||||
WinTCSndApiOutput* output,
|
||||
gdouble volume
|
||||
);
|
||||
|
||||
static void sndopt_pulse_sink_info_set_volume_cb(
|
||||
pa_context* c,
|
||||
const pa_sink_info* i,
|
||||
int eol,
|
||||
void* userdata
|
||||
);
|
||||
static void sndopt_pulse_success_set_mute_cb(
|
||||
pa_context* c,
|
||||
int success,
|
||||
void* userdata
|
||||
);
|
||||
static void sndopt_pulse_success_set_volume_cb(
|
||||
pa_context* c,
|
||||
int success,
|
||||
void* userdata
|
||||
);
|
||||
|
||||
//
|
||||
// GTK TYPE DEFINITIONS & CTORS
|
||||
//
|
||||
G_DEFINE_TYPE(
|
||||
WinTCSndApiOutput,
|
||||
wintc_sndapi_output,
|
||||
G_TYPE_OBJECT
|
||||
)
|
||||
|
||||
static void wintc_sndapi_output_class_init(
|
||||
WinTCSndApiOutputClass* klass
|
||||
)
|
||||
{
|
||||
GObjectClass* object_class = G_OBJECT_CLASS(klass);
|
||||
|
||||
object_class->constructed = wintc_sndapi_output_constructed;
|
||||
object_class->finalize = wintc_sndapi_output_finalize;
|
||||
object_class->get_property = wintc_sndapi_output_get_property;
|
||||
object_class->set_property = wintc_sndapi_output_set_property;
|
||||
|
||||
wintc_sndapi_output_signals[SIGNAL_VOLUME_CHANGED] =
|
||||
g_signal_new(
|
||||
"volume-changed",
|
||||
G_TYPE_FROM_CLASS(object_class),
|
||||
G_SIGNAL_RUN_FIRST,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE,
|
||||
0
|
||||
);
|
||||
wintc_sndapi_output_signals[SIGNAL_MUTED_CHANGED] =
|
||||
g_signal_new(
|
||||
"muted-changed",
|
||||
G_TYPE_FROM_CLASS(object_class),
|
||||
G_SIGNAL_RUN_FIRST,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE,
|
||||
0
|
||||
);
|
||||
|
||||
g_object_class_install_property(
|
||||
object_class,
|
||||
PROP_PA_CONTEXT,
|
||||
g_param_spec_pointer(
|
||||
"pa-context",
|
||||
"PaContext",
|
||||
"The PulseAudio connection context.",
|
||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY
|
||||
)
|
||||
);
|
||||
g_object_class_install_property(
|
||||
object_class,
|
||||
PROP_PA_SINK_ID,
|
||||
g_param_spec_uint(
|
||||
"pa-sink-id",
|
||||
"PaSinkId",
|
||||
"The ID of the PulseAudio sink that the instance represents.",
|
||||
0,
|
||||
G_MAXUINT,
|
||||
0,
|
||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
static void wintc_sndapi_output_init(
|
||||
WINTC_UNUSED(WinTCSndApiOutput* self)
|
||||
) {}
|
||||
|
||||
//
|
||||
// CLASS VIRTUAL METHODS
|
||||
//
|
||||
static void wintc_sndapi_output_constructed(
|
||||
GObject* object
|
||||
)
|
||||
{
|
||||
(G_OBJECT_CLASS(
|
||||
wintc_sndapi_output_parent_class
|
||||
))->constructed(object);
|
||||
}
|
||||
|
||||
static void wintc_sndapi_output_get_property(
|
||||
GObject* object,
|
||||
guint prop_id,
|
||||
GValue* value,
|
||||
GParamSpec* pspec
|
||||
)
|
||||
{
|
||||
WinTCSndApiOutput* output =
|
||||
WINTC_SNDAPI_OUTPUT(object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_PA_SINK_ID:
|
||||
g_value_set_uint(value, output->sink_id);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void wintc_sndapi_output_finalize(
|
||||
GObject* object
|
||||
)
|
||||
{
|
||||
WinTCSndApiOutput* output = WINTC_SNDAPI_OUTPUT(object);
|
||||
|
||||
g_free(output->sink_name);
|
||||
}
|
||||
|
||||
static void wintc_sndapi_output_set_property(
|
||||
GObject* object,
|
||||
guint prop_id,
|
||||
const GValue* value,
|
||||
GParamSpec* pspec
|
||||
)
|
||||
{
|
||||
WinTCSndApiOutput* output =
|
||||
WINTC_SNDAPI_OUTPUT(object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_PA_CONTEXT:
|
||||
output->pulse_context = g_value_get_pointer(value);
|
||||
break;
|
||||
|
||||
case PROP_PA_SINK_ID:
|
||||
output->sink_id = g_value_get_uint(value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// PUBLIC FUNCTIONS
|
||||
//
|
||||
gdouble wintc_sndapi_output_get_volume(
|
||||
WinTCSndApiOutput* output
|
||||
)
|
||||
{
|
||||
return output->volume;
|
||||
}
|
||||
|
||||
gboolean wintc_sndapi_output_is_muted(
|
||||
WinTCSndApiOutput* output
|
||||
)
|
||||
{
|
||||
return output->muted;
|
||||
}
|
||||
|
||||
void wintc_sndapi_output_set_muted(
|
||||
WinTCSndApiOutput* output,
|
||||
gboolean muted
|
||||
)
|
||||
{
|
||||
pa_operation* o;
|
||||
|
||||
output->next_muted = muted;
|
||||
|
||||
if (
|
||||
!(o =
|
||||
pa_context_set_sink_mute_by_index(
|
||||
output->pulse_context,
|
||||
output->sink_id,
|
||||
output->next_muted,
|
||||
sndopt_pulse_success_set_mute_cb,
|
||||
output
|
||||
)
|
||||
)
|
||||
)
|
||||
{
|
||||
g_error("%s", "Failed to set mute state on sink.");
|
||||
return;
|
||||
}
|
||||
|
||||
pa_operation_unref(o);
|
||||
}
|
||||
|
||||
void wintc_sndapi_output_set_volume(
|
||||
WinTCSndApiOutput* output,
|
||||
gdouble new_volume
|
||||
)
|
||||
{
|
||||
pa_operation* o;
|
||||
|
||||
output->next_volume = new_volume;
|
||||
|
||||
if (
|
||||
!(o =
|
||||
pa_context_get_sink_info_by_index(
|
||||
output->pulse_context,
|
||||
output->sink_id,
|
||||
sndopt_pulse_sink_info_set_volume_cb,
|
||||
output
|
||||
)
|
||||
)
|
||||
)
|
||||
{
|
||||
g_error("%s", "Failed to set volume on sink.");
|
||||
return;
|
||||
}
|
||||
|
||||
pa_operation_unref(o);
|
||||
}
|
||||
|
||||
//
|
||||
// INTERNAL FUNCTIONS
|
||||
//
|
||||
WinTCSndApiOutput* wintc_sndapi_output_new(
|
||||
pa_context* pulse_context,
|
||||
uint32_t sink_id
|
||||
)
|
||||
{
|
||||
return WINTC_SNDAPI_OUTPUT(
|
||||
g_object_new(
|
||||
TYPE_WINTC_SNDAPI_OUTPUT,
|
||||
"pa-context", pulse_context,
|
||||
"pa-sink-id", sink_id,
|
||||
NULL
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void wintc_sndapi_output_update_from_sink_info(
|
||||
WinTCSndApiOutput* output,
|
||||
const pa_sink_info* i
|
||||
)
|
||||
{
|
||||
if (g_strcmp0(i->name, output->sink_name) != 0)
|
||||
{
|
||||
WINTC_LOG_DEBUG("Sink name changed to %s", i->name);
|
||||
|
||||
g_free(output->sink_name);
|
||||
output->sink_name = g_strdup(i->name);
|
||||
}
|
||||
|
||||
// Read highest volume
|
||||
//
|
||||
gdouble vol = 0.0f;
|
||||
|
||||
for (uint8_t idx = 0; idx < i->volume.channels; idx++)
|
||||
{
|
||||
vol =
|
||||
MAX(
|
||||
vol,
|
||||
wintc_sndapi_cvt_pa_volume_to_percent(i->volume.values[idx])
|
||||
);
|
||||
}
|
||||
|
||||
wintc_sndapi_output_internal_set_volume(
|
||||
output,
|
||||
vol
|
||||
);
|
||||
|
||||
// Read muted
|
||||
//
|
||||
wintc_sndapi_output_internal_set_muted(
|
||||
output,
|
||||
!!(i->mute)
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// PRIVATE FUNCTIONS
|
||||
//
|
||||
static void wintc_sndapi_output_internal_set_muted(
|
||||
WinTCSndApiOutput* output,
|
||||
gboolean muted
|
||||
)
|
||||
{
|
||||
WINTC_LOG_DEBUG("SNDAPI: Int mute change to %d", muted);
|
||||
|
||||
output->muted = muted;
|
||||
|
||||
g_signal_emit(
|
||||
output,
|
||||
wintc_sndapi_output_signals[SIGNAL_MUTED_CHANGED],
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
static void wintc_sndapi_output_internal_set_volume(
|
||||
WinTCSndApiOutput* output,
|
||||
gdouble volume
|
||||
)
|
||||
{
|
||||
WINTC_LOG_DEBUG("SNDAPI: Int vol change to %f", volume);
|
||||
|
||||
output->volume = volume;
|
||||
|
||||
g_signal_emit(
|
||||
output,
|
||||
wintc_sndapi_output_signals[SIGNAL_VOLUME_CHANGED],
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// CALLBACKS
|
||||
//
|
||||
static void sndopt_pulse_sink_info_set_volume_cb(
|
||||
pa_context* c,
|
||||
const pa_sink_info* i,
|
||||
WINTC_UNUSED(int eol),
|
||||
void* userdata
|
||||
)
|
||||
{
|
||||
WinTCSndApiOutput* output = WINTC_SNDAPI_OUTPUT(userdata);
|
||||
|
||||
if (i == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pa_volume_t desired_volume = wintc_sndapi_cvt_percent_to_pa_volume(
|
||||
output->next_volume
|
||||
);
|
||||
pa_volume_t highest_volume = pa_cvolume_max(&(i->volume));
|
||||
pa_cvolume new_cvolume = i->volume;
|
||||
gboolean okay = FALSE;
|
||||
|
||||
if (desired_volume == highest_volume)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (desired_volume > highest_volume)
|
||||
{
|
||||
okay =
|
||||
pa_cvolume_inc_clamp(
|
||||
&new_cvolume,
|
||||
desired_volume - highest_volume,
|
||||
PA_VOLUME_NORM
|
||||
) != NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
okay =
|
||||
pa_cvolume_dec(
|
||||
&new_cvolume,
|
||||
highest_volume - desired_volume
|
||||
) != NULL;
|
||||
}
|
||||
|
||||
if (!okay)
|
||||
{
|
||||
g_warning("%s", "Failed to inc/dec volume.");
|
||||
}
|
||||
|
||||
// Actually apply the new volume
|
||||
//
|
||||
pa_operation* o;
|
||||
|
||||
if (
|
||||
!(o =
|
||||
pa_context_set_sink_volume_by_index(
|
||||
c,
|
||||
output->sink_id,
|
||||
&new_cvolume,
|
||||
sndopt_pulse_success_set_volume_cb,
|
||||
output
|
||||
)
|
||||
)
|
||||
)
|
||||
{
|
||||
g_error("%s", "Failed to set cvolume on sink.");
|
||||
return;
|
||||
}
|
||||
|
||||
pa_operation_unref(o);
|
||||
}
|
||||
|
||||
static void sndopt_pulse_success_set_mute_cb(
|
||||
WINTC_UNUSED(pa_context* c),
|
||||
int success,
|
||||
void* userdata
|
||||
)
|
||||
{
|
||||
WinTCSndApiOutput* output = WINTC_SNDAPI_OUTPUT(userdata);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
wintc_sndapi_output_internal_set_muted(
|
||||
output,
|
||||
output->next_muted
|
||||
);
|
||||
}
|
||||
|
||||
static void sndopt_pulse_success_set_volume_cb(
|
||||
WINTC_UNUSED(pa_context* c),
|
||||
int success,
|
||||
void* userdata
|
||||
)
|
||||
{
|
||||
WinTCSndApiOutput* output = WINTC_SNDAPI_OUTPUT(userdata);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
wintc_sndapi_output_internal_set_volume(
|
||||
output,
|
||||
output->next_volume
|
||||
);
|
||||
}
|
||||
53
shared/sndapi/src/output.h
Normal file
53
shared/sndapi/src/output.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef __OUTPUT_H__
|
||||
#define __OUTPUT_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
//
|
||||
// GTK OOP BOILERPLATE
|
||||
//
|
||||
typedef struct _WinTCSndApiOutputClass WinTCSndApiOutputClass;
|
||||
typedef struct _WinTCSndApiOutput WinTCSndApiOutput;
|
||||
|
||||
#define TYPE_WINTC_SNDAPI_OUTPUT (wintc_sndapi_output_get_type())
|
||||
#define WINTC_SNDAPI_OUTPUT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_WINTC_SNDAPI_OUTPUT, WinTCSndApiOutput))
|
||||
#define WINTC_SNDAPI_OUTPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_WINTC_SNDAPI_OUTPUT, WinTCSndApiOutputClass))
|
||||
#define IS_WINTC_SNDAPI_OUTPUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_WINTC_SNDAPI_OUTPUT))
|
||||
#define IS_WINTC_SNDAPI_OUTPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_WINTC_SNDAPI_OUTPUT))
|
||||
#define WINTC_SNDAPI_OUTPUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_WINTC_SNDAPI_OUTPUT, WinTCSndApiOutput))
|
||||
|
||||
GType wintc_sndapi_output_get_type(void) G_GNUC_CONST;
|
||||
|
||||
//
|
||||
// PUBLIC FUNCTIONS
|
||||
//
|
||||
gdouble wintc_sndapi_output_get_volume(
|
||||
WinTCSndApiOutput* output
|
||||
);
|
||||
gboolean wintc_sndapi_output_is_muted(
|
||||
WinTCSndApiOutput* output
|
||||
);
|
||||
void wintc_sndapi_output_set_muted(
|
||||
WinTCSndApiOutput* output,
|
||||
gboolean muted
|
||||
);
|
||||
void wintc_sndapi_output_set_volume(
|
||||
WinTCSndApiOutput* output,
|
||||
gdouble new_volume
|
||||
);
|
||||
|
||||
//
|
||||
// INTERNAL FUNCTIONS
|
||||
//
|
||||
WinTCSndApiOutput* wintc_sndapi_output_new( // Private ctor!!
|
||||
pa_context* pulse_context,
|
||||
uint32_t sink_id
|
||||
);
|
||||
|
||||
void wintc_sndapi_output_update_from_sink_info(
|
||||
WinTCSndApiOutput* output,
|
||||
const pa_sink_info* i
|
||||
);
|
||||
|
||||
#endif
|
||||
33
shared/sndapi/src/volcvt.c
Normal file
33
shared/sndapi/src/volcvt.c
Normal file
@@ -0,0 +1,33 @@
|
||||
#include <glib.h>
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
#include "volcvt.h"
|
||||
|
||||
//
|
||||
// INTERNAL FUNCTIONS
|
||||
//
|
||||
gdouble wintc_sndapi_cvt_pa_volume_to_percent(
|
||||
pa_volume_t volume
|
||||
)
|
||||
{
|
||||
gdouble adjusted_vol;
|
||||
gdouble range;
|
||||
|
||||
adjusted_vol = (gdouble) (volume - PA_VOLUME_MUTED);
|
||||
range = (gdouble) (PA_VOLUME_NORM - PA_VOLUME_MUTED);
|
||||
|
||||
return adjusted_vol / range;
|
||||
}
|
||||
|
||||
pa_volume_t wintc_sndapi_cvt_percent_to_pa_volume(
|
||||
gdouble pct
|
||||
)
|
||||
{
|
||||
gdouble range;
|
||||
pa_volume_t scaled;
|
||||
|
||||
range = (gdouble) (PA_VOLUME_NORM - PA_VOLUME_MUTED);
|
||||
scaled = (pa_volume_t) (pct * range);
|
||||
|
||||
return scaled + PA_VOLUME_MUTED;
|
||||
}
|
||||
17
shared/sndapi/src/volcvt.h
Normal file
17
shared/sndapi/src/volcvt.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef __VOLCVT_H__
|
||||
#define __VOLCVT_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
//
|
||||
// INTERNAL FUNCTIONS
|
||||
//
|
||||
gdouble wintc_sndapi_cvt_pa_volume_to_percent(
|
||||
pa_volume_t volume
|
||||
);
|
||||
pa_volume_t wintc_sndapi_cvt_percent_to_pa_volume(
|
||||
gdouble pct
|
||||
);
|
||||
|
||||
#endif
|
||||
@@ -30,6 +30,7 @@ wintc_resolve_library(wintc-comgtk WINTC_COMGTK)
|
||||
wintc_resolve_library(wintc-exec WINTC_EXEC)
|
||||
wintc_resolve_library(wintc-shelldpa WINTC_SHELLDPA)
|
||||
wintc_resolve_library(wintc-shllang WINTC_SHLLANG)
|
||||
wintc_resolve_library(wintc-sndapi WINTC_SNDAPI)
|
||||
|
||||
wintc_compile_resources()
|
||||
wintc_create_meta_h()
|
||||
@@ -57,10 +58,14 @@ add_executable(
|
||||
src/start/startmenuitem.h
|
||||
src/start/util.c
|
||||
src/start/util.h
|
||||
src/systray/behaviour.c
|
||||
src/systray/behaviour.h
|
||||
src/systray/clock.c
|
||||
src/systray/clock.h
|
||||
src/systray/notifarea.c
|
||||
src/systray/notifarea.h
|
||||
src/systray/volume.c
|
||||
src/systray/volume.h
|
||||
src/taskbuttons/taskbuttonbar.c
|
||||
src/taskbuttons/taskbuttonbar.h
|
||||
src/taskbuttons/windowmonitor.c
|
||||
@@ -84,6 +89,7 @@ target_include_directories(
|
||||
PRIVATE ${WINTC_EXEC_INCLUDE_DIRS}
|
||||
PRIVATE ${WINTC_SHELLDPA_INCLUDE_DIRS}
|
||||
PRIVATE ${WINTC_SHLLANG_INCLUDE_DIRS}
|
||||
PRIVATE ${WINTC_SNDAPI_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_directories(
|
||||
@@ -97,6 +103,7 @@ target_link_directories(
|
||||
PRIVATE ${WINTC_EXEC_LIBRARY_DIRS}
|
||||
PRIVATE ${WINTC_SHELLDPA_LIBRARY_DIRS}
|
||||
PRIVATE ${WINTC_SHLLANG_LIBRARY_DIRS}
|
||||
PRIVATE ${WINTC_SNDAPI_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
@@ -110,6 +117,7 @@ target_link_libraries(
|
||||
PRIVATE ${WINTC_EXEC_LIBRARIES}
|
||||
PRIVATE ${WINTC_SHELLDPA_LIBRARIES}
|
||||
PRIVATE ${WINTC_SHLLANG_LIBRARIES}
|
||||
PRIVATE ${WINTC_SNDAPI_LIBRARIES}
|
||||
)
|
||||
|
||||
add_dependencies(
|
||||
|
||||
@@ -7,3 +7,4 @@ bt,rt:wintc-comgtk
|
||||
bt,rt:wintc-exec
|
||||
bt,rt:wintc-shelldpa
|
||||
bt,rt:wintc-shllang
|
||||
bt,rt:wintc-sndapi
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
<gresources>
|
||||
<gresource prefix="/uk/oddmatics/wintc/taskband">
|
||||
<file>start-menu.css</file>
|
||||
<file>volume-popup.css</file>
|
||||
</gresource>
|
||||
</gresources>
|
||||
|
||||
15
shell/taskband/src/res/volume-popup.css
Normal file
15
shell/taskband/src/res/volume-popup.css
Normal file
@@ -0,0 +1,15 @@
|
||||
.wintc-volmgmt > box > label
|
||||
{
|
||||
margin: 10px 20px;
|
||||
}
|
||||
|
||||
.wintc-volmgmt > box > scale
|
||||
{
|
||||
margin: 4px 0px;
|
||||
min-height: 65px;
|
||||
}
|
||||
|
||||
.wintc-volmgmt > box > checkbutton
|
||||
{
|
||||
margin: 12px 6px 4px;
|
||||
}
|
||||
173
shell/taskband/src/systray/behaviour.c
Normal file
173
shell/taskband/src/systray/behaviour.c
Normal file
@@ -0,0 +1,173 @@
|
||||
#include <glib.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "behaviour.h"
|
||||
|
||||
//
|
||||
// PRIVATE ENUMS
|
||||
//
|
||||
enum
|
||||
{
|
||||
PROP_WIDGET_NOTIF = 1,
|
||||
PROP_ICON_NAME
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SIGNAL_ICON_CHANGED = 0,
|
||||
N_SIGNALS
|
||||
};
|
||||
|
||||
//
|
||||
// STATIC DATA
|
||||
//
|
||||
static gint wintc_notification_behaviour_signals[N_SIGNALS] = { 0 };
|
||||
|
||||
//
|
||||
// FORWARD DECLARATIONS
|
||||
//
|
||||
static void wintc_notification_behaviour_get_property(
|
||||
GObject* object,
|
||||
guint prop_id,
|
||||
GValue* value,
|
||||
GParamSpec* pspec
|
||||
);
|
||||
static void wintc_notification_behaviour_set_property(
|
||||
GObject* object,
|
||||
guint prop_id,
|
||||
const GValue* value,
|
||||
GParamSpec* pspec
|
||||
);
|
||||
|
||||
//
|
||||
// GTK TYPE DEFINITIONS & CTORS
|
||||
//
|
||||
G_DEFINE_TYPE(
|
||||
WinTCNotificationBehaviour,
|
||||
wintc_notification_behaviour,
|
||||
G_TYPE_OBJECT
|
||||
)
|
||||
|
||||
static void wintc_notification_behaviour_class_init(
|
||||
WinTCNotificationBehaviourClass* klass
|
||||
)
|
||||
{
|
||||
GObjectClass* object_class = G_OBJECT_CLASS(klass);
|
||||
|
||||
object_class->get_property = wintc_notification_behaviour_get_property;
|
||||
object_class->set_property = wintc_notification_behaviour_set_property;
|
||||
|
||||
wintc_notification_behaviour_signals[SIGNAL_ICON_CHANGED] =
|
||||
g_signal_new(
|
||||
"icon-changed",
|
||||
G_TYPE_FROM_CLASS(object_class),
|
||||
G_SIGNAL_RUN_FIRST,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE,
|
||||
0
|
||||
);
|
||||
|
||||
g_object_class_install_property(
|
||||
object_class,
|
||||
PROP_WIDGET_NOTIF,
|
||||
g_param_spec_object(
|
||||
"widget-notif",
|
||||
"WidgetNotif",
|
||||
"The GTK widget that hosts the notification icon.",
|
||||
GTK_TYPE_WIDGET,
|
||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY
|
||||
)
|
||||
);
|
||||
g_object_class_install_property(
|
||||
object_class,
|
||||
PROP_ICON_NAME,
|
||||
g_param_spec_string(
|
||||
"icon-name",
|
||||
"IconName",
|
||||
"The icon name to display in the notification area.",
|
||||
NULL,
|
||||
G_PARAM_WRITABLE
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
static void wintc_notification_behaviour_init(
|
||||
WinTCNotificationBehaviour* self
|
||||
)
|
||||
{
|
||||
self->icon_name = NULL;
|
||||
self->widget_notif = NULL;
|
||||
}
|
||||
|
||||
//
|
||||
// CLASS VIRTUAL METHODS
|
||||
//
|
||||
static void wintc_notification_behaviour_get_property(
|
||||
GObject* object,
|
||||
guint prop_id,
|
||||
GValue* value,
|
||||
GParamSpec* pspec
|
||||
)
|
||||
{
|
||||
WinTCNotificationBehaviour* behaviour =
|
||||
WINTC_NOTIFICATION_BEHAVIOUR(object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_ICON_NAME:
|
||||
g_value_set_string(value, behaviour->icon_name);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void wintc_notification_behaviour_set_property(
|
||||
GObject* object,
|
||||
guint prop_id,
|
||||
const GValue* value,
|
||||
GParamSpec* pspec
|
||||
)
|
||||
{
|
||||
WinTCNotificationBehaviour* behaviour =
|
||||
WINTC_NOTIFICATION_BEHAVIOUR(object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_ICON_NAME:
|
||||
g_free(behaviour->icon_name);
|
||||
behaviour->icon_name = g_strdup(g_value_get_string(value));
|
||||
|
||||
g_signal_emit(
|
||||
behaviour,
|
||||
wintc_notification_behaviour_signals[SIGNAL_ICON_CHANGED],
|
||||
0
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
case PROP_WIDGET_NOTIF:
|
||||
behaviour->widget_notif = GTK_WIDGET(g_value_get_object(value));
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// PUBLIC FUNCTIONS
|
||||
//
|
||||
const gchar* wintc_notification_behaviour_get_icon_name(
|
||||
WinTCNotificationBehaviour* behaviour
|
||||
)
|
||||
{
|
||||
return behaviour->icon_name;
|
||||
}
|
||||
39
shell/taskband/src/systray/behaviour.h
Normal file
39
shell/taskband/src/systray/behaviour.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef __BEHAVIOUR_H__
|
||||
#define __BEHAVIOUR_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
//
|
||||
// GTK OOP BOILERPLATE
|
||||
//
|
||||
typedef struct _WinTCNotificationBehaviourClass
|
||||
{
|
||||
GObjectClass __parent__;
|
||||
} WinTCNotificationBehaviourClass;
|
||||
|
||||
typedef struct _WinTCNotificationBehaviour
|
||||
{
|
||||
GObject __parent__;
|
||||
|
||||
gchar* icon_name;
|
||||
GtkWidget* widget_notif;
|
||||
} WinTCNotificationBehaviour;
|
||||
|
||||
#define TYPE_WINTC_NOTIFICATION_BEHAVIOUR (wintc_notification_behaviour_get_type())
|
||||
#define WINTC_NOTIFICATION_BEHAVIOUR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_WINTC_NOTIFICATION_BEHAVIOUR, WinTCNotificationBehaviour))
|
||||
#define WINTC_NOTIFICATION_BEHAVIOUR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_WINTC_NOTIFICATION_BEHAVIOUR, WinTCNotificationBehaviourClass))
|
||||
#define IS_WINTC_NOTIFICATION_BEHAVIOUR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_WINTC_NOTIFICATION_BEHAVIOUR))
|
||||
#define IS_WINTC_NOTIFICATION_BEHAVIOUR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_WINTC_NOTIFICATION_BEHAVIOUR))
|
||||
#define WINTC_NOTIFICATION_BEHAVIOUR_GET_CLASS (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_WINTC_NOTIFICATION_BEHAVIOUR, WinTCNotificationBehaviour))
|
||||
|
||||
GType wintc_notification_behaviour_get_type(void) G_GNUC_CONST;
|
||||
|
||||
//
|
||||
// PUBLIC FUNCTIONS
|
||||
//
|
||||
const gchar* wintc_notification_behaviour_get_icon_name(
|
||||
WinTCNotificationBehaviour* behaviour
|
||||
);
|
||||
|
||||
#endif
|
||||
@@ -3,40 +3,48 @@
|
||||
|
||||
#include "clock.h"
|
||||
|
||||
//
|
||||
// PRIVATE ENUMS
|
||||
//
|
||||
enum
|
||||
{
|
||||
PROP_LABEL_TARGET = 1,
|
||||
};
|
||||
|
||||
//
|
||||
// GTK OOP CLASS/INSTANCE DEFINITIONS
|
||||
//
|
||||
struct _TrayClockPrivate
|
||||
struct _WinTCClockRunnerClass
|
||||
{
|
||||
TrayClock* tray_clock;
|
||||
|
||||
guint clock_source_id;
|
||||
GObjectClass __parent__;
|
||||
};
|
||||
|
||||
struct _TrayClockClass
|
||||
struct _WinTCClockRunner
|
||||
{
|
||||
GtkLabelClass __parent__;
|
||||
};
|
||||
GObject __parent__;
|
||||
|
||||
struct _TrayClock
|
||||
{
|
||||
GtkLabel __parent__;
|
||||
|
||||
TrayClockPrivate* priv;
|
||||
guint clock_source_id;
|
||||
GtkLabel* label_target;
|
||||
};
|
||||
|
||||
//
|
||||
// FORWARD DECLARATIONS
|
||||
//
|
||||
static void tray_clock_finalize(
|
||||
static void wintc_clock_runner_finalize(
|
||||
GObject* object
|
||||
);
|
||||
|
||||
static void tray_clock_launch_time(
|
||||
TrayClock* tray_clock
|
||||
static void wintc_clock_runner_set_property(
|
||||
GObject* object,
|
||||
guint prop_id,
|
||||
const GValue* value,
|
||||
GParamSpec* pspec
|
||||
);
|
||||
static void tray_clock_update_time(
|
||||
TrayClock* tray_clock
|
||||
|
||||
static void wintc_clock_runner_launch_time(
|
||||
WinTCClockRunner* tray_clock
|
||||
);
|
||||
static void wintc_clock_runner_update_time(
|
||||
WinTCClockRunner* tray_clock
|
||||
);
|
||||
|
||||
static gboolean on_clock_timeout_elapsed(
|
||||
@@ -49,34 +57,38 @@ static gboolean on_sync_timeout_elapsed(
|
||||
//
|
||||
// GTK TYPE DEFINITION & CTORS
|
||||
//
|
||||
G_DEFINE_TYPE_WITH_CODE(
|
||||
TrayClock,
|
||||
tray_clock,
|
||||
GTK_TYPE_LABEL,
|
||||
G_ADD_PRIVATE(TrayClock)
|
||||
G_DEFINE_TYPE(
|
||||
WinTCClockRunner,
|
||||
wintc_clock_runner,
|
||||
G_TYPE_OBJECT
|
||||
)
|
||||
|
||||
static void tray_clock_class_init(
|
||||
TrayClockClass* klass
|
||||
static void wintc_clock_runner_class_init(
|
||||
WinTCClockRunnerClass* klass
|
||||
)
|
||||
{
|
||||
GObjectClass* gclass = G_OBJECT_CLASS(klass);
|
||||
GObjectClass* object_class = G_OBJECT_CLASS(klass);
|
||||
|
||||
gclass->finalize = tray_clock_finalize;
|
||||
object_class->finalize = wintc_clock_runner_finalize;
|
||||
object_class->set_property = wintc_clock_runner_set_property;
|
||||
|
||||
g_object_class_install_property(
|
||||
object_class,
|
||||
PROP_LABEL_TARGET,
|
||||
g_param_spec_object(
|
||||
"label-target",
|
||||
"LabelTarget",
|
||||
"The target label to manage.",
|
||||
GTK_TYPE_LABEL,
|
||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
static void tray_clock_init(
|
||||
TrayClock* self
|
||||
static void wintc_clock_runner_init(
|
||||
WinTCClockRunner* self
|
||||
)
|
||||
{
|
||||
self->priv = tray_clock_get_instance_private(self);
|
||||
|
||||
// Add style class
|
||||
//
|
||||
GtkStyleContext* style = gtk_widget_get_style_context(GTK_WIDGET(self));
|
||||
|
||||
gtk_style_context_add_class(style, "clock");
|
||||
|
||||
// Establish clock - if we're not dead on the minute then delay launch to sync
|
||||
// up
|
||||
//
|
||||
@@ -85,7 +97,7 @@ static void tray_clock_init(
|
||||
|
||||
if (delay > 0)
|
||||
{
|
||||
tray_clock_update_time(self);
|
||||
wintc_clock_runner_update_time(self);
|
||||
|
||||
g_timeout_add_seconds_full(
|
||||
G_PRIORITY_DEFAULT,
|
||||
@@ -97,66 +109,107 @@ static void tray_clock_init(
|
||||
}
|
||||
else
|
||||
{
|
||||
tray_clock_launch_time(self);
|
||||
wintc_clock_runner_launch_time(self);
|
||||
}
|
||||
|
||||
g_date_time_unref(time);
|
||||
}
|
||||
|
||||
//
|
||||
// FINALIZE
|
||||
// CLASS VIRTUAL METHODS
|
||||
//
|
||||
static void tray_clock_finalize(
|
||||
static void wintc_clock_runner_finalize(
|
||||
GObject* object
|
||||
)
|
||||
{
|
||||
TrayClock* tray_clock = TRAY_CLOCK(object);
|
||||
WinTCClockRunner* clock_runner = WINTC_CLOCK_RUNNER(object);
|
||||
|
||||
if (tray_clock->priv->clock_source_id > 0)
|
||||
if (clock_runner->clock_source_id > 0)
|
||||
{
|
||||
g_source_remove(tray_clock->priv->clock_source_id);
|
||||
g_source_remove(clock_runner->clock_source_id);
|
||||
}
|
||||
|
||||
(*G_OBJECT_CLASS(tray_clock_parent_class)->finalize) (object);
|
||||
(*G_OBJECT_CLASS(wintc_clock_runner_parent_class)->finalize) (object);
|
||||
}
|
||||
|
||||
static void wintc_clock_runner_set_property(
|
||||
GObject* object,
|
||||
guint prop_id,
|
||||
const GValue* value,
|
||||
GParamSpec* pspec
|
||||
)
|
||||
{
|
||||
WinTCClockRunner* clock_runner = WINTC_CLOCK_RUNNER(object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_LABEL_TARGET:
|
||||
clock_runner->label_target =
|
||||
GTK_LABEL(g_value_get_object(value));
|
||||
|
||||
wintc_clock_runner_update_time(clock_runner);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// PUBLIC FUNCTIONS
|
||||
//
|
||||
GtkWidget* tray_clock_new(void)
|
||||
WinTCClockRunner* wintc_clock_runner_new(
|
||||
GtkLabel* label_target
|
||||
)
|
||||
{
|
||||
return GTK_WIDGET(
|
||||
g_object_new(TYPE_TRAY_CLOCK, NULL)
|
||||
return WINTC_CLOCK_RUNNER(
|
||||
g_object_new(
|
||||
TYPE_WINTC_CLOCK_RUNNER,
|
||||
"label-target", label_target,
|
||||
NULL
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// PRIVATE FUNCTIONS
|
||||
//
|
||||
static void tray_clock_launch_time(
|
||||
TrayClock* tray_clock
|
||||
static void wintc_clock_runner_launch_time(
|
||||
WinTCClockRunner* clock_runner
|
||||
)
|
||||
{
|
||||
tray_clock_update_time(tray_clock);
|
||||
wintc_clock_runner_update_time(clock_runner);
|
||||
|
||||
tray_clock->priv->clock_source_id =
|
||||
clock_runner->clock_source_id =
|
||||
g_timeout_add_seconds_full(
|
||||
G_PRIORITY_DEFAULT,
|
||||
60,
|
||||
on_clock_timeout_elapsed,
|
||||
tray_clock,
|
||||
clock_runner,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
||||
static void tray_clock_update_time(
|
||||
TrayClock* tray_clock
|
||||
static void wintc_clock_runner_update_time(
|
||||
WinTCClockRunner* clock_runner
|
||||
)
|
||||
{
|
||||
if (clock_runner->label_target == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Update target label with time
|
||||
//
|
||||
GDateTime* time = g_date_time_new_now_local();
|
||||
gchar* timestr = g_date_time_format(time, "%H:%M");
|
||||
|
||||
gtk_label_set_text(GTK_LABEL(tray_clock), timestr);
|
||||
gtk_label_set_text(
|
||||
GTK_LABEL(clock_runner->label_target),
|
||||
timestr
|
||||
);
|
||||
|
||||
g_date_time_unref(time);
|
||||
g_free(timestr);
|
||||
@@ -169,7 +222,7 @@ static gboolean on_clock_timeout_elapsed(
|
||||
gpointer data
|
||||
)
|
||||
{
|
||||
tray_clock_update_time(TRAY_CLOCK(data));
|
||||
wintc_clock_runner_update_time(WINTC_CLOCK_RUNNER(data));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -178,7 +231,7 @@ static gboolean on_sync_timeout_elapsed(
|
||||
gpointer data
|
||||
)
|
||||
{
|
||||
tray_clock_launch_time(TRAY_CLOCK(data));
|
||||
wintc_clock_runner_launch_time(WINTC_CLOCK_RUNNER(data));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -9,24 +9,25 @@ G_BEGIN_DECLS
|
||||
//
|
||||
// GTK OOP BOILERPLATE
|
||||
//
|
||||
typedef struct _TrayClockPrivate TrayClockPrivate;
|
||||
typedef struct _TrayClockClass TrayClockClass;
|
||||
typedef struct _TrayClock TrayClock;
|
||||
typedef struct _WinTCClockRunnerClass WinTCClockRunnerClass;
|
||||
typedef struct _WinTCClockRunner WinTCClockRunner;
|
||||
|
||||
#define TYPE_TRAY_CLOCK (tray_clock_get_type())
|
||||
#define TRAY_CLOCK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_TRAY_CLOCK, TrayClock))
|
||||
#define TRAY_CLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_TRAY_CLOCK, TrayClockClass))
|
||||
#define IS_TRAY_CLOCK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_TRAY_CLOCK))
|
||||
#define IS_TRAY_CLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_TRAY_CLOCK))
|
||||
#define TRAY_CLOCK_GET_CLASS (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_TRAY_CLOCK, TrayClock))
|
||||
#define TYPE_WINTC_CLOCK_RUNNER (wintc_clock_runner_get_type())
|
||||
#define WINTC_CLOCK_RUNNER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_WINTC_CLOCK_RUNNER, WinTCClockRunner))
|
||||
#define WINTC_CLOCK_RUNNER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_WINTC_CLOCK_RUNNER, WinTCClockRunnerClass))
|
||||
#define IS_WINTC_CLOCK_RUNNER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_WINTC_CLOCK_RUNNER))
|
||||
#define IS_WINTC_CLOCK_RUNNER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_WINTC_CLOCK_RUNNER))
|
||||
#define WINTC_CLOCK_RUNNER_GET_CLASS (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_WINTC_CLOCK_RUNNER, WinTCClockRunner))
|
||||
|
||||
GType tray_clock_get_type(void) G_GNUC_CONST;
|
||||
GType wintc_clock_runner_get_type(void) G_GNUC_CONST;
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
//
|
||||
// PUBLIC FUNCTIONS
|
||||
//
|
||||
GtkWidget* tray_clock_new(void);
|
||||
WinTCClockRunner* wintc_clock_runner_new(
|
||||
GtkLabel* label_target
|
||||
);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,26 +1,208 @@
|
||||
#include <glib.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <wintc-comgtk.h>
|
||||
|
||||
#include "behaviour.h"
|
||||
#include "clock.h"
|
||||
#include "notifarea.h"
|
||||
#include "volume.h"
|
||||
|
||||
//
|
||||
// GTK OOP CLASS/INSTANCE DEFINITIONS
|
||||
//
|
||||
struct _WinTCNotificationAreaClass
|
||||
{
|
||||
GtkBinClass __parent__;
|
||||
};
|
||||
|
||||
struct _WinTCNotificationArea
|
||||
{
|
||||
GtkBin __parent__;
|
||||
|
||||
GtkWidget* box_container;
|
||||
GtkWidget* label_clock;
|
||||
|
||||
WinTCClockRunner* clock_runner;
|
||||
|
||||
GHashTable* map_widget_to_behaviour;
|
||||
};
|
||||
|
||||
//
|
||||
// FORWARD DECLARATIONS
|
||||
//
|
||||
static GtkWidget* wintc_notification_area_append_icon(
|
||||
WinTCNotificationArea* notif_area
|
||||
);
|
||||
static void wintc_notification_area_map_widget(
|
||||
WinTCNotificationArea* notif_area,
|
||||
GtkWidget* widget_notif,
|
||||
WinTCNotificationBehaviour* behaviour
|
||||
);
|
||||
|
||||
static void update_notification_icon(
|
||||
GtkWidget* widget_notif,
|
||||
WinTCNotificationBehaviour* behaviour
|
||||
);
|
||||
|
||||
static void on_behaviour_icon_changed(
|
||||
WinTCNotificationBehaviour* behaviour,
|
||||
gpointer user_data
|
||||
);
|
||||
|
||||
//
|
||||
// GTK TYPE DEFINITION & CTORS
|
||||
//
|
||||
G_DEFINE_TYPE(
|
||||
WinTCNotificationArea,
|
||||
wintc_notification_area,
|
||||
GTK_TYPE_BIN
|
||||
)
|
||||
|
||||
static void wintc_notification_area_class_init(
|
||||
WINTC_UNUSED(WinTCNotificationAreaClass* klass)
|
||||
) {}
|
||||
|
||||
static void wintc_notification_area_init(
|
||||
WinTCNotificationArea* self
|
||||
)
|
||||
{
|
||||
// Create map for notification widgets --> behaviours
|
||||
//
|
||||
self->map_widget_to_behaviour =
|
||||
g_hash_table_new(g_direct_hash, g_direct_equal);
|
||||
|
||||
// Set up UI
|
||||
//
|
||||
self->box_container = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
self->label_clock = gtk_label_new(NULL);
|
||||
|
||||
wintc_widget_add_style_class(self->box_container, "wintc-systray");
|
||||
wintc_widget_add_style_class(self->label_clock, "clock");
|
||||
|
||||
gtk_box_pack_end(
|
||||
GTK_BOX(self->box_container),
|
||||
self->label_clock,
|
||||
FALSE,
|
||||
FALSE,
|
||||
0
|
||||
);
|
||||
|
||||
gtk_container_add(
|
||||
GTK_CONTAINER(self),
|
||||
self->box_container
|
||||
);
|
||||
|
||||
// Hook up clock runner
|
||||
//
|
||||
self->clock_runner =
|
||||
wintc_clock_runner_new(GTK_LABEL(self->label_clock));
|
||||
|
||||
// Create volume icon and behaviour for system tray
|
||||
//
|
||||
WinTCNotificationVolume* notif_volume;
|
||||
GtkWidget* widget_volume;
|
||||
|
||||
widget_volume = wintc_notification_area_append_icon(self);
|
||||
notif_volume = wintc_notification_volume_new(widget_volume);
|
||||
|
||||
wintc_notification_area_map_widget(
|
||||
self,
|
||||
widget_volume,
|
||||
WINTC_NOTIFICATION_BEHAVIOUR(notif_volume)
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// PUBLIC FUNCTIONS
|
||||
//
|
||||
GtkWidget* notification_area_new(void)
|
||||
{
|
||||
GtkWidget* box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
GtkWidget* clock = tray_clock_new();
|
||||
return GTK_WIDGET(
|
||||
g_object_new(
|
||||
TYPE_WINTC_NOTIFICATION_AREA,
|
||||
NULL
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
wintc_widget_add_style_class(box, "wintc-systray");
|
||||
//
|
||||
// PRIVATE FUNCTIONS
|
||||
//
|
||||
static GtkWidget* wintc_notification_area_append_icon(
|
||||
WinTCNotificationArea* notif_area
|
||||
)
|
||||
{
|
||||
GtkWidget* event_box = gtk_event_box_new();
|
||||
GtkWidget* image_icon = gtk_image_new();
|
||||
|
||||
gtk_widget_set_events(event_box, GDK_BUTTON_PRESS_MASK);
|
||||
|
||||
gtk_container_add(
|
||||
GTK_CONTAINER(event_box),
|
||||
image_icon
|
||||
);
|
||||
gtk_box_pack_start(
|
||||
GTK_BOX(box),
|
||||
clock,
|
||||
GTK_BOX(notif_area->box_container),
|
||||
event_box,
|
||||
FALSE,
|
||||
FALSE,
|
||||
0
|
||||
);
|
||||
|
||||
return box;
|
||||
return event_box;
|
||||
}
|
||||
|
||||
static void wintc_notification_area_map_widget(
|
||||
WinTCNotificationArea* notif_area,
|
||||
GtkWidget* widget_notif,
|
||||
WinTCNotificationBehaviour* behaviour
|
||||
)
|
||||
{
|
||||
g_hash_table_insert(
|
||||
notif_area->map_widget_to_behaviour,
|
||||
widget_notif,
|
||||
behaviour
|
||||
);
|
||||
|
||||
// Connect up widget to behaviour
|
||||
//
|
||||
update_notification_icon(
|
||||
widget_notif,
|
||||
behaviour
|
||||
);
|
||||
|
||||
g_signal_connect(
|
||||
behaviour,
|
||||
"icon-changed",
|
||||
G_CALLBACK(on_behaviour_icon_changed),
|
||||
widget_notif
|
||||
);
|
||||
}
|
||||
|
||||
static void update_notification_icon(
|
||||
GtkWidget* widget_notif,
|
||||
WinTCNotificationBehaviour* behaviour
|
||||
)
|
||||
{
|
||||
gtk_image_set_from_icon_name(
|
||||
GTK_IMAGE(gtk_bin_get_child(GTK_BIN(widget_notif))),
|
||||
wintc_notification_behaviour_get_icon_name(behaviour),
|
||||
GTK_ICON_SIZE_SMALL_TOOLBAR
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// CALLBACKS
|
||||
//
|
||||
static void on_behaviour_icon_changed(
|
||||
WinTCNotificationBehaviour* behaviour,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
GtkWidget* widget_notif = GTK_WIDGET(user_data);
|
||||
|
||||
update_notification_icon(
|
||||
widget_notif,
|
||||
behaviour
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,25 @@
|
||||
#ifndef __NOTIFAREA_H__
|
||||
#define __NOTIFAREA_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
//
|
||||
// GTK OOP BOILERPLATE
|
||||
//
|
||||
typedef struct _WinTCNotificationAreaClass WinTCNotificationAreaClass;
|
||||
typedef struct _WinTCNotificationArea WinTCNotificationArea;
|
||||
|
||||
#define TYPE_WINTC_NOTIFICATION_AREA (wintc_notification_area_get_type())
|
||||
#define WINTC_NOTIFICATION_AREA(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_WINTC_NOTIFICATION_AREA, WinTCNotificationArea))
|
||||
#define WINTC_NOTIFICATION_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_WINTC_NOTIFICATION_AREA, WinTCNotificationArea))
|
||||
#define IS_WINTC_NOTIFICATION_AREA(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_WINTC_NOTIFICATION_AREA))
|
||||
#define IS_WINTC_NOTIFICATION_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_WINTC_NOTIFICATION_AREA))
|
||||
#define WINTC_NOTIFICATION_AREA_GET_CLASS(obj) (G_TYPE_CHECK_INSTANCE_GET_CLASS((obj), TYPE_WINTC_NOTIFICATION_AREA))
|
||||
|
||||
//
|
||||
// PUBLIC FUNCTIONS
|
||||
//
|
||||
GtkWidget* notification_area_new(void);
|
||||
|
||||
#endif
|
||||
|
||||
430
shell/taskband/src/systray/volume.c
Normal file
430
shell/taskband/src/systray/volume.c
Normal file
@@ -0,0 +1,430 @@
|
||||
#include <glib.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <wintc-comgtk.h>
|
||||
#include <wintc-shelldpa.h>
|
||||
#include <wintc-sndapi.h>
|
||||
|
||||
#include "behaviour.h"
|
||||
#include "volume.h"
|
||||
|
||||
//
|
||||
// GTK OOP CLASS/INSTANCE DEFINITIONS
|
||||
//
|
||||
struct _WinTCNotificationVolumeClass
|
||||
{
|
||||
WinTCNotificationBehaviourClass __parent__;
|
||||
};
|
||||
|
||||
struct _WinTCNotificationVolume
|
||||
{
|
||||
WinTCNotificationBehaviour __parent__;
|
||||
|
||||
GtkWidget* popup_volmgmt;
|
||||
|
||||
GtkWidget* box_container;
|
||||
GtkWidget* check_mute;
|
||||
GtkWidget* scale_volume;
|
||||
|
||||
gboolean syncing_state;
|
||||
|
||||
// Sound API stuff
|
||||
//
|
||||
WinTCSndApiContext* snd_ctx;
|
||||
WinTCSndApiOutput* snd_output;
|
||||
};
|
||||
|
||||
//
|
||||
// FORWARD DECLARATIONS
|
||||
//
|
||||
static void wintc_notification_volume_constructed(
|
||||
GObject* object
|
||||
);
|
||||
|
||||
static void wintc_notification_volume_set_have_device(
|
||||
WinTCNotificationVolume* volume,
|
||||
gboolean have_device
|
||||
);
|
||||
|
||||
static void on_snd_ctx_connected_changed(
|
||||
WinTCSndApiContext* ctx,
|
||||
gboolean connected,
|
||||
gpointer user_data
|
||||
);
|
||||
static void on_snd_ctx_default_output_changed(
|
||||
WinTCSndApiContext* ctx,
|
||||
gpointer user_data
|
||||
);
|
||||
|
||||
static void on_snd_output_muted_changed(
|
||||
WinTCSndApiOutput* output,
|
||||
gpointer user_data
|
||||
);
|
||||
static void on_snd_output_volume_changed(
|
||||
WinTCSndApiOutput* output,
|
||||
gpointer user_data
|
||||
);
|
||||
|
||||
static gboolean on_widget_notif_button_press_event(
|
||||
GtkWidget* self,
|
||||
GdkEventButton* event,
|
||||
gpointer user_data
|
||||
);
|
||||
|
||||
static void on_check_mute_toggled(
|
||||
GtkToggleButton* self,
|
||||
gpointer user_data
|
||||
);
|
||||
static void on_scale_volume_value_changed(
|
||||
GtkRange* self,
|
||||
gpointer user_data
|
||||
);
|
||||
|
||||
//
|
||||
// GTK TYPE DEFINITIONS & CTORS
|
||||
//
|
||||
G_DEFINE_TYPE(
|
||||
WinTCNotificationVolume,
|
||||
wintc_notification_volume,
|
||||
TYPE_WINTC_NOTIFICATION_BEHAVIOUR
|
||||
)
|
||||
|
||||
static void wintc_notification_volume_class_init(
|
||||
WinTCNotificationVolumeClass* klass
|
||||
)
|
||||
{
|
||||
GObjectClass* object_class = G_OBJECT_CLASS(klass);
|
||||
|
||||
object_class->constructed = wintc_notification_volume_constructed;
|
||||
}
|
||||
|
||||
static void wintc_notification_volume_init(
|
||||
WINTC_UNUSED(WinTCNotificationVolume* self)
|
||||
)
|
||||
{
|
||||
static gboolean css_added = FALSE;
|
||||
|
||||
// Add CSS once to screen - can't do this in class because GTK may not
|
||||
// have initialized first
|
||||
//
|
||||
if (!css_added)
|
||||
{
|
||||
GtkCssProvider* css_volume_popup = gtk_css_provider_new();
|
||||
|
||||
gtk_css_provider_load_from_resource(
|
||||
css_volume_popup,
|
||||
"/uk/oddmatics/wintc/taskband/volume-popup.css"
|
||||
);
|
||||
|
||||
gtk_style_context_add_provider_for_screen(
|
||||
gdk_screen_get_default(),
|
||||
GTK_STYLE_PROVIDER(css_volume_popup),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_FALLBACK
|
||||
);
|
||||
|
||||
css_added = TRUE;
|
||||
}
|
||||
|
||||
// Other init
|
||||
//
|
||||
self->syncing_state = FALSE;
|
||||
}
|
||||
|
||||
//
|
||||
// CLASS VIRTUAL METHODS
|
||||
//
|
||||
static void wintc_notification_volume_constructed(
|
||||
GObject* object
|
||||
)
|
||||
{
|
||||
WinTCNotificationVolume* volume =
|
||||
WINTC_NOTIFICATION_VOLUME(object);
|
||||
WinTCNotificationBehaviour* behaviour =
|
||||
WINTC_NOTIFICATION_BEHAVIOUR(volume);
|
||||
|
||||
// Connect up to the notification icon widget
|
||||
//
|
||||
volume->popup_volmgmt =
|
||||
wintc_dpa_create_popup(behaviour->widget_notif);
|
||||
|
||||
volume->box_container = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
||||
volume->check_mute = gtk_check_button_new_with_label("Mute");
|
||||
volume->scale_volume = gtk_scale_new_with_range(
|
||||
GTK_ORIENTATION_VERTICAL,
|
||||
0.0f,
|
||||
1.0f,
|
||||
0.05f
|
||||
);
|
||||
|
||||
gtk_scale_set_draw_value(GTK_SCALE(volume->scale_volume), FALSE);
|
||||
gtk_range_set_inverted(GTK_RANGE(volume->scale_volume), TRUE);
|
||||
|
||||
wintc_widget_add_style_class(volume->popup_volmgmt, "wintc-volmgmt");
|
||||
|
||||
gtk_box_pack_start(
|
||||
GTK_BOX(volume->box_container),
|
||||
gtk_label_new("Volume"),
|
||||
FALSE,
|
||||
FALSE,
|
||||
0
|
||||
);
|
||||
gtk_box_pack_start(
|
||||
GTK_BOX(volume->box_container),
|
||||
volume->scale_volume,
|
||||
FALSE,
|
||||
FALSE,
|
||||
0
|
||||
);
|
||||
gtk_box_pack_start(
|
||||
GTK_BOX(volume->box_container),
|
||||
volume->check_mute,
|
||||
FALSE,
|
||||
FALSE,
|
||||
0
|
||||
);
|
||||
|
||||
gtk_container_add(
|
||||
GTK_CONTAINER(volume->popup_volmgmt),
|
||||
volume->box_container
|
||||
);
|
||||
|
||||
g_signal_connect(
|
||||
volume->check_mute,
|
||||
"toggled",
|
||||
G_CALLBACK(on_check_mute_toggled),
|
||||
object
|
||||
);
|
||||
g_signal_connect(
|
||||
volume->scale_volume,
|
||||
"value-changed",
|
||||
G_CALLBACK(on_scale_volume_value_changed),
|
||||
object
|
||||
);
|
||||
|
||||
g_signal_connect(
|
||||
behaviour->widget_notif,
|
||||
"button-press-event",
|
||||
G_CALLBACK(on_widget_notif_button_press_event),
|
||||
object
|
||||
);
|
||||
|
||||
// Set initial state
|
||||
//
|
||||
wintc_notification_volume_set_have_device(volume, FALSE);
|
||||
|
||||
// Establish sound API context
|
||||
//
|
||||
volume->snd_ctx = wintc_sndapi_context_new();
|
||||
|
||||
g_signal_connect(
|
||||
volume->snd_ctx,
|
||||
"connected-changed",
|
||||
G_CALLBACK(on_snd_ctx_connected_changed),
|
||||
volume
|
||||
);
|
||||
g_signal_connect(
|
||||
volume->snd_ctx,
|
||||
"default-output-changed",
|
||||
G_CALLBACK(on_snd_ctx_default_output_changed),
|
||||
volume
|
||||
);
|
||||
|
||||
wintc_sndapi_context_connect(volume->snd_ctx);
|
||||
|
||||
// Chain up
|
||||
//
|
||||
(G_OBJECT_CLASS(
|
||||
wintc_notification_volume_parent_class
|
||||
))->constructed(object);
|
||||
}
|
||||
|
||||
//
|
||||
// PUBLIC FUNCTIONS
|
||||
//
|
||||
WinTCNotificationVolume* wintc_notification_volume_new(
|
||||
GtkWidget* widget_notif
|
||||
)
|
||||
{
|
||||
return WINTC_NOTIFICATION_VOLUME(
|
||||
g_object_new(
|
||||
TYPE_WINTC_NOTIFICATION_VOLUME,
|
||||
"widget-notif", widget_notif,
|
||||
NULL
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// PRIVATE FUNCTIONS
|
||||
//
|
||||
static void wintc_notification_volume_set_have_device(
|
||||
WinTCNotificationVolume* volume,
|
||||
gboolean have_device
|
||||
)
|
||||
{
|
||||
gtk_widget_set_sensitive(volume->popup_volmgmt, have_device);
|
||||
|
||||
if (!have_device)
|
||||
{
|
||||
g_object_set(
|
||||
volume,
|
||||
"icon-name", "audio-volume-muted",
|
||||
NULL
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// CALLBACKS
|
||||
//
|
||||
static void on_snd_ctx_connected_changed(
|
||||
WINTC_UNUSED(WinTCSndApiContext* ctx),
|
||||
gboolean connected,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
WinTCNotificationVolume* volume =
|
||||
WINTC_NOTIFICATION_VOLUME(user_data);
|
||||
|
||||
if (!connected)
|
||||
{
|
||||
wintc_notification_volume_set_have_device(volume, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
static void on_snd_ctx_default_output_changed(
|
||||
WinTCSndApiContext* ctx,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
WinTCNotificationVolume* volume =
|
||||
WINTC_NOTIFICATION_VOLUME(user_data);
|
||||
|
||||
WinTCSndApiOutput* output =
|
||||
wintc_sndapi_context_get_default_output(ctx);
|
||||
|
||||
if (volume->snd_output == output)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
volume->snd_output = output;
|
||||
|
||||
if (volume->snd_output == NULL)
|
||||
{
|
||||
wintc_notification_volume_set_have_device(volume, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
wintc_notification_volume_set_have_device(volume, TRUE);
|
||||
|
||||
g_signal_connect(
|
||||
volume->snd_output,
|
||||
"muted-changed",
|
||||
G_CALLBACK(on_snd_output_muted_changed),
|
||||
volume
|
||||
);
|
||||
g_signal_connect(
|
||||
volume->snd_output,
|
||||
"volume-changed",
|
||||
G_CALLBACK(on_snd_output_volume_changed),
|
||||
volume
|
||||
);
|
||||
}
|
||||
|
||||
static void on_snd_output_muted_changed(
|
||||
WinTCSndApiOutput* output,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
WinTCNotificationVolume* volume =
|
||||
WINTC_NOTIFICATION_VOLUME(user_data);
|
||||
|
||||
gboolean muted = wintc_sndapi_output_is_muted(output);
|
||||
gchar* icon_name = muted ? "audio-volume-muted" : "audio-volume-medium";
|
||||
|
||||
volume->syncing_state = TRUE;
|
||||
gtk_toggle_button_set_active(
|
||||
GTK_TOGGLE_BUTTON(volume->check_mute),
|
||||
muted
|
||||
);
|
||||
volume->syncing_state = FALSE;
|
||||
|
||||
g_object_set(
|
||||
volume,
|
||||
"icon-name", icon_name,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
||||
static void on_snd_output_volume_changed(
|
||||
WinTCSndApiOutput* output,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
WinTCNotificationVolume* volume =
|
||||
WINTC_NOTIFICATION_VOLUME(user_data);
|
||||
|
||||
gdouble new_volume = wintc_sndapi_output_get_volume(output);
|
||||
|
||||
volume->syncing_state = TRUE;
|
||||
gtk_range_set_value(
|
||||
GTK_RANGE(volume->scale_volume),
|
||||
new_volume
|
||||
);
|
||||
volume->syncing_state = FALSE;
|
||||
}
|
||||
|
||||
static gboolean on_widget_notif_button_press_event(
|
||||
GtkWidget* self,
|
||||
WINTC_UNUSED(GdkEventButton* event),
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
WinTCNotificationVolume* volume =
|
||||
WINTC_NOTIFICATION_VOLUME(user_data);
|
||||
|
||||
wintc_dpa_show_popup(
|
||||
volume->popup_volmgmt,
|
||||
self
|
||||
);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void on_check_mute_toggled(
|
||||
GtkToggleButton* self,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
WinTCNotificationVolume* volume =
|
||||
WINTC_NOTIFICATION_VOLUME(user_data);
|
||||
|
||||
if (volume->syncing_state)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
wintc_sndapi_output_set_muted(
|
||||
volume->snd_output,
|
||||
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(self))
|
||||
);
|
||||
}
|
||||
|
||||
static void on_scale_volume_value_changed(
|
||||
GtkRange* self,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
WinTCNotificationVolume* volume =
|
||||
WINTC_NOTIFICATION_VOLUME(user_data);
|
||||
|
||||
if (volume->syncing_state)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
wintc_sndapi_output_set_volume(
|
||||
volume->snd_output,
|
||||
gtk_range_get_value(GTK_RANGE(self))
|
||||
);
|
||||
}
|
||||
29
shell/taskband/src/systray/volume.h
Normal file
29
shell/taskband/src/systray/volume.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef __VOLUME_H__
|
||||
#define __VOLUME_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
//
|
||||
// GTK OOP BOILERPLATE
|
||||
//
|
||||
typedef struct _WinTCNotificationVolumeClass WinTCNotificationVolumeClass;
|
||||
typedef struct _WinTCNotificationVolume WinTCNotificationVolume;
|
||||
|
||||
#define TYPE_WINTC_NOTIFICATION_VOLUME (wintc_notification_volume_get_type())
|
||||
#define WINTC_NOTIFICATION_VOLUME(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_WINTC_NOTIFICATION_VOLUME, WinTCNotificationVolume))
|
||||
#define WINTC_NOTIFICATION_VOLUME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_WINTC_NOTIFICATION_BEHAVIOUR, WinTCNotificationVolumeClass))
|
||||
#define IS_WINTC_NOTIFICATION_VOLUME(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_WINTC_NOTIFICATION_VOLUME))
|
||||
#define IS_WINTC_NOTIFICATION_VOLUME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_WINTC_NOTIFICATION_VOLUME))
|
||||
#define WINTC_NOTIFICATION_VOLUME_GET_CLASS (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_WINTC_NOTIFICATION_VOLUME, WinTCNotificationVolume))
|
||||
|
||||
GType wintc_notification_volume_get_type(void) G_GNUC_CONST;
|
||||
|
||||
//
|
||||
// PUBLIC FUNCTIONS
|
||||
//
|
||||
WinTCNotificationVolume* wintc_notification_volume_new(
|
||||
GtkWidget* widget_notif
|
||||
);
|
||||
|
||||
#endif
|
||||
@@ -12,6 +12,8 @@ lightdm-->bt-->lightdm-dev
|
||||
lightdm-->rt-->lightdm
|
||||
msgfmt-->bt,rt-->gettext
|
||||
plymouth-->bt,rt-->plymouth
|
||||
pulseaudio-->bt-->pulseaudio-dev
|
||||
pulseaudio-->rt-->libpulse
|
||||
python3-venv-->bt,rt-->py3-virtualenv
|
||||
sass-->bt,rt-->sassc
|
||||
wintc-comctl-->bt,rt-->libwintc-comctl
|
||||
@@ -20,6 +22,7 @@ wintc-exec-->bt,rt-->libwintc-exec
|
||||
wintc-msgina-->bt,rt-->libwintc-msgina
|
||||
wintc-shelldpa-->bt,rt-->libwintc-shelldpa
|
||||
wintc-shllang-->bt,rt-->libwintc-shllang
|
||||
wintc-sndapi-->bt,rt-->libwintc-sndapi
|
||||
wintc-winbrand-->bt,rt-->libwintc-winbrand
|
||||
xcursorgen-->bt,rt-->xcursorgen
|
||||
xdg-mime-->bt,rt-->xdg-utils
|
||||
xdg-mime-->bt,rt-->xdg-utils
|
||||
|
||||
@@ -6,6 +6,7 @@ gtk3-->bt,rt-->gtk3
|
||||
lightdm-->bt,rt-->lightdm
|
||||
msgfmt-->bt,rt-->gettext
|
||||
plymouth-->bt,rt-->plymouth
|
||||
pulseaudio-->bt,rt-->libpulse
|
||||
python3-venv-->bt,rt-->python3
|
||||
sass-->bt,rt-->ruby-sass
|
||||
wintc-comctl-->bt,rt-->wintc-comctl
|
||||
@@ -14,6 +15,7 @@ wintc-exec-->bt,rt-->wintc-exec
|
||||
wintc-msgina-->bt,rt-->wintc-msgina
|
||||
wintc-shelldpa-->bt,rt-->wintc-shelldpa
|
||||
wintc-shllang-->bt,rt-->wintc-shllang
|
||||
wintc-sndapi-->bt,rt-->wintc-sndapi
|
||||
wintc-winbrand-->bt,rt-->wintc-winbrand
|
||||
xcursorgen-->bt,rt-->xorg-xcursorgen
|
||||
xdg-mime-->bt,rt-->xdg-utils
|
||||
|
||||
@@ -22,6 +22,7 @@ wintc-exec-->bt,rt-->libwintc-exec
|
||||
wintc-msgina-->bt,rt-->libwintc-msgina
|
||||
wintc-shelldpa-->bt,rt-->libwintc-shelldpa
|
||||
wintc-shllang-->bt,rt-->libwintc-shllang
|
||||
wintc-sndapi-->bt,rt-->libwintc-sndapi
|
||||
wintc-winbrand-->bt,rt-->libwintc-winbrand
|
||||
xcursorgen-->bt,rt-->x11-apps
|
||||
xdg-mime-->bt,rt-->xdg-utils
|
||||
|
||||
Reference in New Issue
Block a user