Enhancement: Fixes #76, Implement sound volume indicator / levels in notification area

This commit is contained in:
Rory Fewell
2023-12-14 23:06:20 +00:00
parent 4780bba27e
commit eef73fe4a0
30 changed files with 2426 additions and 76 deletions

View File

@@ -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

View File

@@ -1,3 +1,4 @@
bt,rt:glib2
bt,rt:gtk3
bt,rt:pulseaudio
bt,rt:wintc-comgtk

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);

View 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
View 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
View File

@@ -0,0 +1,4 @@
bt,rt:glib2
bt,rt:gtk3
bt,rt:pulseaudio
bt,rt:wintc-comgtk

View 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
View 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;
}
}

View 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
View 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
);
}

View 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

View 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;
}

View 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

View File

@@ -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(

View File

@@ -7,3 +7,4 @@ bt,rt:wintc-comgtk
bt,rt:wintc-exec
bt,rt:wintc-shelldpa
bt,rt:wintc-shllang
bt,rt:wintc-sndapi

View File

@@ -2,5 +2,6 @@
<gresources>
<gresource prefix="/uk/oddmatics/wintc/taskband">
<file>start-menu.css</file>
<file>volume-popup.css</file>
</gresource>
</gresources>

View 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;
}

View 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;
}

View 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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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
);
}

View File

@@ -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

View 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))
);
}

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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