6 Commits

Author SHA1 Message Date
Rafał Dzięgiel
aa0be37cb9 New translations clapper-app.pot (Arabic) 2025-06-14 19:10:30 +02:00
Rafał Dzięgiel
9aee805b6d New translations clapper-app.pot (Arabic) 2025-06-14 17:48:59 +02:00
Rafał Dzięgiel
6ee406c11d New translations clapper-gtk.pot (Arabic) 2025-06-14 17:48:58 +02:00
Rafał Dzięgiel
fcc41b2e9c New translations clapper-app.pot (Persian) 2025-05-18 16:52:10 +02:00
Rafał Dzięgiel
fb8ee8497a New translations clapper-gtk.pot (Chinese Simplified) 2025-05-08 09:56:58 +02:00
Rafał Dzięgiel
3ba2611901 New translations clapper-app.pot (Chinese Simplified) 2025-05-01 15:53:00 +02:00
211 changed files with 2582 additions and 10340 deletions

View File

@@ -2,7 +2,7 @@
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
<https://fsf.org/>
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -55,7 +55,7 @@ modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
@@ -111,7 +111,7 @@ modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
@@ -158,7 +158,7 @@ Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
@@ -216,7 +216,7 @@ instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
@@ -267,7 +267,7 @@ Library will still fall under Section 6.)
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
@@ -329,7 +329,7 @@ restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
@@ -370,7 +370,7 @@ subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
@@ -422,7 +422,7 @@ conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
@@ -456,7 +456,7 @@ SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
@@ -484,7 +484,9 @@ convey the exclusion of warranty; and each file should have at least the
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <https://www.gnu.org/licenses/>.
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
Also add information on how to contact you by electronic and paper mail.
@@ -493,9 +495,10 @@ school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
library `Frob' (a library for tweaking knobs) written by James Random
Hacker.
<signature of Moe Ghoul>, 1 April 1990
Moe Ghoul, President of Vice
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@@ -1,49 +0,0 @@
#!/usr/bin/env python3
import gi
gi.require_version('Adw', '1')
gi.require_version('Clapper', '0.0')
gi.require_version('ClapperGtk', '0.0')
gi.require_version('Gtk', '4.0')
from gi.repository import Adw, Clapper, ClapperGtk, Gtk
Clapper.init(None)
def on_activate(app):
# Create our widgets.
win = Gtk.ApplicationWindow(application=app, title='Clapper Audio', default_width=640, default_height=96)
audio = ClapperGtk.Audio()
box = Gtk.Box(valign=Gtk.Align.CENTER, margin_start=8, margin_end=8, spacing=4)
prev_btn = ClapperGtk.PreviousItemButton()
play_btn = ClapperGtk.TogglePlayButton()
next_btn = ClapperGtk.NextItemButton()
seek_bar = ClapperGtk.SeekBar()
# Add media for playback. First media item in queue will be automatically selected.
item = Clapper.MediaItem(uri='https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3')
audio.props.player.props.queue.add_item(item)
item = Clapper.MediaItem(uri='https://www.learningcontainer.com/wp-content/uploads/2020/02/Kalimba.mp3')
audio.props.player.props.queue.add_item(item)
# Assemble window.
box.append(prev_btn)
box.append(play_btn)
box.append(next_btn)
box.append(seek_bar)
audio.set_child(box)
win.set_child(audio)
win.present()
# Not too loud. Mind the ears.
audio.props.player.props.volume = 0.7
# Start playback.
audio.props.player.play()
# Create a new application.
app = Adw.Application(application_id='com.example.ClapperAudio')
app.connect('activate', on_activate)
# Run the application.
app.run(None)

View File

@@ -1,5 +1,5 @@
project('clapper', 'c',
version: '0.9.1',
version: '0.9.0',
meson_version: '>= 0.64.0',
license: 'LGPL-2.1-or-later AND GPL-3.0-or-later', # LGPL-2.1+ for libs and gst-plugin, GPL-3.0+ for app
default_options: [
@@ -11,7 +11,7 @@ project('clapper', 'c',
glib_req = '>= 2.76.0'
gst_req = '>= 1.24.0'
gtk4_req = '>= 4.10.0'
adw_req = '>= 1.5.0'
adw_req = '>= 1.4.0'
clapper_version = meson.project_version().split('-')[0]
version_array = clapper_version.split('.')

View File

@@ -16,7 +16,7 @@
"autodelete": false
},
"com.github.rafostar.Clapper.Enhancers": {
"versions": "stable;test;master",
"versions": "master;test;stable",
"directory": "extensions/clapper/enhancers",
"add-ld-path": "lib",
"no-autodownload": false,

View File

@@ -12,7 +12,7 @@
"autodelete": false
},
"com.github.rafostar.Clapper.Enhancers": {
"versions": "stable;test;master",
"versions": "master;test;stable",
"directory": "extensions/clapper/enhancers",
"add-ld-path": "lib",
"no-autodownload": false,

View File

@@ -642,6 +642,18 @@ clapper_app_application_command_line (GApplication *app, GApplicationCommandLine
return EXIT_SUCCESS;
}
static gboolean
_is_claps_file (GFile *file)
{
gchar *basename = g_file_get_basename (file);
gboolean is_claps;
is_claps = (basename && g_str_has_suffix (basename, ".claps"));
g_free (basename);
return is_claps;
}
static void
add_item_from_file (GFile *file, ClapperQueue *queue)
{
@@ -654,6 +666,51 @@ add_item_from_file (GFile *file, ClapperQueue *queue)
gst_object_unref (item);
}
static void
add_items_from_claps_file (GFile *file, ClapperQueue *queue)
{
GDataInputStream *dstream = NULL;
GFileInputStream *stream;
GError *error = NULL;
gchar *line;
if (!(stream = g_file_read (file, NULL, &error)))
goto finish;
dstream = g_data_input_stream_new (G_INPUT_STREAM (stream));
while ((line = g_data_input_stream_read_line (
dstream, NULL, NULL, &error))) {
g_strstrip (line);
if (strlen (line) > 0) {
GFile *tmp_file = gst_uri_is_valid (line)
? g_file_new_for_uri (line)
: g_file_new_for_path (line);
if (_is_claps_file (tmp_file))
add_items_from_claps_file (tmp_file, queue);
else
add_item_from_file (tmp_file, queue);
g_object_unref (tmp_file);
}
g_free (line);
}
finish:
if (error) {
GST_ERROR ("Could not read \".claps\" file, reason: %s", error->message);
g_error_free (error);
}
if (stream) {
g_input_stream_close (G_INPUT_STREAM (stream), NULL, NULL);
g_object_unref (stream);
}
g_clear_object (&dstream);
}
static void
add_item_with_subtitles (GFile *media_file,
GFile *subs_file, ClapperQueue *queue)
@@ -722,8 +779,12 @@ clapper_app_application_open (GApplication *app,
if (!handled) {
gint i;
for (i = 0; i < n_files; ++i)
add_item_from_file (files[i], queue);
for (i = 0; i < n_files; ++i) {
if (_is_claps_file (files[i]))
add_items_from_claps_file (files[i], queue);
else
add_item_from_file (files[i], queue);
}
}
add_only = (g_strcmp0 (hint, "add-only") == 0);

View File

@@ -72,43 +72,6 @@ _open_subtitles_cb (GtkFileDialog *dialog, GAsyncResult *result, ClapperMediaIte
gst_object_unref (item); // Borrowed reference
}
static void
_on_select_file_dir_finish (GFile *file, AdwActionRow *action_row, GError *error)
{
if (G_LIKELY (error == NULL)) {
gchar *path = g_file_get_path (file);
adw_action_row_set_subtitle (action_row, path);
g_free (path);
} else {
if (error->domain != GTK_DIALOG_ERROR || error->code != GTK_DIALOG_ERROR_DISMISSED) {
g_printerr ("Error: %s\n",
(error->message) ? error->message : "Could not open file dialog");
}
g_error_free (error);
}
g_clear_object (&file);
g_object_unref (action_row); // Borrowed reference
}
static void
_select_file_cb (GtkFileDialog *dialog, GAsyncResult *result, AdwActionRow *action_row)
{
GError *error = NULL;
GFile *file = gtk_file_dialog_open_finish (dialog, result, &error);
_on_select_file_dir_finish (file, action_row, error);
}
static void
_select_dir_cb (GtkFileDialog *dialog, GAsyncResult *result, AdwActionRow *action_row)
{
GError *error = NULL;
GFile *file = gtk_file_dialog_select_folder_finish (dialog, result, &error);
_on_select_file_dir_finish (file, action_row, error);
}
static void
_dialog_add_mime_types (GtkFileDialog *dialog, const gchar *filter_name,
const gchar *const *mime_types)
@@ -181,35 +144,3 @@ clapper_app_file_dialog_open_subtitles (GtkApplication *gtk_app, ClapperMediaIte
g_object_unref (dialog);
}
void
clapper_app_file_dialog_select_prefs_file (GtkApplication *gtk_app, AdwActionRow *action_row)
{
GtkWindow *window = gtk_application_get_active_window (gtk_app);
GtkFileDialog *dialog = gtk_file_dialog_new ();
gtk_file_dialog_set_modal (dialog, TRUE);
gtk_file_dialog_set_title (dialog, "Select File");
gtk_file_dialog_open (dialog, window, NULL,
(GAsyncReadyCallback) _select_file_cb,
g_object_ref (action_row));
g_object_unref (dialog);
}
void
clapper_app_file_dialog_select_prefs_dir (GtkApplication *gtk_app, AdwActionRow *action_row)
{
GtkWindow *window = gtk_application_get_active_window (gtk_app);
GtkFileDialog *dialog = gtk_file_dialog_new ();
gtk_file_dialog_set_modal (dialog, TRUE);
gtk_file_dialog_set_title (dialog, "Select Folder");
gtk_file_dialog_select_folder (dialog, window, NULL,
(GAsyncReadyCallback) _select_dir_cb,
g_object_ref (action_row));
g_object_unref (dialog);
}

View File

@@ -19,7 +19,6 @@
#include <glib.h>
#include <gtk/gtk.h>
#include <adwaita.h>
#include <clapper/clapper.h>
G_BEGIN_DECLS
@@ -30,10 +29,4 @@ void clapper_app_file_dialog_open_files (GtkApplication *gtk_app);
G_GNUC_INTERNAL
void clapper_app_file_dialog_open_subtitles (GtkApplication *gtk_app, ClapperMediaItem *item);
G_GNUC_INTERNAL
void clapper_app_file_dialog_select_prefs_file (GtkApplication *gtk_app, AdwActionRow *action_row);
G_GNUC_INTERNAL
void clapper_app_file_dialog_select_prefs_dir (GtkApplication *gtk_app, AdwActionRow *action_row);
G_END_DECLS

View File

@@ -23,7 +23,6 @@
#include "clapper-app-preferences-window.h"
#include "clapper-app-application.h"
#include "clapper-app-file-dialog.h"
#include "clapper-app-utils.h"
#define GST_CAT_DEFAULT clapper_app_preferences_window_debug
@@ -42,14 +41,6 @@ struct _ClapperAppPreferencesWindow
AdwSpinRow *subtitle_offset_spin_row;
GtkFontDialogButton *font_dialog_button;
GtkStack *enhancers_stack;
GtkWidget *browse_enhancers_page;
GtkWidget *no_enhancers_page;
AdwNavigationPage *enhancers_subpage;
AdwComboRow *enhancers_combo_row;
AdwPreferencesGroup *enhancer_config_group;
AdwNavigationPage *plugins_subpage;
AdwComboRow *plugins_combo_row;
AdwComboRow *features_combo_row;
@@ -57,8 +48,6 @@ struct _ClapperAppPreferencesWindow
GSettings *settings;
GList *enhancer_pspec_rows;
GList *features;
GtkStringList *plugins_list;
@@ -78,19 +67,6 @@ typedef struct
gboolean updated;
} ClapperAppPreferencesIterRanksData;
typedef struct
{
GSettings *settings;
const gchar *key;
} ClapperAppPreferencesResetData;
typedef struct
{
GSettings *settings;
GParamSpec *pspec;
guint flag;
} ClapperAppPreferencesFlagMapData;
enum
{
PROP_0,
@@ -100,347 +76,6 @@ enum
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
static void
_flag_map_data_free (ClapperAppPreferencesFlagMapData *data)
{
GST_TRACE ("Destroying flag map data: %p", data);
g_object_unref (data->settings);
g_free (data);
}
static void
_reset_button_closure (ClapperAppPreferencesResetData *data, GClosure *closure)
{
GST_TRACE ("Destroying reset button data: %p", data);
g_object_unref (data->settings);
g_free (data);
}
static void
_reset_button_clicked_cb (GtkButton *button, ClapperAppPreferencesResetData *data)
{
g_settings_reset (data->settings, data->key);
}
static void
file_selection_row_activated_cb (AdwActionRow *action_row, GParamSpec *pspec)
{
GtkApplication *gtk_app;
GtkWidget *window;
if (!(window = gtk_widget_get_ancestor (GTK_WIDGET (action_row), GTK_TYPE_WINDOW))) {
GST_ERROR ("Could not get a hold of parent window");
return;
}
gtk_app = gtk_window_get_application (GTK_WINDOW (window));
if (pspec->flags & CLAPPER_ENHANCER_PARAM_FILEPATH)
clapper_app_file_dialog_select_prefs_file (gtk_app, action_row);
else
clapper_app_file_dialog_select_prefs_dir (gtk_app, action_row);
}
static gboolean
_get_enum_mapping (GValue *value, GVariant *variant, GParamSpec *pspec)
{
GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_peek (pspec->value_type));
const gchar *selected_str = g_variant_get_string (variant, NULL);
guint i, selected = 0;
for (i = 0; i < enum_class->n_values; ++i) {
if (g_strcmp0 (selected_str, enum_class->values[i].value_nick) == 0) {
selected = i;
break;
}
}
g_value_set_uint (value, selected);
return TRUE;
}
static GVariant *
_set_enum_mapping (GValue *value, GVariantType *exp_type, GParamSpec *pspec)
{
GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_peek (pspec->value_type));
guint selected = g_value_get_uint (value);
if (G_UNLIKELY (selected == GTK_INVALID_LIST_POSITION))
selected = 0;
return g_variant_new_string (enum_class->values[selected].value_nick);
}
static gboolean
_get_flag_mapping (GValue *value, GVariant *variant, ClapperAppPreferencesFlagMapData *data)
{
guint flags = g_settings_get_flags (data->settings, data->pspec->name);
g_value_set_boolean (value, (flags & data->flag));
return TRUE;
}
static GVariant *
_set_flag_mapping (GValue *value, GVariantType *exp_type, ClapperAppPreferencesFlagMapData *data)
{
GFlagsClass *flags_class = G_FLAGS_CLASS (g_type_class_peek (data->pspec->value_type));
GStrvBuilder *builder;
GVariant *variant;
gchar **strv;
gboolean active = g_value_get_boolean (value);
guint i, flags = g_settings_get_flags (data->settings, data->pspec->name);
if (active)
flags |= data->flag;
else
flags &= ~(data->flag);
builder = g_strv_builder_new ();
for (i = 0; i < flags_class->n_values; ++i) {
if (flags & flags_class->values[i].value)
g_strv_builder_add (builder, flags_class->values[i].value_nick);
}
strv = g_strv_builder_end (builder);
g_strv_builder_unref (builder);
variant = g_variant_new_strv ((const gchar *const *) strv, -1);
g_strfreev (strv);
return variant;
}
static gboolean
_add_enhancer_config_row (ClapperAppPreferencesWindow *self, GParamSpec *pspec,
GSettings *enhancer_settings)
{
GtkWidget *row = NULL, *reset_button;
ClapperAppPreferencesResetData *reset_data;
const gchar *bind_prop = NULL;
gboolean is_enum = FALSE, is_flags = FALSE;
switch (pspec->value_type) {
case G_TYPE_BOOLEAN:{
row = adw_switch_row_new ();
break;
}
case G_TYPE_INT:{
GParamSpecInt *p = (GParamSpecInt *) pspec;
row = adw_spin_row_new_with_range (p->minimum, p->maximum, 1);
break;
}
case G_TYPE_UINT:{
GParamSpecUInt *p = (GParamSpecUInt *) pspec;
row = adw_spin_row_new_with_range (p->minimum, p->maximum, 1);
break;
}
case G_TYPE_DOUBLE:{
GParamSpecDouble *p = (GParamSpecDouble *) pspec;
row = adw_spin_row_new_with_range (p->minimum, p->maximum, 0.25);
break;
}
case G_TYPE_STRING:{
if (pspec->flags & (CLAPPER_ENHANCER_PARAM_FILEPATH | CLAPPER_ENHANCER_PARAM_DIRPATH)) {
GtkWidget *image;
image = gtk_image_new_from_icon_name ("document-open-symbolic");
gtk_widget_set_margin_end (image, 10); // matches other rows
row = adw_action_row_new ();
adw_action_row_add_suffix (ADW_ACTION_ROW (row), image);
adw_action_row_set_activatable_widget (ADW_ACTION_ROW (row), image);
g_signal_connect (row, "activated",
G_CALLBACK (file_selection_row_activated_cb), pspec);
} else {
row = adw_entry_row_new ();
}
break;
}
default:{
if ((is_enum = G_IS_PARAM_SPEC_ENUM (pspec))) {
GtkExpression *expression;
AdwEnumListModel *enum_model;
row = adw_combo_row_new ();
expression = gtk_property_expression_new (ADW_TYPE_ENUM_LIST_ITEM, NULL, "nick");
adw_combo_row_set_expression (ADW_COMBO_ROW (row), expression);
enum_model = adw_enum_list_model_new (pspec->value_type);
adw_combo_row_set_model (ADW_COMBO_ROW (row), G_LIST_MODEL (enum_model));
gtk_expression_unref (expression);
g_object_unref (enum_model);
break;
} else if ((is_flags = G_IS_PARAM_SPEC_FLAGS (pspec))) {
GFlagsClass *flags_class = G_FLAGS_CLASS (g_type_class_peek (pspec->value_type));
guint i;
row = adw_expander_row_new ();
for (i = 0; i < flags_class->n_values; ++i) {
GtkWidget *flag_row = adw_switch_row_new ();
ClapperAppPreferencesFlagMapData *fm_data;
adw_preferences_row_set_title (ADW_PREFERENCES_ROW (flag_row),
flags_class->values[i].value_nick);
fm_data = g_new (ClapperAppPreferencesFlagMapData, 1);
fm_data->settings = g_object_ref (enhancer_settings);
fm_data->pspec = pspec;
fm_data->flag = flags_class->values[i].value;
GST_TRACE ("Created flag map data: %p", fm_data);
g_settings_bind_with_mapping (enhancer_settings, pspec->name, flag_row,
"active", G_SETTINGS_BIND_DEFAULT,
(GSettingsBindGetMapping) _get_flag_mapping,
(GSettingsBindSetMapping) _set_flag_mapping,
fm_data, (GDestroyNotify) _flag_map_data_free);
adw_expander_row_add_row (ADW_EXPANDER_ROW (row), flag_row);
}
break;
}
g_warning ("Unsupported enhancer \"%s\" property type: %s",
pspec->name, g_type_name (pspec->value_type));
return FALSE;
}
}
reset_button = gtk_button_new_from_icon_name ("view-refresh-symbolic");
gtk_widget_set_tooltip_text (reset_button, _("Restore default"));
gtk_widget_set_halign (reset_button, GTK_ALIGN_CENTER);
gtk_widget_set_valign (reset_button, GTK_ALIGN_CENTER);
gtk_widget_add_css_class (reset_button, "circular");
gtk_widget_set_tooltip_text (row, g_param_spec_get_blurb (pspec));
adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), g_param_spec_get_nick (pspec));
if (ADW_IS_SWITCH_ROW (row)) {
bind_prop = "active";
} else if (ADW_IS_SPIN_ROW (row)) {
bind_prop = "value";
adw_spin_row_set_numeric (ADW_SPIN_ROW (row), TRUE);
} else if (ADW_IS_ENTRY_ROW (row)) {
bind_prop = "text";
} else if (ADW_IS_COMBO_ROW (row)) {
bind_prop = "selected";
} else if (ADW_IS_ACTION_ROW (row)) {
bind_prop = "subtitle";
} else if (!is_flags) { // In case of flags we bind individual widgets
g_assert_not_reached ();
return FALSE;
}
if (ADW_IS_ENTRY_ROW (row))
adw_entry_row_add_prefix (ADW_ENTRY_ROW (row), reset_button);
else if (ADW_IS_ACTION_ROW (row))
adw_action_row_add_prefix (ADW_ACTION_ROW (row), reset_button);
else if (ADW_IS_EXPANDER_ROW (row))
adw_expander_row_add_prefix (ADW_EXPANDER_ROW (row), reset_button);
if (is_enum) {
g_settings_bind_with_mapping (enhancer_settings, pspec->name, row,
bind_prop, G_SETTINGS_BIND_DEFAULT,
(GSettingsBindGetMapping) _get_enum_mapping,
(GSettingsBindSetMapping) _set_enum_mapping,
pspec, NULL);
} else if (!is_flags) {
g_settings_bind (enhancer_settings, pspec->name, row,
bind_prop, G_SETTINGS_BIND_DEFAULT);
}
reset_data = g_new (ClapperAppPreferencesResetData, 1);
reset_data->settings = g_object_ref (enhancer_settings);
reset_data->key = pspec->name;
GST_TRACE ("Created reset button data: %p", reset_data);
g_signal_connect_data (reset_button, "clicked",
G_CALLBACK (_reset_button_clicked_cb), reset_data,
(GClosureNotify) _reset_button_closure, G_CONNECT_DEFAULT);
adw_preferences_group_add (self->enhancer_config_group, row);
self->enhancer_pspec_rows = g_list_append (self->enhancer_pspec_rows, row);
return TRUE;
}
static void
selected_enhancer_changed_cb (AdwComboRow *combo_row,
GParamSpec *pspec G_GNUC_UNUSED, ClapperAppPreferencesWindow *self)
{
guint selected = adw_combo_row_get_selected (combo_row);
/* Remove old rows */
if (self->enhancer_pspec_rows) {
GList *el;
for (el = self->enhancer_pspec_rows; el; el = g_list_next (el))
adw_preferences_group_remove (self->enhancer_config_group, GTK_WIDGET (el->data));
g_clear_list (&self->enhancer_pspec_rows, NULL);
}
/* Add new rows */
if (selected != GTK_INVALID_LIST_POSITION) {
ClapperEnhancerProxyList *proxies = clapper_get_global_enhancer_proxies ();
ClapperEnhancerProxy *proxy = clapper_enhancer_proxy_list_peek_proxy (proxies, selected);
GParamSpec **pspecs;
guint n_pspecs;
gboolean has_props = FALSE;
if ((pspecs = clapper_enhancer_proxy_get_target_properties (proxy, &n_pspecs))) {
GSettings *enhancer_settings = NULL;
guint i;
for (i = 0; i < n_pspecs; ++i) {
if (pspecs[i]->flags & CLAPPER_ENHANCER_PARAM_GLOBAL) {
if (!enhancer_settings)
enhancer_settings = clapper_enhancer_proxy_get_settings (proxy);
if (enhancer_settings)
has_props |= _add_enhancer_config_row (self, pspecs[i], enhancer_settings);
}
}
g_clear_object (&enhancer_settings);
}
if (!has_props) {
GtkWidget *row = adw_action_row_new ();
adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), _("No configurable properties"));
adw_preferences_group_add (self->enhancer_config_group, row);
self->enhancer_pspec_rows = g_list_append (self->enhancer_pspec_rows, row);
}
}
}
static void
enhancers_config_activated_cb (AdwActionRow *action_row, ClapperAppPreferencesWindow *self)
{
/* If no model set yet */
if (!adw_combo_row_get_model (self->enhancers_combo_row)) {
ClapperEnhancerProxyList *proxies = clapper_get_global_enhancer_proxies ();
adw_combo_row_set_model (self->enhancers_combo_row, G_LIST_MODEL (proxies));
adw_combo_row_set_selected (self->enhancers_combo_row, GTK_INVALID_LIST_POSITION);
GST_DEBUG ("Populated names combo row in enhancers subpage");
if (clapper_enhancer_proxy_list_get_n_proxies (proxies) > 0)
gtk_stack_set_visible_child (self->enhancers_stack, self->browse_enhancers_page);
else
gtk_stack_set_visible_child (self->enhancers_stack, self->no_enhancers_page);
}
adw_preferences_window_push_subpage (ADW_PREFERENCES_WINDOW (self), self->enhancers_subpage);
}
/* Sort by plugin name and if the same, sort by element name */
static gint
_compare_plugins_cb (gconstpointer ptr_a, gconstpointer ptr_b)
@@ -666,12 +301,6 @@ _make_plugin_features_string_list (ClapperAppPreferencesWindow *self, const gcha
return features_list;
}
static gboolean
list_has_selection_closure (ClapperAppPreferencesWindow *self, guint selected)
{
return (selected != GTK_INVALID_LIST_POSITION);
}
static GtkStringList *
ranking_features_model_closure (ClapperAppPreferencesWindow *self, GtkStringObject *string_obj)
{
@@ -914,7 +543,6 @@ clapper_app_preferences_window_finalize (GObject *object)
g_object_unref (self->settings);
g_clear_list (&self->enhancer_pspec_rows, NULL);
g_clear_object (&self->plugins_list);
if (self->features)
@@ -970,14 +598,6 @@ clapper_app_preferences_window_class_init (ClapperAppPreferencesWindowClass *kla
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, subtitle_offset_spin_row);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, font_dialog_button);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, enhancers_stack);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, browse_enhancers_page);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, no_enhancers_page);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, enhancers_subpage);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, enhancers_combo_row);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, enhancer_config_group);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, plugins_subpage);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, plugins_combo_row);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, features_combo_row);
@@ -985,13 +605,9 @@ clapper_app_preferences_window_class_init (ClapperAppPreferencesWindowClass *kla
gtk_widget_class_bind_template_callback (widget_class, seek_method_name_closure);
gtk_widget_class_bind_template_callback (widget_class, enhancers_config_activated_cb);
gtk_widget_class_bind_template_callback (widget_class, selected_enhancer_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, plugin_ranking_activated_cb);
gtk_widget_class_bind_template_callback (widget_class, plugin_ranking_unrealize_cb);
gtk_widget_class_bind_template_callback (widget_class, list_has_selection_closure);
gtk_widget_class_bind_template_callback (widget_class, ranking_features_model_closure);
gtk_widget_class_bind_template_callback (widget_class, add_override_button_sensitive_closure);
gtk_widget_class_bind_template_callback (widget_class, add_override_button_clicked_cb);

View File

@@ -582,7 +582,7 @@ _create_pipeline_svg_file_in_thread (GTask *task, GObject *source G_GNUC_UNUSED,
GVC_t *gvc;
gchar *path, *template = NULL, *dot_data = NULL, *img_data = NULL;
gint fd;
gsize size = 0;
guint size = 0;
if (!(tmp_subdir = _create_tmp_subdir ("pipelines", cancellable, &error)))
goto finish;
@@ -610,16 +610,7 @@ _create_pipeline_svg_file_in_thread (GTask *task, GObject *source G_GNUC_UNUSED,
gvc = gvContext ();
gvLayout (gvc, graph, "dot");
#ifdef HAVE_GVC_13
gvRenderData (gvc, graph, "svg", &img_data, &size);
#else
{
guint tmp_size = 0; // Temporary uint to satisfy older API
gvRenderData (gvc, graph, "svg", &img_data, &tmp_size);
size = tmp_size;
}
#endif
agclose (graph);
gvFreeContext (gvc);

View File

@@ -86,7 +86,9 @@ typedef struct
gint64 last_tick;
} ClapperAppWindowResizeData;
#if CLAPPER_HAVE_MPRIS
static guint16 instance_count = 0;
#endif
static inline GQuark
clapper_app_window_extra_options_get_quark (void)
@@ -231,7 +233,7 @@ video_map_cb (GtkWidget *widget, ClapperAppWindow *self)
GST_TRACE_OBJECT (self, "Video map");
player = clapper_gtk_av_get_player (CLAPPER_GTK_AV_CAST (self->video));
player = clapper_gtk_video_get_player (CLAPPER_GTK_VIDEO_CAST (self->video));
g_signal_connect (player, "notify::volume",
G_CALLBACK (_player_volume_changed_cb), self);
@@ -252,7 +254,7 @@ video_unmap_cb (GtkWidget *widget, ClapperAppWindow *self)
GST_TRACE_OBJECT (self, "Video unmap");
player = clapper_gtk_av_get_player (CLAPPER_GTK_AV_CAST (self->video));
player = clapper_gtk_video_get_player (CLAPPER_GTK_VIDEO_CAST (self->video));
g_signal_handlers_disconnect_by_func (player, _player_volume_changed_cb, self);
g_signal_handlers_disconnect_by_func (player, _player_speed_changed_cb, self);
@@ -524,7 +526,7 @@ drag_update_cb (GtkGestureDrag *drag,
static inline void
_alter_volume (ClapperAppWindow *self, gdouble dy)
{
ClapperPlayer *player = clapper_gtk_av_get_player (CLAPPER_GTK_AV_CAST (self->video));
ClapperPlayer *player = clapper_gtk_video_get_player (CLAPPER_GTK_VIDEO_CAST (self->video));
gdouble volume = clapper_player_get_volume (player);
/* We do not want for volume to change too suddenly */
@@ -547,7 +549,7 @@ _alter_volume (ClapperAppWindow *self, gdouble dy)
static inline void
_alter_speed (ClapperAppWindow *self, gdouble dx)
{
ClapperPlayer *player = clapper_gtk_av_get_player (CLAPPER_GTK_AV_CAST (self->video));
ClapperPlayer *player = clapper_gtk_video_get_player (CLAPPER_GTK_VIDEO_CAST (self->video));
gdouble speed = clapper_player_get_speed (player);
speed -= dx * 0.02;
@@ -571,7 +573,8 @@ _begin_seek_operation (ClapperAppWindow *self)
if (self->seeking)
return FALSE;
player = clapper_gtk_av_get_player (CLAPPER_GTK_AV_CAST (self->video));
player = clapper_gtk_video_get_player (
CLAPPER_GTK_VIDEO_CAST (self->video));
queue = clapper_player_get_queue (player);
current_item = clapper_queue_get_current_item (queue);
@@ -599,8 +602,8 @@ static void
_end_seek_operation (ClapperAppWindow *self)
{
if (self->seeking && self->current_duration > 0) {
ClapperPlayer *player = clapper_gtk_av_get_player (
CLAPPER_GTK_AV_CAST (self->video));
ClapperPlayer *player = clapper_gtk_video_get_player (
CLAPPER_GTK_VIDEO_CAST (self->video));
clapper_player_seek_custom (player, self->pending_position,
g_settings_get_int (self->settings, "seek-method"));
@@ -763,8 +766,8 @@ _handle_seek_key_press (ClapperAppWindow *self, gboolean forward)
static void
_handle_chapter_key_press (ClapperAppWindow *self, gboolean forward)
{
ClapperPlayer *player = clapper_gtk_av_get_player (
CLAPPER_GTK_AV_CAST (self->video));
ClapperPlayer *player = clapper_gtk_video_get_player (
CLAPPER_GTK_VIDEO_CAST (self->video));
ClapperQueue *queue = clapper_player_get_queue (player);
ClapperMediaItem *current_item = clapper_queue_get_current_item (queue);
ClapperTimeline *timeline;
@@ -854,8 +857,8 @@ _handle_chapter_key_press (ClapperAppWindow *self, gboolean forward)
static void
_handle_item_key_press (ClapperAppWindow *self, gboolean forward)
{
ClapperPlayer *player = clapper_gtk_av_get_player (
CLAPPER_GTK_AV_CAST (self->video));
ClapperPlayer *player = clapper_gtk_video_get_player (
CLAPPER_GTK_VIDEO_CAST (self->video));
ClapperQueue *queue = clapper_player_get_queue (player);
guint prev_index, index;
@@ -863,7 +866,7 @@ _handle_item_key_press (ClapperAppWindow *self, gboolean forward)
prev_index = clapper_queue_get_current_index (queue);
gtk_widget_activate_action (self->video,
(forward) ? "av.next-item" : "av.previous-item", NULL);
(forward) ? "video.next-item" : "video.previous-item", NULL);
index = clapper_queue_get_current_index (queue);
/* Notify only when changed */
@@ -880,14 +883,14 @@ _handle_speed_key_press (ClapperAppWindow *self, gboolean forward)
forward ^= (gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL);
gtk_widget_activate_action (self->video,
(forward) ? "av.speed-up" : "av.speed-down", NULL);
(forward) ? "video.speed-up" : "video.speed-down", NULL);
}
static inline void
_handle_progression_key_press (ClapperAppWindow *self)
{
ClapperPlayer *player = clapper_gtk_av_get_player (
CLAPPER_GTK_AV_CAST (self->video));
ClapperPlayer *player = clapper_gtk_video_get_player (
CLAPPER_GTK_VIDEO_CAST (self->video));
ClapperQueue *queue = clapper_player_get_queue (player);
ClapperQueueProgressionMode mode;
const gchar *icon = NULL, *label = NULL;
@@ -907,11 +910,11 @@ key_pressed_cb (GtkEventControllerKey *controller, guint keyval,
switch (keyval) {
case GDK_KEY_Up:
if ((state & GDK_MODIFIER_MASK) == 0)
gtk_widget_activate_action (self->video, "av.volume-up", NULL);
gtk_widget_activate_action (self->video, "video.volume-up", NULL);
break;
case GDK_KEY_Down:
if ((state & GDK_MODIFIER_MASK) == 0)
gtk_widget_activate_action (self->video, "av.volume-down", NULL);
gtk_widget_activate_action (self->video, "video.volume-down", NULL);
break;
case GDK_KEY_Left:
if ((state & GDK_MODIFIER_MASK) == 0) {
@@ -942,7 +945,7 @@ key_pressed_cb (GtkEventControllerKey *controller, guint keyval,
case GDK_KEY_space:
case GDK_KEY_k:
if (!self->key_held && (state & GDK_MODIFIER_MASK) == 0)
gtk_widget_activate_action (self->video, "av.toggle-play", NULL);
gtk_widget_activate_action (self->video, "video.toggle-play", NULL);
break;
case GDK_KEY_less:
if (!self->key_held) // Needs seek (action is slow)
@@ -954,7 +957,7 @@ key_pressed_cb (GtkEventControllerKey *controller, guint keyval,
break;
case GDK_KEY_m:
if (!self->key_held && (state & GDK_MODIFIER_MASK) == 0)
gtk_widget_activate_action (self->video, "av.toggle-mute", NULL);
gtk_widget_activate_action (self->video, "video.toggle-mute", NULL);
break;
case GDK_KEY_p:
if (!self->key_held && (state & GDK_MODIFIER_MASK) == 0)
@@ -1122,7 +1125,7 @@ clapper_app_window_get_video (ClapperAppWindow *self)
ClapperPlayer *
clapper_app_window_get_player (ClapperAppWindow *self)
{
return clapper_gtk_av_get_player (CLAPPER_GTK_AV_CAST (self->video));
return clapper_gtk_video_get_player (CLAPPER_GTK_VIDEO_CAST (self->video));
}
ClapperAppWindowExtraOptions *
@@ -1197,7 +1200,6 @@ clapper_app_window_init (ClapperAppWindow *self)
GtkSettings *settings;
GtkWidget *dummy_titlebar;
gint distance = 0;
GtkWindowGroup *group;
gtk_widget_set_size_request (GTK_WIDGET (self),
MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT);
@@ -1229,11 +1231,6 @@ clapper_app_window_init (ClapperAppWindow *self)
gtk_drop_target_set_gtypes (self->drop_target,
(GType[3]) { GDK_TYPE_FILE_LIST, G_TYPE_FILE, G_TYPE_STRING }, 3);
/* Add to window group */
group = gtk_window_group_new ();
gtk_window_group_add_window (group, GTK_WINDOW (self));
g_object_unref (group);
}
static void
@@ -1242,8 +1239,6 @@ clapper_app_window_constructed (GObject *object)
ClapperAppWindow *self = CLAPPER_APP_WINDOW_CAST (object);
ClapperPlayer *player = clapper_app_window_get_player (self);
ClapperQueue *queue = clapper_player_get_queue (player);
ClapperEnhancerProxyList *proxies = clapper_player_get_enhancer_proxies (player);
ClapperEnhancerProxy *proxy;
ClapperGtkExtraMenuButton *button;
AdwStyleManager *manager;
@@ -1257,30 +1252,22 @@ clapper_app_window_constructed (GObject *object)
#if (CLAPPER_HAVE_MPRIS || CLAPPER_HAVE_SERVER || CLAPPER_HAVE_DISCOVERER)
ClapperFeature *feature = NULL;
#endif
#if CLAPPER_HAVE_MPRIS
gchar mpris_name[45];
g_snprintf (mpris_name, sizeof (mpris_name),
"org.mpris.MediaPlayer2.Clapper.instance%" G_GUINT16_FORMAT, instance_count++);
#endif
self->settings = g_settings_new (CLAPPER_APP_ID);
self->last_volume = PERCENTAGE_ROUND (g_settings_get_double (self->settings, "volume"));
if ((proxy = clapper_enhancer_proxy_list_get_proxy_by_module (proxies, "clapper-mpris"))) {
clapper_enhancer_proxy_set_locally (proxy,
"own-name", mpris_name,
"identity", CLAPPER_APP_NAME,
"desktop-entry", CLAPPER_APP_ID,
"queue-controllable", TRUE, NULL);
gst_object_unref (proxy);
} else {
#if CLAPPER_HAVE_MPRIS
feature = CLAPPER_FEATURE (clapper_mpris_new (
mpris_name, CLAPPER_APP_NAME, CLAPPER_APP_ID));
clapper_mpris_set_queue_controllable (CLAPPER_MPRIS (feature), TRUE);
clapper_player_add_feature (player, feature);
gst_object_unref (feature);
feature = CLAPPER_FEATURE (clapper_mpris_new (
mpris_name, CLAPPER_APP_NAME, CLAPPER_APP_ID));
clapper_mpris_set_queue_controllable (CLAPPER_MPRIS (feature), TRUE);
clapper_player_add_feature (player, feature);
gst_object_unref (feature);
#endif
}
#if CLAPPER_HAVE_SERVER
feature = CLAPPER_FEATURE (clapper_server_new ());

View File

@@ -21,10 +21,10 @@ window.info .subcontent streamlist preferencesgroup {
window.preferences .subcontent {
margin: 16px;
}
window.preferences .configsubpage .subcontent popover {
window.preferences .pluginssubpage .subcontent popover {
min-width: 264px;
}
window.preferences .configsubpage .subcontent button.pill {
window.preferences .pluginssubpage .subcontent button.pill {
margin-top: 8px;
margin-bottom: 8px;
}

View File

@@ -1,11 +1,10 @@
appstream_cli = find_program('appstreamcli', required: false)
if appstream_cli.found()
appstream_util = find_program('appstream-util', required: false)
if appstream_util.found()
test('Validate appstream file',
appstream_cli,
appstream_util,
args: [
'validate',
'--no-net',
'--explain',
'validate-relax',
'--nonet',
join_paths(meson.current_source_dir(), 'metainfo', 'com.github.rafostar.Clapper.metainfo.xml'),
]
)

View File

@@ -31,10 +31,8 @@
gint
main (gint argc, gchar **argv)
{
GApplication *application;
ClapperEnhancerProxyList *proxies;
const gchar *clapper_ldir;
guint i, n_proxies;
GApplication *application;
gint status;
#ifdef G_OS_WIN32
@@ -66,15 +64,6 @@ main (gint argc, gchar **argv)
resolution = clapper_app_utils_win_hi_res_clock_start ();
#endif
proxies = clapper_get_global_enhancer_proxies ();
n_proxies = clapper_enhancer_proxy_list_get_n_proxies (proxies);
/* Allow usage of all enhancers */
for (i = 0; i < n_proxies; ++i) {
ClapperEnhancerProxy *proxy = clapper_enhancer_proxy_list_peek_proxy (proxies, i);
clapper_enhancer_proxy_set_target_creation_allowed (proxy, TRUE);
}
application = clapper_app_application_new ();
status = g_application_run (application, argc, argv);

View File

@@ -105,9 +105,6 @@ if not pp_option.disabled()
if cgraph_dep.found() and gvc_dep.found()
clapperapp_c_args += ['-DHAVE_GRAPHVIZ']
clapperapp_deps += [cgraph_dep, gvc_dep]
if gvc_dep.version().version_compare('>= 13.0.0')
clapperapp_c_args += ['-DHAVE_GVC_13']
endif
clapperapp_available_functionalities += 'pipeline-preview'
elif pp_option.enabled()
error('pipeline-preview option was enabled, but required dependencies were not found')

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: clapper\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-02-15 12:47+0100\n"
"PO-Revision-Date: 2025-02-15 12:10\n"
"PO-Revision-Date: 2025-06-14 17:10\n"
"Last-Translator: \n"
"Language-Team: Arabic\n"
"Language: ar_SA\n"
@@ -20,30 +20,30 @@ msgstr ""
#. Translators: Do NOT translate app name!
#: src/bin/clapper-app/data/applications/com.github.rafostar.Clapper.desktop.in:4
msgid "Clapper"
msgstr ""
msgstr "كلابر"
#: src/bin/clapper-app/data/applications/com.github.rafostar.Clapper.desktop.in:5
msgid "Multimedia Player"
msgstr ""
msgstr "مشغل وسائط متعددة"
#: src/bin/clapper-app/data/applications/com.github.rafostar.Clapper.desktop.in:6
msgid "Play videos and music"
msgstr ""
msgstr "شغِّل الفيديوهات والموسيقى"
#. Translators: Search terms to find this application. Do NOT translate the semicolons!
#: src/bin/clapper-app/data/applications/com.github.rafostar.Clapper.desktop.in:16
msgid "Video;Movie;Film;Clip;Series;Player;Playlist;DVD;TV;Disc;"
msgstr ""
msgstr "الفيديوهات;الفيلم;الأفلام;المقطع;المقاطع;المسلسلات;المشغلات;قائمة تشغيل;DVD;دي في دي;التلفاز;القرص;Clapper;"
#: src/bin/clapper-app/data/applications/com.github.rafostar.Clapper.desktop.in:22
#: src/bin/clapper-app/ui/clapper-app-headerbar.ui:183
msgid "New Window"
msgstr ""
msgstr "نافذة جديدة"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:14
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:14
msgid "Codec"
msgstr ""
msgstr "المرماز"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:24
msgid "Channels"
@@ -65,23 +65,23 @@ msgstr ""
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:70
#: src/bin/clapper-app/ui/clapper-app-subtitle-stream-list-item.ui:24
msgid "Language"
msgstr ""
msgstr "اللغة"
#: src/bin/clapper-app/ui/clapper-app-headerbar.ui:189
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:80
msgid "Preferences"
msgstr "الإعدادات"
msgstr "التفضيلات"
#: src/bin/clapper-app/ui/clapper-app-headerbar.ui:193
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:84
msgid "Keyboard Shortcuts"
msgstr ""
msgstr "اختصارات لوحة المفاتيح"
#. TRANSLATORS: Please do not translate application name
#: src/bin/clapper-app/ui/clapper-app-headerbar.ui:200
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:91
msgid "About Clapper"
msgstr "حول Clapper"
msgstr "عَنْ «كلابر»"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:10
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:11
@@ -90,19 +90,19 @@ msgstr "عام"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:13
msgid "New window"
msgstr ""
msgstr "نافذة جديدة"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:19
msgid "Open preferences"
msgstr ""
msgstr "افتح التفضيلات"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:25
msgid "Show shortcuts"
msgstr "إظهار الاختصارات"
msgstr "أظهِر الاختصارات"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:31
msgid "Toggle fullscreen"
msgstr "تبديل ملء الشاشة"
msgstr "بدِّل ملء الشاشة"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:32
msgid "Double tap | Double click"
@@ -122,7 +122,7 @@ msgstr ""
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:51
msgid "Quit"
msgstr "خروج"
msgstr "اخرج"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:59
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:32
@@ -133,13 +133,13 @@ msgstr "الوسائط"
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:55
#: src/bin/clapper-app/ui/clapper-app-queue-list.ui:178
msgid "Add Files…"
msgstr ""
msgstr "أضِف ملفات…"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:68
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:65
#: src/bin/clapper-app/ui/clapper-app-queue-list.ui:182
msgid "Add URI…"
msgstr ""
msgstr "أضِف URI…"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:76
msgid "Queue"
@@ -148,12 +148,12 @@ msgstr ""
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:80
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:87
msgid "Next item"
msgstr "المحتوى التالي"
msgstr "العنصر التالي"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:94
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:101
msgid "Previous item"
msgstr "المحتوى السابق"
msgstr "العنصر السابق"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:107
msgid "Change progression mode"
@@ -162,11 +162,11 @@ msgstr ""
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:115
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:76
msgid "Playback"
msgstr "المشغل"
msgstr "التشغيل"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:118
msgid "Toggle play"
msgstr "بَدْءّ / إيقاف"
msgstr "بدِّل التشغيل"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:119
msgid "Tap | Left click"
@@ -175,7 +175,7 @@ msgstr ""
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:126
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:134
msgid "Seek forward"
msgstr "التقدم للأمام"
msgstr "تقدَم للأمام"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:127
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:151
@@ -190,11 +190,11 @@ msgstr ""
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:142
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:150
msgid "Seek backward"
msgstr "الرجوع للوراء"
msgstr "عُد للوراء"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:157
msgid "Volume up"
msgstr "رفع مستوى الصوت"
msgstr "ارفع شدة الصوت"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:158
msgid "Scroll up"
@@ -202,7 +202,7 @@ msgstr ""
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:164
msgid "Volume down"
msgstr "خفض مستوى الصوت"
msgstr "اخفض شدة الصوت"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:165
msgid "Scroll down"
@@ -210,7 +210,7 @@ msgstr ""
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:171
msgid "Toggle mute"
msgstr "كتم الصوت"
msgstr "بدِّل الكتم"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:177
msgid "Speed up"
@@ -232,7 +232,7 @@ msgstr "الفصل السابق"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:13
msgid "Info"
msgstr ""
msgstr "معلومات"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:35
#: src/bin/clapper-app/ui/clapper-app-subtitle-stream-list-item.ui:14
@@ -254,7 +254,7 @@ msgstr ""
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:84
#: src/bin/clapper-app/clapper-app-list-item-utils.c:36
msgid "Video"
msgstr ""
msgstr "الفيديو"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:107
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:80
@@ -266,7 +266,7 @@ msgstr "الصوت"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:100
#: src/bin/clapper-app/clapper-app-list-item-utils.c:42
msgid "Subtitles"
msgstr "التَّرْجَمَةً"
msgstr "الترجَمة"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:155
msgid "Video Playback"
@@ -309,11 +309,11 @@ msgstr ""
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:15
msgid "Seeking"
msgstr "الوضع"
msgstr "السعي"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:18
msgid "Method"
msgstr ""
msgstr "الطريقة"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:19
msgid "A preferred method used for seeking"
@@ -349,15 +349,15 @@ msgstr "النسبة المئوية"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:63
msgid "Features"
msgstr ""
msgstr "الميزات"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:66
msgid "Server"
msgstr "الخادم"
msgstr "الخادوم"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:67
msgid "Control player remotely"
msgstr "التحكم بالوسائط عن بعد"
msgstr "يتحكم بالوسائط عن بعد"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:83
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:103
@@ -374,7 +374,7 @@ msgstr ""
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:118
msgid "Default font"
msgstr "الخط الافتراضي"
msgstr "الخط المبدئي"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:119
msgid "Text font used for subtitles when media does not explicitly specify one"
@@ -382,15 +382,15 @@ msgstr ""
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:137
msgid "Tweaks"
msgstr "تعديلات"
msgstr "التطويعات"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:144
msgid "Plugin ranking"
msgstr "أعدادات الإضافات"
msgstr "منازل الملحقات"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:145
msgid "Alter default ranks of GStreamer plugins"
msgstr "تغيير الأعدادات الافتراضية للأضافات GStreamer"
msgstr "يغيِّر المنازل المبدئية لملحقات GStreamer"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:164
msgid "Plugin Ranking"
@@ -443,11 +443,11 @@ msgstr ""
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:18
msgid "Enter or drop URI here"
msgstr "أدخل أو الصق URI هنا"
msgstr "أدخِل أو أسقِط URI هنا"
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:22
msgid "Cancel"
msgstr "إلغاء"
msgstr "ألغِ"
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:24
msgid "Resolution"
@@ -464,7 +464,8 @@ msgstr ""
#. TRANSLATORS: Put your name(s) here for credits or leave untranslated
#: src/bin/clapper-app/clapper-app-about-window.c:47
msgid "translator-credits"
msgstr "Yousef Fawaz"
msgstr "Yousef Fawaz\n"
"أحمد النجماوي <iramosu@protonmail.com>"
#: src/bin/clapper-app/clapper-app-application.c:696
msgid "Create a new window"
@@ -512,7 +513,7 @@ msgstr ""
#: src/bin/clapper-app/clapper-app-info-window.c:86
msgid "Hardware"
msgstr ""
msgstr "العتاد"
#: src/bin/clapper-app/clapper-app-info-window.c:86
msgid "Software"
@@ -520,7 +521,7 @@ msgstr ""
#: src/bin/clapper-app/clapper-app-preferences-window.c:441
msgid "Accurate"
msgstr "دَقيق"
msgstr "دقيق"
#: src/bin/clapper-app/clapper-app-preferences-window.c:443
msgid "Normal"

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: clapper\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-02-15 12:47+0100\n"
"PO-Revision-Date: 2025-02-15 13:14\n"
"PO-Revision-Date: 2025-05-18 14:52\n"
"Last-Translator: \n"
"Language-Team: Persian\n"
"Language: fa_IR\n"
@@ -305,7 +305,7 @@ msgstr "جریان‌های زیرنویس"
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:46
msgid "Start by adding media to playback queue"
msgstr "آغزا با افزودن رسانه به صف پخش"
msgstr "آغاز با افزودن رسانه به صف پخش"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:15
msgid "Seeking"
@@ -402,7 +402,7 @@ msgstr "افزایه‌های موجود"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:183
msgid "Select a plugin and its feature to override rank for. When multiple elements have similiar capabilities, the one with highest value is preferred."
msgstr "گزینش افزایه و ویژگیش برای پایمالی رتبه دهیش. هنگامی که چندین عنصر قابلیت‌ّای مشابهی دارند، آنی که بیش‌ترین رتبه را دارد ترجیح داده می‌شود."
msgstr "گزینش افزایه و ویژگیش برای پایمالی رتبه دهیش. هنگامی که چندین عنصر قابلیت‌های مشابهی دارند، آنی که بیش‌ترین رتبه را دارد ترجیح داده می‌شود."
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:186
msgid "Plugin"

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: clapper\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-02-15 12:47+0100\n"
"PO-Revision-Date: 2025-02-15 12:11\n"
"PO-Revision-Date: 2025-05-01 13:52\n"
"Last-Translator: \n"
"Language-Team: Chinese Simplified\n"
"Language: zh_CN\n"
@@ -33,7 +33,7 @@ msgstr "播放视频和音乐"
#. Translators: Search terms to find this application. Do NOT translate the semicolons!
#: src/bin/clapper-app/data/applications/com.github.rafostar.Clapper.desktop.in:16
msgid "Video;Movie;Film;Clip;Series;Player;Playlist;DVD;TV;Disc;"
msgstr ""
msgstr "视频电影影片短片连续剧播放器播放列表DVD电视唱片"
#: src/bin/clapper-app/data/applications/com.github.rafostar.Clapper.desktop.in:22
#: src/bin/clapper-app/ui/clapper-app-headerbar.ui:183

View File

@@ -136,24 +136,6 @@
<object class="AdwPreferencesPage">
<property name="title" translatable="yes">Tweaks</property>
<property name="icon-name">applications-engineering-symbolic</property>
<child>
<object class="AdwPreferencesGroup">
<property name="title" translatable="no">Clapper</property>
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Enhancers</property>
<property name="subtitle" translatable="yes">Browse and configure properties of available enhancers</property>
<property name="activatable">true</property>
<signal name="activated" handler="enhancers_config_activated_cb"/>
<child>
<object class="GtkImage">
<property name="icon_name">go-next-symbolic</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup">
<property name="title" translatable="no">GStreamer</property>
@@ -178,124 +160,6 @@
<class name="preferences"/>
</style>
</template>
<object class="AdwNavigationPage" id="enhancers_subpage">
<property name="title" translatable="yes">Clapper Enhancers</property>
<property name="child">
<object class="AdwToolbarView">
<child type="top">
<object class="AdwHeaderBar"/>
</child>
<property name="content">
<object class="GtkScrolledWindow">
<child>
<object class="AdwClamp">
<style>
<class name="subcontent"/>
</style>
<child>
<object class="GtkStack" id="enhancers_stack">
<child>
<object class="GtkBox" id="browse_enhancers_page">
<property name="orientation">vertical</property>
<child>
<object class="AdwPreferencesGroup">
<property name="title" translatable="yes">Available enhancers</property>
<property name="description" translatable="yes">Select an enhancer plugin to view its information and properties to configure.</property>
<child>
<object class="AdwComboRow" id="enhancers_combo_row">
<property name="title" translatable="yes">Enhancer</property>
<property name="enable-search">true</property>
<property name="expression">
<lookup type="ClapperEnhancerProxy" name="friendly-name"/>
</property>
<signal name="notify::selected" handler="selected_enhancer_changed_cb"/>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<binding name="visible">
<closure type="gboolean" function="list_has_selection_closure">
<lookup name="selected">enhancers_combo_row</lookup>
</closure>
</binding>
<child>
<object class="AdwPreferencesGroup">
<property name="title" translatable="yes">Information</property>
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Module</property>
<binding name="subtitle">
<lookup name="module-name" type="ClapperEnhancerProxy">
<lookup name="selected-item">enhancers_combo_row</lookup>
</lookup>
</binding>
<style>
<class name="property"/>
</style>
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Description</property>
<binding name="subtitle">
<lookup name="description" type="ClapperEnhancerProxy">
<lookup name="selected-item">enhancers_combo_row</lookup>
</lookup>
</binding>
<style>
<class name="property"/>
</style>
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Version</property>
<binding name="subtitle">
<lookup name="version" type="ClapperEnhancerProxy">
<lookup name="selected-item">enhancers_combo_row</lookup>
</lookup>
</binding>
<style>
<class name="property"/>
</style>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup" id="enhancer_config_group">
<property name="title" translatable="yes">Properties</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="AdwStatusPage" id="no_enhancers_page">
<property name="vexpand">true</property>
<property name="hexpand">true</property>
<property name="icon-name">edit-find-symbolic</property>
<property name="title" translatable="yes">No Clapper Enhancers Found</property>
<property name="description" translatable="yes">Install some to add more cool functionalities to the player!</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</property>
</object>
</property>
<signal name="unrealize" handler="plugin_ranking_unrealize_cb"/>
<style>
<class name="configsubpage"/>
</style>
</object>
<object class="AdwNavigationPage" id="plugins_subpage">
<property name="title" translatable="yes">Plugin Ranking</property>
<property name="child">
@@ -376,7 +240,7 @@
</property>
<signal name="unrealize" handler="plugin_ranking_unrealize_cb"/>
<style>
<class name="configsubpage"/>
<class name="pluginssubpage"/>
</style>
</object>
</interface>

View File

@@ -1,268 +0,0 @@
/* Clapper GTK Integration Library
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
/**
* ClapperGtkAudio:
*
* A GTK widget for audio playback with Clapper API.
*
* #ClapperGtkAudio is a widget meant for integrating audio playback
* within GTK application. It exposes [class@Clapper.Player] through its
* base class [property@ClapperGtk.Av:player] property.
*
* Other widgets (buttons, seek bar, etc.) provided by `ClapperGtk` library, once placed
* anywhere inside audio container (including nesting within another widget like [class@Gtk.Box])
* will automatically control #ClapperGtkAudio they are within. This allows to freely create
* custom UI best suited for specific application.
*
* # Basic usage
*
* A typical use case is to embed audio widget as part of your app where audio playback
* is needed (can be even the very first child of the window). Get the [class@Clapper.Player]
* belonging to the AV widget and start adding new [class@Clapper.MediaItem] items to the
* [class@Clapper.Queue] for playback. For more information please refer to the Clapper
* playback library documentation.
*
* # Actions
*
* You can use built-in actions of parent [class@ClapperGtk.Av].
* See its documentation for the list of available ones.
*
* # ClapperGtkAudio as GtkBuildable
*
* #ClapperGtkAudio implementation of the [iface@Gtk.Buildable] interface supports
* placing a single widget (which might then hold multiple widgets) as `<child>` element.
*
* ```xml
* <object class="ClapperGtkAudio" id="audio">
* <child>
* <object class="GtkBox">
* <property name="orientation">horizontal</property>
* <child>
* <object class="ClapperGtkPreviousItemButton">
* </child>
* <child>
* <object class="ClapperGtkTogglePlayButton">
* </child>
* <child>
* <object class="ClapperGtkNextItemButton">
* </child>
* </object>
* </child>
* </object>
* ```
*
* Since: 0.10
*/
#include "config.h"
#include "clapper-gtk-audio.h"
#define GST_CAT_DEFAULT clapper_gtk_audio_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperGtkAudio
{
ClapperGtkAv parent;
GtkWidget *child;
};
static void
clapper_gtk_audio_add_child (GtkBuildable *buildable,
GtkBuilder *builder, GObject *child, const char *type)
{
if (GTK_IS_WIDGET (child)) {
clapper_gtk_audio_set_child (CLAPPER_GTK_AUDIO (buildable), GTK_WIDGET (child));
} else {
GtkBuildableIface *parent_iface = g_type_interface_peek_parent (GTK_BUILDABLE_GET_IFACE (buildable));
parent_iface->add_child (buildable, builder, child, type);
}
}
static void
_buildable_iface_init (GtkBuildableIface *iface)
{
iface->add_child = clapper_gtk_audio_add_child;
}
#define parent_class clapper_gtk_audio_parent_class
G_DEFINE_TYPE_WITH_CODE (ClapperGtkAudio, clapper_gtk_audio, CLAPPER_GTK_TYPE_AV,
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, _buildable_iface_init))
enum
{
PROP_0,
PROP_CHILD,
PROP_LAST
};
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
static inline void
_unparent_child (ClapperGtkAudio *self)
{
GtkWidget *child;
if ((child = gtk_widget_get_first_child (GTK_WIDGET (self))))
gtk_widget_unparent (child);
}
/**
* clapper_gtk_audio_new:
*
* Creates a new #ClapperGtkAudio instance.
*
* Newly created audio widget will also have set "scaletempo" GStreamer element
* as default audio filter on its [class@Clapper.Player] and disable video and
* subtitle streams. This can be changed after construction by setting
* corresponding player properties.
*
* Returns: a new audio #GtkWidget.
*/
GtkWidget *
clapper_gtk_audio_new (void)
{
return g_object_new (CLAPPER_GTK_TYPE_AUDIO, NULL);
}
/**
* clapper_gtk_audio_set_child:
* @audio: a #ClapperGtkAudio
* @child: (nullable): a #GtkWidget
*
* Set a child #GtkWidget of @audio.
*/
void
clapper_gtk_audio_set_child (ClapperGtkAudio *self, GtkWidget *child)
{
g_return_if_fail (CLAPPER_GTK_IS_AUDIO (self));
g_return_if_fail (GTK_IS_WIDGET (child));
_unparent_child (self);
if (child)
gtk_widget_set_parent (child, GTK_WIDGET (self));
}
/**
* clapper_gtk_audio_get_child:
* @audio: a #ClapperGtkAudio
*
* Get a child #GtkWidget of @audio.
*
* Returns: (transfer none) (nullable): #GtkWidget set as child.
*/
GtkWidget *
clapper_gtk_audio_get_child (ClapperGtkAudio *self)
{
g_return_val_if_fail (CLAPPER_GTK_IS_AUDIO (self), NULL);
return gtk_widget_get_first_child (GTK_WIDGET (self));
}
static void
clapper_gtk_audio_init (ClapperGtkAudio *self)
{
}
static void
clapper_gtk_audio_constructed (GObject *object)
{
ClapperGtkAudio *self = CLAPPER_GTK_AUDIO_CAST (object);
ClapperPlayer *player;
G_OBJECT_CLASS (parent_class)->constructed (object);
player = clapper_gtk_av_get_player (CLAPPER_GTK_AV_CAST (self));
clapper_player_set_video_enabled (player, FALSE);
clapper_player_set_subtitles_enabled (player, FALSE);
}
static void
clapper_gtk_audio_dispose (GObject *object)
{
ClapperGtkAudio *self = CLAPPER_GTK_AUDIO_CAST (object);
_unparent_child (self);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
clapper_gtk_audio_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
ClapperGtkAudio *self = CLAPPER_GTK_AUDIO_CAST (object);
switch (prop_id) {
case PROP_CHILD:
g_value_set_object (value, clapper_gtk_audio_get_child (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_gtk_audio_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
ClapperGtkAudio *self = CLAPPER_GTK_AUDIO_CAST (object);
switch (prop_id) {
case PROP_CHILD:
clapper_gtk_audio_set_child (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_gtk_audio_class_init (ClapperGtkAudioClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappergtkaudio", GST_DEBUG_FG_MAGENTA,
"Clapper GTK Audio");
gobject_class->constructed = clapper_gtk_audio_constructed;
gobject_class->get_property = clapper_gtk_audio_get_property;
gobject_class->set_property = clapper_gtk_audio_set_property;
gobject_class->dispose = clapper_gtk_audio_dispose;
/**
* ClapperGtkAudio:child:
*
* The child widget of `ClapperGtkAudio`.
*/
param_specs[PROP_CHILD] = g_param_spec_object ("child",
NULL, NULL, GTK_TYPE_WIDGET,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_GENERIC);
gtk_widget_class_set_css_name (widget_class, "clapper-gtk-audio");
}

View File

@@ -1,49 +0,0 @@
/* Clapper GTK Integration Library
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once
#if !defined(__CLAPPER_GTK_INSIDE__) && !defined(CLAPPER_GTK_COMPILATION)
#error "Only <clapper-gtk/clapper-gtk.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
#include <clapper-gtk/clapper-gtk-av.h>
#include <clapper-gtk/clapper-gtk-visibility.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_AUDIO (clapper_gtk_audio_get_type())
#define CLAPPER_GTK_AUDIO_CAST(obj) ((ClapperGtkAudio *)(obj))
CLAPPER_GTK_API
G_DECLARE_FINAL_TYPE (ClapperGtkAudio, clapper_gtk_audio, CLAPPER_GTK, AUDIO, ClapperGtkAv)
CLAPPER_GTK_API
GtkWidget * clapper_gtk_audio_new (void);
CLAPPER_GTK_API
void clapper_gtk_audio_set_child (ClapperGtkAudio *audio, GtkWidget *child);
CLAPPER_GTK_API
GtkWidget * clapper_gtk_audio_get_child (ClapperGtkAudio *audio);
G_END_DECLS

View File

@@ -1,645 +0,0 @@
/* Clapper GTK Integration Library
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
/**
* ClapperGtkAv:
*
* A base class for GTK audio and video widgets.
*
* See its descendants: [class@ClapperGtk.Audio] and [class@ClapperGtk.Video].
*
* # Actions
*
* #ClapperGtkAv defines a set of built-in actions:
*
* ```yaml
* - "av.toggle-play": toggle play/pause
* - "av.play": start/resume playback
* - "av.pause": pause playback
* - "av.stop": stop playback
* - "av.seek": seek to position (variant "d")
* - "av.seek-custom": seek to position using seek method (variant "(di)")
* - "av.toggle-mute": toggle mute state
* - "av.set-mute": set mute state (variant "b")
* - "av.volume-up": increase volume by 2%
* - "av.volume-down": decrease volume by 2%
* - "av.set-volume": set volume to specified value (variant "d")
* - "av.speed-up": increase speed (from 0.05x - 2x range to nearest quarter)
* - "av.speed-down": decrease speed (from 0.05x - 2x range to nearest quarter)
* - "av.set-speed": set speed to specified value (variant "d")
* - "av.previous-item": select previous item in queue
* - "av.next-item": select next item in queue
* - "av.select-item": select item at specified index in queue (variant "u")
* ```
*
* Since: 0.10
*/
#include "config.h"
#include <math.h>
#include "clapper-gtk-av.h"
#define PERCENTAGE_ROUND(a) (round ((gdouble) a / 0.01) * 0.01)
#define DEFAULT_AUTO_INHIBIT FALSE
#define GST_CAT_DEFAULT clapper_gtk_av_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
typedef struct _ClapperGtkAvPrivate ClapperGtkAvPrivate;
struct _ClapperGtkAvPrivate
{
ClapperPlayer *player;
gboolean auto_inhibit;
guint inhibit_cookie;
};
#define parent_class clapper_gtk_av_parent_class
G_DEFINE_TYPE_WITH_PRIVATE (ClapperGtkAv, clapper_gtk_av, GTK_TYPE_WIDGET)
enum
{
PROP_0,
PROP_PLAYER,
PROP_AUTO_INHIBIT,
PROP_INHIBITED,
PROP_LAST
};
static gboolean provider_added = FALSE;
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
static void
toggle_play_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget);
ClapperPlayer *player = clapper_gtk_av_get_player (self);
switch (clapper_player_get_state (player)) {
case CLAPPER_PLAYER_STATE_PLAYING:
clapper_player_pause (player);
break;
case CLAPPER_PLAYER_STATE_STOPPED:
case CLAPPER_PLAYER_STATE_PAUSED:
clapper_player_play (player);
break;
default:
break;
}
}
static void
play_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget);
ClapperPlayer *player = clapper_gtk_av_get_player (self);
clapper_player_play (player);
}
static void
pause_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget);
ClapperPlayer *player = clapper_gtk_av_get_player (self);
clapper_player_pause (player);
}
static void
stop_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget);
ClapperPlayer *player = clapper_gtk_av_get_player (self);
clapper_player_stop (player);
}
static void
seek_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget);
ClapperPlayer *player = clapper_gtk_av_get_player (self);
gdouble position = g_variant_get_double (parameter);
clapper_player_seek (player, position);
}
static void
seek_custom_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget);
ClapperPlayer *player = clapper_gtk_av_get_player (self);
ClapperPlayerSeekMethod method = CLAPPER_PLAYER_SEEK_METHOD_NORMAL;
gdouble position = 0;
g_variant_get (parameter, "(di)", &position, &method);
clapper_player_seek_custom (player, position, method);
}
static void
toggle_mute_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget);
ClapperPlayer *player = clapper_gtk_av_get_player (self);
clapper_player_set_mute (player, !clapper_player_get_mute (player));
}
static void
set_mute_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget);
ClapperPlayer *player = clapper_gtk_av_get_player (self);
gboolean mute = g_variant_get_boolean (parameter);
clapper_player_set_mute (player, mute);
}
static void
volume_up_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget);
ClapperPlayer *player = clapper_gtk_av_get_player (self);
gdouble volume = (clapper_player_get_volume (player) + 0.02);
if (volume > 2.0)
volume = 2.0;
clapper_player_set_volume (player, PERCENTAGE_ROUND (volume));
}
static void
volume_down_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget);
ClapperPlayer *player = clapper_gtk_av_get_player (self);
gdouble volume = (clapper_player_get_volume (player) - 0.02);
if (volume < 0)
volume = 0;
clapper_player_set_volume (player, PERCENTAGE_ROUND (volume));
}
static void
set_volume_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget);
ClapperPlayer *player = clapper_gtk_av_get_player (self);
gdouble volume = g_variant_get_double (parameter);
clapper_player_set_volume (player, volume);
}
static void
speed_up_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget);
ClapperPlayer *player = clapper_gtk_av_get_player (self);
gdouble dest, speed = clapper_player_get_speed (player);
if (speed >= 2.0)
return;
dest = 0.25;
while (speed >= dest)
dest += 0.25;
if (dest > 2.0)
dest = 2.0;
clapper_player_set_speed (player, dest);
}
static void
speed_down_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget);
ClapperPlayer *player = clapper_gtk_av_get_player (self);
gdouble dest, speed = clapper_player_get_speed (player);
if (speed <= 0.05)
return;
dest = 2.0;
while (speed <= dest)
dest -= 0.25;
if (dest < 0.05)
dest = 0.05;
clapper_player_set_speed (player, dest);
}
static void
set_speed_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget);
ClapperPlayer *player = clapper_gtk_av_get_player (self);
gdouble speed = g_variant_get_double (parameter);
clapper_player_set_speed (player, speed);
}
static void
previous_item_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget);
ClapperPlayer *player = clapper_gtk_av_get_player (self);
clapper_queue_select_previous_item (clapper_player_get_queue (player));
}
static void
next_item_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget);
ClapperPlayer *player = clapper_gtk_av_get_player (self);
clapper_queue_select_next_item (clapper_player_get_queue (player));
}
static void
select_item_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget);
ClapperPlayer *player = clapper_gtk_av_get_player (self);
guint index = g_variant_get_uint32 (parameter);
clapper_queue_select_index (clapper_player_get_queue (player), index);
}
static void
_ensure_css_provider (void)
{
GdkDisplay *display;
if (provider_added)
return;
display = gdk_display_get_default ();
if (G_LIKELY (display != NULL)) {
GtkCssProvider *provider = gtk_css_provider_new ();
gtk_css_provider_load_from_resource (provider,
CLAPPER_GTK_RESOURCE_PREFIX "/css/styles.css");
gtk_style_context_add_provider_for_display (display,
(GtkStyleProvider *) provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION - 1);
g_object_unref (provider);
provider_added = TRUE;
}
}
static inline void
_set_inhibit_session (ClapperGtkAv *self, gboolean inhibit)
{
ClapperGtkAvPrivate *priv = clapper_gtk_av_get_instance_private (self);
GtkRoot *root;
GApplication *app;
gboolean inhibited = (priv->inhibit_cookie != 0);
if (inhibited == inhibit)
return;
GST_DEBUG_OBJECT (self, "Trying to %sinhibit session...", (inhibit) ? "" : "un");
root = gtk_widget_get_root (GTK_WIDGET (self));
if (!root && !GTK_IS_WINDOW (root)) {
GST_WARNING_OBJECT (self, "Cannot %sinhibit session "
"without root window", (inhibit) ? "" : "un");
return;
}
/* NOTE: Not using application from window prop,
* as it goes away early when unrooting */
app = g_application_get_default ();
if (!app && !GTK_IS_APPLICATION (app)) {
GST_WARNING_OBJECT (self, "Cannot %sinhibit session "
"without window application set", (inhibit) ? "" : "un");
return;
}
if (inhibited) {
gtk_application_uninhibit (GTK_APPLICATION (app), priv->inhibit_cookie);
priv->inhibit_cookie = 0;
}
if (inhibit) {
priv->inhibit_cookie = gtk_application_inhibit (GTK_APPLICATION (app),
GTK_WINDOW (root), GTK_APPLICATION_INHIBIT_IDLE,
"Media is playing");
}
GST_DEBUG_OBJECT (self, "Session %sinhibited", (inhibit) ? "" : "un");
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_INHIBITED]);
}
static void
_player_state_changed_cb (ClapperPlayer *player,
GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkAv *self)
{
ClapperGtkAvPrivate *priv = clapper_gtk_av_get_instance_private (self);
if (priv->auto_inhibit) {
ClapperPlayerState state = clapper_player_get_state (player);
_set_inhibit_session (self, state == CLAPPER_PLAYER_STATE_PLAYING);
}
}
/**
* clapper_gtk_av_get_player:
* @av: a #ClapperGtkAv
*
* Get #ClapperPlayer used by this #ClapperGtkAv instance.
*
* Returns: (transfer none): a #ClapperPlayer used by widget.
*
* Since: 0.10
*/
ClapperPlayer *
clapper_gtk_av_get_player (ClapperGtkAv *self)
{
ClapperGtkAvPrivate *priv;
g_return_val_if_fail (CLAPPER_GTK_IS_AV (self), NULL);
priv = clapper_gtk_av_get_instance_private (self);
return priv->player;
}
/**
* clapper_gtk_av_set_auto_inhibit:
* @av: a #ClapperGtkAv
* @inhibit: whether to enable automatic session inhibit
*
* Set whether widget should try to automatically inhibit session
* from idling (and possibly screen going black) when media is playing.
*
* Since: 0.10
*/
void
clapper_gtk_av_set_auto_inhibit (ClapperGtkAv *self, gboolean inhibit)
{
ClapperGtkAvPrivate *priv;
g_return_if_fail (CLAPPER_GTK_IS_AV (self));
priv = clapper_gtk_av_get_instance_private (self);
if (priv->auto_inhibit != inhibit) {
priv->auto_inhibit = inhibit;
/* Uninhibit if we were auto inhibited earlier */
if (!priv->auto_inhibit)
_set_inhibit_session (self, FALSE);
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_AUTO_INHIBIT]);
}
}
/**
* clapper_gtk_av_get_auto_inhibit:
* @av: a #ClapperGtkAv
*
* Get whether automatic session inhibit is enabled.
*
* Returns: %TRUE if enabled, %FALSE otherwise.
*
* Since: 0.10
*/
gboolean
clapper_gtk_av_get_auto_inhibit (ClapperGtkAv *self)
{
ClapperGtkAvPrivate *priv;
g_return_val_if_fail (CLAPPER_GTK_IS_AV (self), FALSE);
priv = clapper_gtk_av_get_instance_private (self);
return priv->auto_inhibit;
}
/**
* clapper_gtk_av_get_inhibited:
* @av: a #ClapperGtkAv
*
* Get whether session is currently inhibited by
* [property@ClapperGtk.Av:auto-inhibit].
*
* Returns: %TRUE if inhibited, %FALSE otherwise.
*
* Since: 0.10
*/
gboolean
clapper_gtk_av_get_inhibited (ClapperGtkAv *self)
{
ClapperGtkAvPrivate *priv;
g_return_val_if_fail (CLAPPER_GTK_IS_AV (self), FALSE);
priv = clapper_gtk_av_get_instance_private (self);
return (priv->inhibit_cookie != 0);
}
static void
clapper_gtk_av_root (GtkWidget *widget)
{
ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget);
ClapperGtkAvPrivate *priv = clapper_gtk_av_get_instance_private (self);
_ensure_css_provider ();
GTK_WIDGET_CLASS (parent_class)->root (widget);
if (priv->auto_inhibit) {
ClapperPlayerState state = clapper_player_get_state (priv->player);
_set_inhibit_session (self, state == CLAPPER_PLAYER_STATE_PLAYING);
}
}
static void
clapper_gtk_av_unroot (GtkWidget *widget)
{
ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget);
_set_inhibit_session (self, FALSE);
GTK_WIDGET_CLASS (parent_class)->unroot (widget);
}
static void
clapper_gtk_av_init (ClapperGtkAv *self)
{
ClapperGtkAvPrivate *priv = clapper_gtk_av_get_instance_private (self);
priv->auto_inhibit = DEFAULT_AUTO_INHIBIT;
}
static void
clapper_gtk_av_constructed (GObject *object)
{
ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (object);
ClapperGtkAvPrivate *priv = clapper_gtk_av_get_instance_private (self);
GstElement *afilter;
priv->player = clapper_player_new ();
g_signal_connect (priv->player, "notify::state",
G_CALLBACK (_player_state_changed_cb), self);
afilter = gst_element_factory_make ("scaletempo", NULL);
if (G_LIKELY (afilter != NULL))
clapper_player_set_audio_filter (priv->player, afilter);
G_OBJECT_CLASS (parent_class)->constructed (object);
}
static void
clapper_gtk_av_dispose (GObject *object)
{
ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (object);
ClapperGtkAvPrivate *priv = clapper_gtk_av_get_instance_private (self);
/* Something else might still be holding a reference on the player,
* thus we should disconnect everything before disposing template */
if (priv->player) {
g_signal_handlers_disconnect_by_func (priv->player,
_player_state_changed_cb, self);
}
gst_clear_object (&priv->player);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
clapper_gtk_av_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (object);
switch (prop_id) {
case PROP_PLAYER:
g_value_set_object (value, clapper_gtk_av_get_player (self));
break;
case PROP_AUTO_INHIBIT:
g_value_set_boolean (value, clapper_gtk_av_get_auto_inhibit (self));
break;
case PROP_INHIBITED:
g_value_set_boolean (value, clapper_gtk_av_get_inhibited (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_gtk_av_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (object);
switch (prop_id) {
case PROP_AUTO_INHIBIT:
clapper_gtk_av_set_auto_inhibit (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_gtk_av_class_init (ClapperGtkAvClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappergtkav", GST_DEBUG_FG_MAGENTA,
"Clapper GTK AV");
widget_class->root = clapper_gtk_av_root;
widget_class->unroot = clapper_gtk_av_unroot;
gobject_class->constructed = clapper_gtk_av_constructed;
gobject_class->get_property = clapper_gtk_av_get_property;
gobject_class->set_property = clapper_gtk_av_set_property;
gobject_class->dispose = clapper_gtk_av_dispose;
/**
* ClapperGtkAv:player:
*
* A #ClapperPlayer used by widget.
*/
param_specs[PROP_PLAYER] = g_param_spec_object ("player",
NULL, NULL, CLAPPER_TYPE_PLAYER,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkAv:auto-inhibit:
*
* Try to automatically inhibit session when media is playing.
*/
param_specs[PROP_AUTO_INHIBIT] = g_param_spec_boolean ("auto-inhibit",
NULL, NULL, DEFAULT_AUTO_INHIBIT,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkAv:inhibited:
*
* Get whether session is currently inhibited by playback.
*/
param_specs[PROP_INHIBITED] = g_param_spec_boolean ("inhibited",
NULL, NULL, FALSE,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
gtk_widget_class_install_action (widget_class, "av.toggle-play", NULL, toggle_play_action_cb);
gtk_widget_class_install_action (widget_class, "av.play", NULL, play_action_cb);
gtk_widget_class_install_action (widget_class, "av.pause", NULL, pause_action_cb);
gtk_widget_class_install_action (widget_class, "av.stop", NULL, stop_action_cb);
gtk_widget_class_install_action (widget_class, "av.seek", "d", seek_action_cb);
gtk_widget_class_install_action (widget_class, "av.seek-custom", "(di)", seek_custom_action_cb);
gtk_widget_class_install_action (widget_class, "av.toggle-mute", NULL, toggle_mute_action_cb);
gtk_widget_class_install_action (widget_class, "av.set-mute", "b", set_mute_action_cb);
gtk_widget_class_install_action (widget_class, "av.volume-up", NULL, volume_up_action_cb);
gtk_widget_class_install_action (widget_class, "av.volume-down", NULL, volume_down_action_cb);
gtk_widget_class_install_action (widget_class, "av.set-volume", "d", set_volume_action_cb);
gtk_widget_class_install_action (widget_class, "av.speed-up", NULL, speed_up_action_cb);
gtk_widget_class_install_action (widget_class, "av.speed-down", NULL, speed_down_action_cb);
gtk_widget_class_install_action (widget_class, "av.set-speed", "d", set_speed_action_cb);
gtk_widget_class_install_action (widget_class, "av.previous-item", NULL, previous_item_action_cb);
gtk_widget_class_install_action (widget_class, "av.next-item", NULL, next_item_action_cb);
gtk_widget_class_install_action (widget_class, "av.select-item", "u", select_item_action_cb);
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_GENERIC);
gtk_widget_class_set_css_name (widget_class, "clapper-gtk-av");
}

View File

@@ -1,60 +0,0 @@
/* Clapper GTK Integration Library
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once
#if !defined(__CLAPPER_GTK_INSIDE__) && !defined(CLAPPER_GTK_COMPILATION)
#error "Only <clapper-gtk/clapper-gtk.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
#include <clapper/clapper.h>
#include <clapper-gtk/clapper-gtk-visibility.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_AV (clapper_gtk_av_get_type())
#define CLAPPER_GTK_AV_CAST(obj) ((ClapperGtkAv *)(obj))
CLAPPER_GTK_API
G_DECLARE_DERIVABLE_TYPE (ClapperGtkAv, clapper_gtk_av, CLAPPER_GTK, AV, GtkWidget)
struct _ClapperGtkAvClass
{
GtkWidgetClass parent_class;
/*< private >*/
gpointer padding[4];
};
CLAPPER_GTK_API
ClapperPlayer * clapper_gtk_av_get_player (ClapperGtkAv *av);
CLAPPER_GTK_API
void clapper_gtk_av_set_auto_inhibit (ClapperGtkAv *av, gboolean inhibit);
CLAPPER_GTK_API
gboolean clapper_gtk_av_get_auto_inhibit (ClapperGtkAv *av);
CLAPPER_GTK_API
gboolean clapper_gtk_av_get_inhibited (ClapperGtkAv *av);
G_END_DECLS

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <clapper/clapper.h>

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "clapper-gtk-buffering-paintable-private.h"

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <clapper/clapper.h>

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
@@ -82,7 +83,7 @@ clapper_gtk_next_item_button_init (ClapperGtkNextItemButton *self)
{
gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
gtk_button_set_icon_name (GTK_BUTTON (self), "media-skip-forward-symbolic");
gtk_actionable_set_action_name (GTK_ACTIONABLE (self), "av.next-item");
gtk_actionable_set_action_name (GTK_ACTIONABLE (self), "video.next-item");
}
static void

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
@@ -82,7 +83,7 @@ clapper_gtk_previous_item_button_init (ClapperGtkPreviousItemButton *self)
{
gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
gtk_button_set_icon_name (GTK_BUTTON (self), "media-skip-backward-symbolic");
gtk_actionable_set_action_name (GTK_ACTIONABLE (self), "av.previous-item");
gtk_actionable_set_action_name (GTK_ACTIONABLE (self), "video.previous-item");
}
static void

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
@@ -395,47 +396,6 @@ _update_duration_label (ClapperGtkSeekBar *self, gdouble duration)
gtk_adjustment_set_upper (adjustment, duration);
}
static gboolean
_find_marks_in_widget (GtkWidget *widget, GtkWidget **top_marks, GtkWidget **bottom_marks)
{
GtkWidget *child;
if (g_strcmp0 (gtk_widget_get_css_name (widget), "marks") == 0) {
if (gtk_widget_has_css_class (widget, "top"))
*top_marks = widget;
else if (gtk_widget_has_css_class (widget, "bottom"))
*bottom_marks = widget;
/* Its unexpected to have marks within marks,
* so do not iterate children of marks widget */
return (*top_marks && *bottom_marks);
}
child = gtk_widget_get_first_child (widget);
while (child != NULL) {
if (_find_marks_in_widget (child, top_marks, bottom_marks))
return TRUE;
child = gtk_widget_get_next_sibling (child);
}
return FALSE;
}
static gboolean
_find_last_mark_in_marks (GtkWidget *marks, GtkWidget **last_mark)
{
GtkWidget *widget = gtk_widget_get_last_child (marks);
if (widget && g_strcmp0 (gtk_widget_get_css_name (widget), "mark") == 0) {
*last_mark = widget;
return TRUE;
}
return FALSE;
}
static void
_update_scale_marks (ClapperGtkSeekBar *self, ClapperTimeline *timeline)
{
@@ -464,43 +424,11 @@ _update_scale_marks (ClapperGtkSeekBar *self, ClapperTimeline *timeline)
for (i = 0; i < n_markers; ++i) {
ClapperMarker *marker = clapper_timeline_get_marker (timeline, i);
ClapperMarkerType marker_type = clapper_marker_get_marker_type (marker);
gdouble start = clapper_marker_get_start (marker);
gtk_scale_add_mark (GTK_SCALE (self->scale), start, GTK_POS_TOP, NULL);
gtk_scale_add_mark (GTK_SCALE (self->scale), start, GTK_POS_BOTTOM, NULL);
if (marker_type >= CLAPPER_MARKER_TYPE_CUSTOM_1) {
GtkWidget *top_marks = NULL, *bottom_marks = NULL;
GtkWidget *top_mark = NULL, *bottom_mark = NULL;
if (_find_marks_in_widget (self->scale, &top_marks, &bottom_marks)
&& _find_last_mark_in_marks (top_marks, &top_mark)
&& _find_last_mark_in_marks (bottom_marks, &bottom_mark)) {
const gchar *custom_name;
switch (marker_type) {
case CLAPPER_MARKER_TYPE_CUSTOM_1:
custom_name = "custom1";
break;
case CLAPPER_MARKER_TYPE_CUSTOM_2:
custom_name = "custom2";
break;
case CLAPPER_MARKER_TYPE_CUSTOM_3:
custom_name = "custom3";
break;
default:
custom_name = NULL;
break;
}
if (G_LIKELY (custom_name != NULL)) {
gtk_widget_add_css_class (top_mark, custom_name);
gtk_widget_add_css_class (bottom_mark, custom_name);
}
}
}
gst_object_unref (marker);
}

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "config.h"

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "config.h"

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
@@ -83,7 +84,7 @@ static void
clapper_gtk_toggle_play_button_init (ClapperGtkTogglePlayButton *self)
{
gtk_button_set_icon_name (GTK_BUTTON (self), PLAY_ICON_NAME);
gtk_actionable_set_action_name (GTK_ACTIONABLE (self), "av.toggle-play");
gtk_actionable_set_action_name (GTK_ACTIONABLE (self), "video.toggle-play");
}
static void

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "config.h"
@@ -21,7 +22,7 @@
#include <glib/gi18n-lib.h>
#include "clapper-gtk-utils-private.h"
#include "clapper-gtk-av.h"
#include "clapper-gtk-video.h"
static gboolean initialized = FALSE;
@@ -29,18 +30,18 @@ static gboolean initialized = FALSE;
* clapper_gtk_get_player_from_ancestor:
* @widget: a #GtkWidget
*
* Get [class@Clapper.Player] used by [class@ClapperGtk.Av] ancestor of @widget.
* Get [class@Clapper.Player] used by [class@ClapperGtk.Video] ancestor of @widget.
*
* This utility is a convenience wrapper for calling [method@Gtk.Widget.get_ancestor]
* of type `CLAPPER_GTK_TYPE_AV` and [method@ClapperGtk.Av.get_player] with
* of type `CLAPPER_GTK_TYPE_VIDEO` and [method@ClapperGtk.Video.get_player] with
* additional %NULL checking and type casting.
*
* This is meant to be used mainly for custom widget development as an easy access to the
* underlying parent [class@Clapper.Player] object. If you want to get the player from
* [class@ClapperGtk.Av] widget itself, use [method@ClapperGtk.Av.get_player] instead.
* [class@ClapperGtk.Video] widget itself, use [method@ClapperGtk.Video.get_player] instead.
*
* Rememeber that this function will return %NULL when widget does not have
* a [class@ClapperGtk.Av] ancestor in widget hierarchy (widget is not yet placed).
* a [class@ClapperGtk.Video] ancestor in widget hierarchy (widget is not yet placed).
*
* Returns: (transfer none) (nullable): a #ClapperPlayer from ancestor of a @widget.
*/
@@ -52,8 +53,8 @@ clapper_gtk_get_player_from_ancestor (GtkWidget *widget)
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
if ((parent = gtk_widget_get_ancestor (widget, CLAPPER_GTK_TYPE_AV)))
player = clapper_gtk_av_get_player (CLAPPER_GTK_AV_CAST (parent));
if ((parent = gtk_widget_get_ancestor (widget, CLAPPER_GTK_TYPE_VIDEO)))
player = clapper_gtk_video_get_player (CLAPPER_GTK_VIDEO_CAST (parent));
return player;
}

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -1,95 +0,0 @@
/* Clapper GTK Integration Library
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#include "clapper-gtk-version.h"
/**
* clapper_gtk_get_major_version:
*
* ClapperGtk runtime major version component
*
* This returns the ClapperGtk library version your code is
* running against unlike [const@ClapperGtk.MAJOR_VERSION]
* which represents compile time version.
*
* Returns: the major version number of the ClapperGtk library
*
* Since: 0.10
*/
guint
clapper_gtk_get_major_version (void)
{
return CLAPPER_GTK_MAJOR_VERSION;
}
/**
* clapper_gtk_get_minor_version:
*
* ClapperGtk runtime minor version component
*
* This returns the ClapperGtk library version your code is
* running against unlike [const@ClapperGtk.MINOR_VERSION]
* which represents compile time version.
*
* Returns: the minor version number of the ClapperGtk library
*
* Since: 0.10
*/
guint
clapper_gtk_get_minor_version (void)
{
return CLAPPER_GTK_MINOR_VERSION;
}
/**
* clapper_gtk_get_micro_version:
*
* ClapperGtk runtime micro version component
*
* This returns the ClapperGtk library version your code is
* running against unlike [const@ClapperGtk.MICRO_VERSION]
* which represents compile time version.
*
* Returns: the micro version number of the ClapperGtk library
*
* Since: 0.10
*/
guint
clapper_gtk_get_micro_version (void)
{
return CLAPPER_GTK_MICRO_VERSION;
}
/**
* clapper_gtk_get_version_s:
*
* ClapperGtk runtime version as string
*
* This returns the ClapperGtk library version your code is
* running against unlike [const@ClapperGtk.VERSION_S]
* which represents compile time version.
*
* Returns: the version of the ClapperGtk library as string
*
* Since: 0.10
*/
const gchar *
clapper_gtk_get_version_s (void)
{
return CLAPPER_GTK_VERSION_S;
}

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once
@@ -22,8 +23,6 @@
#error "Only <clapper-gtk/clapper-gtk.h> can be included directly."
#endif
#include <glib.h>
/**
* CLAPPER_GTK_MAJOR_VERSION:
*
@@ -75,15 +74,3 @@
(CLAPPER_GTK_MAJOR_VERSION == (major) && CLAPPER_GTK_MINOR_VERSION > (minor)) || \
(CLAPPER_GTK_MAJOR_VERSION == (major) && CLAPPER_GTK_MINOR_VERSION == (minor) && \
CLAPPER_GTK_MICRO_VERSION >= (micro)))
G_BEGIN_DECLS
guint clapper_gtk_get_major_version (void);
guint clapper_gtk_get_minor_version (void);
guint clapper_gtk_get_micro_version (void);
const gchar * clapper_gtk_get_version_s (void);
G_END_DECLS

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "config.h"

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
@@ -22,8 +23,8 @@
* A ready to be used GTK video widget implementing Clapper API.
*
* #ClapperGtkVideo is the main widget exposed by `ClapperGtk` API. It both displays
* videos played by [class@Clapper.Player] (exposed as [property@ClapperGtk.Av:player] property)
* and manages revealing and fading of any additional widgets overlaid on top of it.
* videos played by [class@Clapper.Player] (exposed as its property) and manages
* revealing and fading of any additional widgets overlaid on top of it.
*
* Other widgets provided by `ClapperGtk` library, once placed anywhere on video
* (including nesting within another widget like [class@Gtk.Box]) will automatically
@@ -34,7 +35,7 @@
* # Basic usage
*
* A typical use case is to embed video widget as part of your app where video playback
* is needed. Get the [class@Clapper.Player] belonging to the AV widget and start adding
* is needed. Get the [class@Clapper.Player] belonging to the video widget and start adding
* new [class@Clapper.MediaItem] items to the [class@Clapper.Queue] for playback.
* For more information please refer to the Clapper playback library documentation.
*
@@ -46,8 +47,27 @@
*
* # Actions
*
* You can use built-in actions of parent [class@ClapperGtk.Av].
* See its documentation, for the list of available ones.
* #ClapperGtkVideo defines a set of built-in actions:
*
* ```yaml
* - "video.toggle-play": toggle play/pause
* - "video.play": start/resume playback
* - "video.pause": pause playback
* - "video.stop": stop playback
* - "video.seek": seek to position (variant "d")
* - "video.seek-custom": seek to position using seek method (variant "(di)")
* - "video.toggle-mute": toggle mute state
* - "video.set-mute": set mute state (variant "b")
* - "video.volume-up": increase volume by 2%
* - "video.volume-down": decrease volume by 2%
* - "video.set-volume": set volume to specified value (variant "d")
* - "video.speed-up": increase speed (from 0.05x - 2x range to nearest quarter)
* - "video.speed-down": decrease speed (from 0.05x - 2x range to nearest quarter)
* - "video.set-speed": set speed to specified value (variant "d")
* - "video.previous-item": select previous item in queue
* - "video.next-item": select next item in queue
* - "video.select-item": select item at specified index in queue (variant "u")
* ```
*
* # ClapperGtkVideo as GtkBuildable
*
@@ -74,6 +94,8 @@
#include "config.h"
#include <math.h>
#include "clapper-gtk-enums.h"
#include "clapper-gtk-video.h"
#include "clapper-gtk-lead-container.h"
@@ -81,8 +103,11 @@
#include "clapper-gtk-buffering-animation-private.h"
#include "clapper-gtk-video-placeholder-private.h"
#define PERCENTAGE_ROUND(a) (round ((gdouble) a / 0.01) * 0.01)
#define DEFAULT_FADE_DELAY 3000
#define DEFAULT_TOUCH_FADE_DELAY 5000
#define DEFAULT_AUTO_INHIBIT FALSE
#define MIN_MOTION_DELAY 100000
@@ -91,7 +116,7 @@ GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperGtkVideo
{
ClapperGtkAv parent;
GtkWidget parent;
GtkWidget *overlay;
GtkWidget *status;
@@ -101,8 +126,10 @@ struct _ClapperGtkVideo
GtkGesture *click_gesture;
/* Props */
ClapperPlayer *player;
guint fade_delay;
guint touch_fade_delay;
gboolean auto_inhibit;
GPtrArray *overlays;
GPtrArray *fading_overlays;
@@ -114,6 +141,8 @@ struct _ClapperGtkVideo
guint fade_timeout;
gboolean reveal, revealed;
guint inhibit_cookie;
/* Current pointer coords and type */
gdouble x, y;
gboolean is_touch;
@@ -146,14 +175,17 @@ _buildable_iface_init (GtkBuildableIface *iface)
}
#define parent_class clapper_gtk_video_parent_class
G_DEFINE_TYPE_WITH_CODE (ClapperGtkVideo, clapper_gtk_video, CLAPPER_GTK_TYPE_AV,
G_DEFINE_TYPE_WITH_CODE (ClapperGtkVideo, clapper_gtk_video, GTK_TYPE_WIDGET,
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, _buildable_iface_init))
enum
{
PROP_0,
PROP_PLAYER,
PROP_FADE_DELAY,
PROP_TOUCH_FADE_DELAY,
PROP_AUTO_INHIBIT,
PROP_INHIBITED,
PROP_LAST
};
@@ -164,111 +196,209 @@ enum
SIGNAL_LAST
};
static gboolean provider_added = FALSE;
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
static guint signals[SIGNAL_LAST] = { 0, };
/* FIXME: 1.0: Remove these compat actions, since they were moved to base class */
static void
toggle_play_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
gtk_widget_activate_action_variant (widget, "av.toggle-play", parameter);
ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget);
ClapperPlayer *player = clapper_gtk_video_get_player (self);
switch (clapper_player_get_state (player)) {
case CLAPPER_PLAYER_STATE_PLAYING:
clapper_player_pause (player);
break;
case CLAPPER_PLAYER_STATE_STOPPED:
case CLAPPER_PLAYER_STATE_PAUSED:
clapper_player_play (player);
break;
default:
break;
}
}
static void
play_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
gtk_widget_activate_action_variant (widget, "av.play", parameter);
ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget);
ClapperPlayer *player = clapper_gtk_video_get_player (self);
clapper_player_play (player);
}
static void
pause_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
gtk_widget_activate_action_variant (widget, "av.pause", parameter);
ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget);
ClapperPlayer *player = clapper_gtk_video_get_player (self);
clapper_player_pause (player);
}
static void
stop_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
gtk_widget_activate_action_variant (widget, "av.stop", parameter);
ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget);
ClapperPlayer *player = clapper_gtk_video_get_player (self);
clapper_player_stop (player);
}
static void
seek_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
gtk_widget_activate_action_variant (widget, "av.seek", parameter);
ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget);
ClapperPlayer *player = clapper_gtk_video_get_player (self);
gdouble position = g_variant_get_double (parameter);
clapper_player_seek (player, position);
}
static void
seek_custom_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
gtk_widget_activate_action_variant (widget, "av.seek-custom", parameter);
ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget);
ClapperPlayer *player = clapper_gtk_video_get_player (self);
ClapperPlayerSeekMethod method = CLAPPER_PLAYER_SEEK_METHOD_NORMAL;
gdouble position = 0;
g_variant_get (parameter, "(di)", &position, &method);
clapper_player_seek_custom (player, position, method);
}
static void
toggle_mute_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
gtk_widget_activate_action_variant (widget, "av.toggle-mute", parameter);
ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget);
ClapperPlayer *player = clapper_gtk_video_get_player (self);
clapper_player_set_mute (player, !clapper_player_get_mute (player));
}
static void
set_mute_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
gtk_widget_activate_action_variant (widget, "av.set-mute", parameter);
ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget);
ClapperPlayer *player = clapper_gtk_video_get_player (self);
gboolean mute = g_variant_get_boolean (parameter);
clapper_player_set_mute (player, mute);
}
static void
volume_up_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
gtk_widget_activate_action_variant (widget, "av.volume-up", parameter);
ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget);
ClapperPlayer *player = clapper_gtk_video_get_player (self);
gdouble volume = (clapper_player_get_volume (player) + 0.02);
if (volume > 2.0)
volume = 2.0;
clapper_player_set_volume (player, PERCENTAGE_ROUND (volume));
}
static void
volume_down_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
gtk_widget_activate_action_variant (widget, "av.volume-down", parameter);
ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget);
ClapperPlayer *player = clapper_gtk_video_get_player (self);
gdouble volume = (clapper_player_get_volume (player) - 0.02);
if (volume < 0)
volume = 0;
clapper_player_set_volume (player, PERCENTAGE_ROUND (volume));
}
static void
set_volume_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
gtk_widget_activate_action_variant (widget, "av.set-volume", parameter);
ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget);
ClapperPlayer *player = clapper_gtk_video_get_player (self);
gdouble volume = g_variant_get_double (parameter);
clapper_player_set_volume (player, volume);
}
static void
speed_up_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
gtk_widget_activate_action_variant (widget, "av.speed-up", parameter);
ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget);
ClapperPlayer *player = clapper_gtk_video_get_player (self);
gdouble dest, speed = clapper_player_get_speed (player);
if (speed >= 2.0)
return;
dest = 0.25;
while (speed >= dest)
dest += 0.25;
if (dest > 2.0)
dest = 2.0;
clapper_player_set_speed (player, dest);
}
static void
speed_down_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
gtk_widget_activate_action_variant (widget, "av.speed-down", parameter);
ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget);
ClapperPlayer *player = clapper_gtk_video_get_player (self);
gdouble dest, speed = clapper_player_get_speed (player);
if (speed <= 0.05)
return;
dest = 2.0;
while (speed <= dest)
dest -= 0.25;
if (dest < 0.05)
dest = 0.05;
clapper_player_set_speed (player, dest);
}
static void
set_speed_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
gtk_widget_activate_action_variant (widget, "av.set-speed", parameter);
ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget);
ClapperPlayer *player = clapper_gtk_video_get_player (self);
gdouble speed = g_variant_get_double (parameter);
clapper_player_set_speed (player, speed);
}
static void
previous_item_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
gtk_widget_activate_action_variant (widget, "av.previous-item", parameter);
ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget);
ClapperPlayer *player = clapper_gtk_video_get_player (self);
clapper_queue_select_previous_item (clapper_player_get_queue (player));
}
static void
next_item_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
gtk_widget_activate_action_variant (widget, "av.next-item", parameter);
ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget);
ClapperPlayer *player = clapper_gtk_video_get_player (self);
clapper_queue_select_next_item (clapper_player_get_queue (player));
}
static void
select_item_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter)
{
gtk_widget_activate_action_variant (widget, "av.select-item", parameter);
ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget);
ClapperPlayer *player = clapper_gtk_video_get_player (self);
guint index = g_variant_get_uint32 (parameter);
clapper_queue_select_index (clapper_player_get_queue (player), index);
}
static void
@@ -742,6 +872,73 @@ touch_released_cb (GtkGestureClick *click, gint n_press,
_reset_fade_timeout (self);
}
static void
_ensure_css_provider (void)
{
GdkDisplay *display;
if (provider_added)
return;
display = gdk_display_get_default ();
if (G_LIKELY (display != NULL)) {
GtkCssProvider *provider = gtk_css_provider_new ();
gtk_css_provider_load_from_resource (provider,
CLAPPER_GTK_RESOURCE_PREFIX "/css/styles.css");
gtk_style_context_add_provider_for_display (display,
(GtkStyleProvider *) provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION - 1);
g_object_unref (provider);
provider_added = TRUE;
}
}
static inline void
_set_inhibit_session (ClapperGtkVideo *self, gboolean inhibit)
{
GtkRoot *root;
GApplication *app;
gboolean inhibited = (self->inhibit_cookie != 0);
if (inhibited == inhibit)
return;
GST_DEBUG_OBJECT (self, "Trying to %sinhibit session...", (inhibit) ? "" : "un");
root = gtk_widget_get_root (GTK_WIDGET (self));
if (!root && !GTK_IS_WINDOW (root)) {
GST_WARNING_OBJECT (self, "Cannot %sinhibit session "
"without root window", (inhibit) ? "" : "un");
return;
}
/* NOTE: Not using application from window prop,
* as it goes away early when unrooting */
app = g_application_get_default ();
if (!app && !GTK_IS_APPLICATION (app)) {
GST_WARNING_OBJECT (self, "Cannot %sinhibit session "
"without window application set", (inhibit) ? "" : "un");
return;
}
if (inhibited) {
gtk_application_uninhibit (GTK_APPLICATION (app), self->inhibit_cookie);
self->inhibit_cookie = 0;
}
if (inhibit) {
self->inhibit_cookie = gtk_application_inhibit (GTK_APPLICATION (app),
GTK_WINDOW (root), GTK_APPLICATION_INHIBIT_IDLE,
"Video is playing");
}
GST_DEBUG_OBJECT (self, "Session %sinhibited", (inhibit) ? "" : "un");
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_INHIBITED]);
}
static inline void
_set_buffering_animation_enabled (ClapperGtkVideo *self, gboolean enabled)
{
@@ -767,6 +964,9 @@ _player_state_changed_cb (ClapperPlayer *player,
{
ClapperPlayerState state = clapper_player_get_state (player);
if (self->auto_inhibit)
_set_inhibit_session (self, state == CLAPPER_PLAYER_STATE_PLAYING);
_set_buffering_animation_enabled (self, state == CLAPPER_PLAYER_STATE_BUFFERING);
}
@@ -907,7 +1107,7 @@ _fading_overlay_revealed_cb (GtkRevealer *revealer,
*
* Creates a new #ClapperGtkVideo instance.
*
* Newly created video widget will also have set some default GStreamer elements
* Newly created video widget will also set some default GStreamer elements
* on its [class@Clapper.Player]. This includes Clapper own video sink and
* a "scaletempo" element as audio filter. Both can still be changed after
* construction by setting corresponding player properties.
@@ -988,21 +1188,19 @@ clapper_gtk_video_add_fading_overlay (ClapperGtkVideo *self, GtkWidget *widget)
}
/**
* clapper_gtk_video_get_player: (skip)
* clapper_gtk_video_get_player:
* @video: a #ClapperGtkVideo
*
* Get #ClapperPlayer used by this #ClapperGtkVideo instance.
*
* Returns: (transfer none): a #ClapperPlayer used by video.
*
* Deprecated: 0.10: Use [method@ClapperGtk.Av.get_player] instead.
*/
ClapperPlayer *
clapper_gtk_video_get_player (ClapperGtkVideo *self)
{
g_return_val_if_fail (CLAPPER_GTK_IS_VIDEO (self), NULL);
return clapper_gtk_av_get_player (CLAPPER_GTK_AV_CAST (self));
return self->player;
}
/**
@@ -1078,58 +1276,60 @@ clapper_gtk_video_get_touch_fade_delay (ClapperGtkVideo *self)
}
/**
* clapper_gtk_video_set_auto_inhibit: (skip)
* clapper_gtk_video_set_auto_inhibit:
* @video: a #ClapperGtkVideo
* @inhibit: whether to enable automatic session inhibit
*
* Set whether video should try to automatically inhibit session
* from idling (and possibly screen going black) when video is playing.
*
* Deprecated: 0.10: Use [method@ClapperGtk.Av.set_auto_inhibit] instead.
*/
void
clapper_gtk_video_set_auto_inhibit (ClapperGtkVideo *self, gboolean inhibit)
{
g_return_if_fail (CLAPPER_GTK_IS_VIDEO (self));
clapper_gtk_av_set_auto_inhibit (CLAPPER_GTK_AV_CAST (self), inhibit);
if (self->auto_inhibit != inhibit) {
self->auto_inhibit = inhibit;
/* Uninhibit if we were auto inhibited earlier */
if (!self->auto_inhibit)
_set_inhibit_session (self, FALSE);
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_AUTO_INHIBIT]);
}
}
/**
* clapper_gtk_video_get_auto_inhibit: (skip)
* clapper_gtk_video_get_auto_inhibit:
* @video: a #ClapperGtkVideo
*
* Get whether automatic session inhibit is enabled.
*
* Returns: %TRUE if enabled, %FALSE otherwise.
*
* Deprecated: 0.10: Use [method@ClapperGtk.Av.get_auto_inhibit] instead.
*/
gboolean
clapper_gtk_video_get_auto_inhibit (ClapperGtkVideo *self)
{
g_return_val_if_fail (CLAPPER_GTK_IS_VIDEO (self), FALSE);
return clapper_gtk_av_get_auto_inhibit (CLAPPER_GTK_AV_CAST (self));
return self->auto_inhibit;
}
/**
* clapper_gtk_video_get_inhibited: (skip)
* clapper_gtk_video_get_inhibited:
* @video: a #ClapperGtkVideo
*
* Get whether session is currently inhibited by
* [property@ClapperGtk.Av:auto-inhibit].
* [property@ClapperGtk.Video:auto-inhibit].
*
* Returns: %TRUE if inhibited, %FALSE otherwise.
*
* Deprecated: 0.10: Use [method@ClapperGtk.Av.get_inhibited] instead.
*/
gboolean
clapper_gtk_video_get_inhibited (ClapperGtkVideo *self)
{
g_return_val_if_fail (CLAPPER_GTK_IS_VIDEO (self), FALSE);
return clapper_gtk_av_get_inhibited (CLAPPER_GTK_AV_CAST (self));
return (self->inhibit_cookie != 0);
}
static void
@@ -1138,6 +1338,8 @@ clapper_gtk_video_root (GtkWidget *widget)
ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget);
GtkRoot *root;
_ensure_css_provider ();
GTK_WIDGET_CLASS (parent_class)->root (widget);
root = gtk_widget_get_root (widget);
@@ -1149,6 +1351,11 @@ clapper_gtk_video_root (GtkWidget *widget)
G_CALLBACK (_window_is_active_cb), self);
_window_is_active_cb (window, NULL, self);
}
if (self->auto_inhibit) {
ClapperPlayerState state = clapper_player_get_state (self->player);
_set_inhibit_session (self, state == CLAPPER_PLAYER_STATE_PLAYING);
}
}
static void
@@ -1162,6 +1369,8 @@ clapper_gtk_video_unroot (GtkWidget *widget)
_window_is_active_cb, self);
}
_set_inhibit_session (self, FALSE);
GTK_WIDGET_CLASS (parent_class)->unroot (widget);
}
@@ -1177,6 +1386,7 @@ clapper_gtk_video_init (ClapperGtkVideo *self)
self->fade_delay = DEFAULT_FADE_DELAY;
self->touch_fade_delay = DEFAULT_TOUCH_FADE_DELAY;
self->auto_inhibit = DEFAULT_AUTO_INHIBIT;
/* Ensure private types */
g_type_ensure (CLAPPER_GTK_TYPE_STATUS);
@@ -1191,18 +1401,15 @@ static void
clapper_gtk_video_constructed (GObject *object)
{
ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (object);
GstElement *vsink;
ClapperPlayer *player;
GstElement *afilter, *vsink;
ClapperQueue *queue;
G_OBJECT_CLASS (parent_class)->constructed (object);
self->player = clapper_player_new ();
queue = clapper_player_get_queue (self->player);
player = clapper_gtk_av_get_player (CLAPPER_GTK_AV_CAST (self));
queue = clapper_player_get_queue (player);
g_signal_connect (player, "notify::state",
g_signal_connect (self->player, "notify::state",
G_CALLBACK (_player_state_changed_cb), self);
g_signal_connect (player, "notify::video-sink",
g_signal_connect (self->player, "notify::video-sink",
G_CALLBACK (_video_sink_changed_cb), self);
vsink = gst_element_factory_make ("clappersink", NULL);
@@ -1222,23 +1429,28 @@ clapper_gtk_video_constructed (GObject *object)
}
}
clapper_player_set_video_sink (player, vsink);
clapper_player_set_video_sink (self->player, vsink);
}
g_signal_connect (player, "error",
afilter = gst_element_factory_make ("scaletempo", NULL);
if (G_LIKELY (afilter != NULL))
clapper_player_set_audio_filter (self->player, afilter);
g_signal_connect (self->player, "error",
G_CALLBACK (_player_error_cb), self);
g_signal_connect (player, "missing-plugin",
g_signal_connect (self->player, "missing-plugin",
G_CALLBACK (_player_missing_plugin_cb), self);
g_signal_connect (queue, "notify::current-item",
G_CALLBACK (_queue_current_item_changed_cb), self);
G_OBJECT_CLASS (parent_class)->constructed (object);
}
static void
clapper_gtk_video_dispose (GObject *object)
{
ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (object);
ClapperPlayer *player;
if (self->notify_revealed_id != 0) {
GtkRevealer *revealer = GTK_REVEALER (g_ptr_array_index (self->fading_overlays, 0));
@@ -1249,20 +1461,18 @@ clapper_gtk_video_dispose (GObject *object)
g_clear_handle_id (&self->fade_timeout, g_source_remove);
player = clapper_gtk_av_get_player (CLAPPER_GTK_AV_CAST (self));
/* Something else might still be holding a reference on the player,
* thus we should disconnect everything before disposing template */
if (player) { // NULL if dispose run multiple times
ClapperQueue *queue = clapper_player_get_queue (player);
if (self->player) {
ClapperQueue *queue = clapper_player_get_queue (self->player);
g_signal_handlers_disconnect_by_func (player,
g_signal_handlers_disconnect_by_func (self->player,
_player_state_changed_cb, self);
g_signal_handlers_disconnect_by_func (player,
g_signal_handlers_disconnect_by_func (self->player,
_video_sink_changed_cb, self);
g_signal_handlers_disconnect_by_func (player,
g_signal_handlers_disconnect_by_func (self->player,
_player_error_cb, self);
g_signal_handlers_disconnect_by_func (player,
g_signal_handlers_disconnect_by_func (self->player,
_player_missing_plugin_cb, self);
g_signal_handlers_disconnect_by_func (queue,
@@ -1272,6 +1482,7 @@ clapper_gtk_video_dispose (GObject *object)
gtk_widget_dispose_template (GTK_WIDGET (object), CLAPPER_GTK_TYPE_VIDEO);
g_clear_pointer (&self->overlay, gtk_widget_unparent);
gst_clear_object (&self->player);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
@@ -1294,12 +1505,21 @@ clapper_gtk_video_get_property (GObject *object, guint prop_id,
ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (object);
switch (prop_id) {
case PROP_PLAYER:
g_value_set_object (value, clapper_gtk_video_get_player (self));
break;
case PROP_FADE_DELAY:
g_value_set_uint (value, clapper_gtk_video_get_fade_delay (self));
break;
case PROP_TOUCH_FADE_DELAY:
g_value_set_uint (value, clapper_gtk_video_get_touch_fade_delay (self));
break;
case PROP_AUTO_INHIBIT:
g_value_set_boolean (value, clapper_gtk_video_get_auto_inhibit (self));
break;
case PROP_INHIBITED:
g_value_set_boolean (value, clapper_gtk_video_get_inhibited (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -1319,6 +1539,9 @@ clapper_gtk_video_set_property (GObject *object, guint prop_id,
case PROP_TOUCH_FADE_DELAY:
clapper_gtk_video_set_touch_fade_delay (self, g_value_get_uint (value));
break;
case PROP_AUTO_INHIBIT:
clapper_gtk_video_set_auto_inhibit (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -1343,6 +1566,15 @@ clapper_gtk_video_class_init (ClapperGtkVideoClass *klass)
gobject_class->dispose = clapper_gtk_video_dispose;
gobject_class->finalize = clapper_gtk_video_finalize;
/**
* ClapperGtkVideo:player:
*
* A #ClapperPlayer used by video.
*/
param_specs[PROP_PLAYER] = g_param_spec_object ("player",
NULL, NULL, CLAPPER_TYPE_PLAYER,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkVideo:fade-delay:
*
@@ -1362,6 +1594,24 @@ clapper_gtk_video_class_init (ClapperGtkVideoClass *klass)
NULL, NULL, 1, G_MAXUINT, DEFAULT_TOUCH_FADE_DELAY,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkVideo:auto-inhibit:
*
* Try to automatically inhibit session when video is playing.
*/
param_specs[PROP_AUTO_INHIBIT] = g_param_spec_boolean ("auto-inhibit",
NULL, NULL, DEFAULT_AUTO_INHIBIT,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkVideo:inhibited:
*
* Get whether session is currently inhibited by the video.
*/
param_specs[PROP_INHIBITED] = g_param_spec_boolean ("inhibited",
NULL, NULL, FALSE,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkVideo::toggle-fullscreen:
* @video: a #ClapperGtkVideo
@@ -1393,8 +1643,6 @@ clapper_gtk_video_class_init (ClapperGtkVideoClass *klass)
g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
/* FIXME: 1.0: Remove these actions, since they were moved to
* base class AV widget, but are here for compat reasons. */
gtk_widget_class_install_action (widget_class, "video.toggle-play", NULL, toggle_play_action_cb);
gtk_widget_class_install_action (widget_class, "video.play", NULL, play_action_cb);
gtk_widget_class_install_action (widget_class, "video.pause", NULL, pause_action_cb);

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once
@@ -27,7 +28,6 @@
#include <gtk/gtk.h>
#include <clapper/clapper.h>
#include <clapper-gtk/clapper-gtk-av.h>
#include <clapper-gtk/clapper-gtk-visibility.h>
G_BEGIN_DECLS
@@ -36,7 +36,7 @@ G_BEGIN_DECLS
#define CLAPPER_GTK_VIDEO_CAST(obj) ((ClapperGtkVideo *)(obj))
CLAPPER_GTK_API
G_DECLARE_FINAL_TYPE (ClapperGtkVideo, clapper_gtk_video, CLAPPER_GTK, VIDEO, ClapperGtkAv)
G_DECLARE_FINAL_TYPE (ClapperGtkVideo, clapper_gtk_video, CLAPPER_GTK, VIDEO, GtkWidget)
CLAPPER_GTK_API
GtkWidget * clapper_gtk_video_new (void);
@@ -47,7 +47,7 @@ void clapper_gtk_video_add_overlay (ClapperGtkVideo *video, GtkWidget *widget);
CLAPPER_GTK_API
void clapper_gtk_video_add_fading_overlay (ClapperGtkVideo *video, GtkWidget *widget);
CLAPPER_GTK_DEPRECATED_FOR(clapper_gtk_av_get_player)
CLAPPER_GTK_API
ClapperPlayer * clapper_gtk_video_get_player (ClapperGtkVideo *video);
CLAPPER_GTK_API
@@ -62,13 +62,13 @@ void clapper_gtk_video_set_touch_fade_delay (ClapperGtkVideo *video, guint delay
CLAPPER_GTK_API
guint clapper_gtk_video_get_touch_fade_delay (ClapperGtkVideo *video);
CLAPPER_GTK_DEPRECATED_FOR(clapper_gtk_av_set_auto_inhibit)
CLAPPER_GTK_API
void clapper_gtk_video_set_auto_inhibit (ClapperGtkVideo *video, gboolean inhibit);
CLAPPER_GTK_DEPRECATED_FOR(clapper_gtk_av_get_auto_inhibit)
CLAPPER_GTK_API
gboolean clapper_gtk_video_get_auto_inhibit (ClapperGtkVideo *video);
CLAPPER_GTK_DEPRECATED_FOR(clapper_gtk_av_get_inhibited)
CLAPPER_GTK_API
gboolean clapper_gtk_video_get_inhibited (ClapperGtkVideo *video);
G_END_DECLS

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once
@@ -23,8 +24,6 @@
#include <clapper-gtk/clapper-gtk-enums.h>
#include <clapper-gtk/clapper-gtk-version.h>
#include <clapper-gtk/clapper-gtk-audio.h>
#include <clapper-gtk/clapper-gtk-av.h>
#include <clapper-gtk/clapper-gtk-billboard.h>
#include <clapper-gtk/clapper-gtk-container.h>
#include <clapper-gtk/clapper-gtk-extra-menu-button.h>

View File

@@ -129,15 +129,6 @@ clapper-gtk-seek-bar label {
margin-left: 2px;
margin-right: 2px;
}
clapper-gtk-seek-bar scale marks .custom1 indicator {
color: tomato;
}
clapper-gtk-seek-bar scale marks .custom2 indicator {
color: goldenrod;
}
clapper-gtk-seek-bar scale marks .custom3 indicator {
color: limegreen;
}
clapper-gtk-extra-menu-button popover .spinsidebutton {
min-width: 28px;

View File

@@ -90,8 +90,6 @@ clappergtk_conf_inc = [
clappergtk_headers = [
'clapper-gtk.h',
'clapper-gtk-audio.h',
'clapper-gtk-av.h',
'clapper-gtk-enums.h',
'clapper-gtk-billboard.h',
'clapper-gtk-container.h',
@@ -111,8 +109,6 @@ clappergtk_headers = [
clappergtk_visibility_header,
]
clappergtk_sources = [
'clapper-gtk-audio.c',
'clapper-gtk-av.c',
'clapper-gtk-billboard.c',
'clapper-gtk-buffering-animation.c',
'clapper-gtk-buffering-paintable.c',
@@ -131,7 +127,6 @@ clappergtk_sources = [
'clapper-gtk-toggle-fullscreen-button.c',
'clapper-gtk-toggle-play-button.c',
'clapper-gtk-utils.c',
'clapper-gtk-version.c',
'clapper-gtk-video.c',
'clapper-gtk-video-placeholder.c',
clappergtk_resources,
@@ -181,6 +176,7 @@ if build_gir
clappergtk_enums,
],
extra_args: [
gir_init_section,
'--quiet',
'--warn-all',
'-DCLAPPER_GTK_COMPILATION',

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: clapper\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-01-07 19:03+0100\n"
"PO-Revision-Date: 2025-01-07 18:31\n"
"PO-Revision-Date: 2025-06-14 15:48\n"
"Last-Translator: \n"
"Language-Team: Arabic\n"
"Language: ar_SA\n"
@@ -20,7 +20,7 @@ msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Video"
msgstr ""
msgstr "الفيديو"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
@@ -30,50 +30,50 @@ msgstr "الصوت"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:230
msgid "Subtitles"
msgstr "التَّرْجَمَةً"
msgstr "الترجَمة"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Show Subtitles"
msgstr ""
msgstr "اعرض الترجَمة"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
msgstr "افتح…"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
msgstr "اكتم"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
msgstr "صفِّر"
#: src/lib/clapper-gtk/ui/clapper-gtk-video-placeholder.ui:25
msgid "External video output"
msgstr ""
msgstr "مخرج الفيديو الخارجي"
#: src/lib/clapper-gtk/ui/clapper-gtk-video-placeholder.ui:37
msgid "Used video sink cannot be embedded within application window"
msgstr ""
msgstr "لا يمكن تضمين بالوعة الفيديو المستخدمة في نافذة التطبيق"
#: src/lib/clapper-gtk/clapper-gtk-status.c:79
msgid "Unplayable Content"
msgstr ""
msgstr "محتوى غير قابل للتشغيل"
#. TRANSLATORS: Please do not try to translate "GStreamer" (it is a library name).
#: src/lib/clapper-gtk/clapper-gtk-status.c:89
#, c-format
msgid "Your GStreamer installation is missing a plugin: %s"
msgstr ""
msgstr "يفتقد تنصيبك لـGStreamer ملحقًا: %s"
#: src/lib/clapper-gtk/clapper-gtk-status.c:90
msgid "Missing Plugin"
msgstr ""
msgstr "ملحق مفقود"
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr "غير محدّد"
msgstr "غير محدَّد"
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
@@ -81,9 +81,9 @@ msgstr "قنوات"
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
msgstr "لا وسائط"
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""
msgstr "عنوان مجهول"

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: clapper\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-01-07 19:03+0100\n"
"PO-Revision-Date: 2025-01-07 18:32\n"
"PO-Revision-Date: 2025-05-08 07:56\n"
"Last-Translator: \n"
"Language-Team: Chinese Simplified\n"
"Language: zh_CN\n"
@@ -50,11 +50,11 @@ msgstr "重置"
#: src/lib/clapper-gtk/ui/clapper-gtk-video-placeholder.ui:25
msgid "External video output"
msgstr ""
msgstr "外部视频输出"
#: src/lib/clapper-gtk/ui/clapper-gtk-video-placeholder.ui:37
msgid "Used video sink cannot be embedded within application window"
msgstr ""
msgstr "已使用的视频接受器不能嵌入到应用程序窗口"
#: src/lib/clapper-gtk/clapper-gtk-status.c:79
msgid "Unplayable Content"

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface domain="clapper-gtk">
<template class="ClapperGtkVideo" parent="ClapperGtkAv">
<template class="ClapperGtkVideo" parent="GtkWidget">
<child type="overlay">
<object class="ClapperGtkStatus" id="status">
<property name="halign">center</property>

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once
@@ -44,16 +45,12 @@ void clapper_app_bus_post_refresh_streams (ClapperAppBus *app_bus, GstObject *sr
void clapper_app_bus_post_refresh_timeline (ClapperAppBus *app_bus, GstObject *src);
void clapper_app_bus_post_insert_playlist (ClapperAppBus *app_bus, GstObject *src, GstObject *playlist_item, GObject *playlist);
void clapper_app_bus_post_simple_signal (ClapperAppBus *app_bus, GstObject *src, guint signal_id);
void clapper_app_bus_post_object_desc_signal (ClapperAppBus *app_bus, GstObject *src, guint signal_id, GstObject *object, const gchar *desc);
void clapper_app_bus_post_desc_with_details_signal (ClapperAppBus *app_bus, GstObject *src, guint signal_id, const gchar *desc, const gchar *details);
void clapper_app_bus_post_message_signal (ClapperAppBus *app_bus, GstObject *src, guint signal_id, GstMessage *msg);
void clapper_app_bus_post_error_signal (ClapperAppBus *app_bus, GstObject *src, guint signal_id, GError *error, const gchar *debug_info);
G_END_DECLS

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <gio/gio.h>
@@ -21,7 +22,6 @@
#include "clapper-bus-private.h"
#include "clapper-app-bus-private.h"
#include "clapper-player-private.h"
#include "clapper-queue-private.h"
#include "clapper-media-item-private.h"
#include "clapper-timeline-private.h"
@@ -42,11 +42,9 @@ enum
CLAPPER_APP_BUS_STRUCTURE_PROP_NOTIFY,
CLAPPER_APP_BUS_STRUCTURE_REFRESH_STREAMS,
CLAPPER_APP_BUS_STRUCTURE_REFRESH_TIMELINE,
CLAPPER_APP_BUS_STRUCTURE_INSERT_PLAYLIST,
CLAPPER_APP_BUS_STRUCTURE_SIMPLE_SIGNAL,
CLAPPER_APP_BUS_STRUCTURE_OBJECT_DESC_SIGNAL,
CLAPPER_APP_BUS_STRUCTURE_DESC_WITH_DETAILS_SIGNAL,
CLAPPER_APP_BUS_STRUCTURE_MESSAGE_SIGNAL,
CLAPPER_APP_BUS_STRUCTURE_ERROR_SIGNAL
};
@@ -55,11 +53,9 @@ static ClapperBusQuark _structure_quarks[] = {
{"prop-notify", 0},
{"refresh-streams", 0},
{"refresh-timeline", 0},
{"insert-playlist", 0},
{"simple-signal", 0},
{"object-desc-signal", 0},
{"desc-with-details-signal", 0},
{"message", 0},
{"error-signal", 0},
{NULL, 0}
};
@@ -70,7 +66,6 @@ enum
CLAPPER_APP_BUS_FIELD_PSPEC,
CLAPPER_APP_BUS_FIELD_SIGNAL_ID,
CLAPPER_APP_BUS_FIELD_OBJECT,
CLAPPER_APP_BUS_FIELD_OTHER_OBJECT,
CLAPPER_APP_BUS_FIELD_DESC,
CLAPPER_APP_BUS_FIELD_DETAILS,
CLAPPER_APP_BUS_FIELD_ERROR,
@@ -82,7 +77,6 @@ static ClapperBusQuark _field_quarks[] = {
{"pspec", 0},
{"signal-id", 0},
{"object", 0},
{"other-object", 0},
{"desc", 0},
{"details", 0},
{"error", 0},
@@ -167,36 +161,6 @@ _handle_refresh_timeline_msg (GstMessage *msg, const GstStructure *structure)
clapper_timeline_refresh (timeline);
}
void
clapper_app_bus_post_insert_playlist (ClapperAppBus *self, GstObject *src,
GstObject *playlist_item, GObject *playlist)
{
GstStructure *structure = gst_structure_new_id (_STRUCTURE_QUARK (INSERT_PLAYLIST),
_FIELD_QUARK (OBJECT), GST_TYPE_OBJECT, playlist_item,
_FIELD_QUARK (OTHER_OBJECT), G_TYPE_OBJECT, playlist,
NULL);
gst_bus_post (GST_BUS_CAST (self), gst_message_new_application (src, structure));
}
static inline void
_handle_insert_playlist_msg (GstMessage *msg, const GstStructure *structure)
{
ClapperPlayer *player = CLAPPER_PLAYER_CAST (GST_MESSAGE_SRC (msg));
ClapperQueue *queue = clapper_player_get_queue (player);
GstObject *playlist_item;
GObject *playlist;
gst_structure_id_get (structure,
_FIELD_QUARK (OBJECT), GST_TYPE_OBJECT, &playlist_item,
_FIELD_QUARK (OTHER_OBJECT), G_TYPE_OBJECT, &playlist,
NULL);
clapper_queue_handle_playlist (queue,
CLAPPER_MEDIA_ITEM (playlist_item), G_LIST_STORE (playlist));
gst_object_unref (playlist_item);
g_object_unref (playlist);
}
void
clapper_app_bus_post_simple_signal (ClapperAppBus *self, GstObject *src, guint signal_id)
{
@@ -278,53 +242,6 @@ _handle_desc_with_details_signal_msg (GstMessage *msg, const GstStructure *struc
g_free (details);
}
void
clapper_app_bus_post_message_signal (ClapperAppBus *self,
GstObject *src, guint signal_id, GstMessage *msg)
{
/* Check for any "message" signal connection */
if (g_signal_handler_find (src, G_SIGNAL_MATCH_ID,
signal_id, 0, NULL, NULL, NULL) != 0) {
const GstStructure *structure = gst_message_get_structure (msg);
GQuark detail;
if (G_UNLIKELY (structure == NULL))
return;
detail = g_quark_from_string (gst_structure_get_name (structure));
/* If specific detail is connected or ALL "message" handler */
if (g_signal_handler_find (src, G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DETAIL,
signal_id, detail, NULL, NULL, NULL) != 0
|| g_signal_handler_find (src, G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DETAIL,
signal_id, 0, NULL, NULL, NULL) != 0) {
GstStructure *structure = gst_structure_new_id (_STRUCTURE_QUARK (MESSAGE_SIGNAL),
_FIELD_QUARK (SIGNAL_ID), G_TYPE_UINT, signal_id,
_FIELD_QUARK (DETAILS), G_TYPE_UINT, detail,
_FIELD_QUARK (OBJECT), GST_TYPE_MESSAGE, msg,
NULL);
gst_bus_post (GST_BUS_CAST (self), gst_message_new_application (src, structure));
}
}
}
static inline void
_handle_message_signal_msg (GstMessage *msg, const GstStructure *structure)
{
guint signal_id = 0;
GQuark detail = 0;
GstMessage *fwd_message = NULL;
gst_structure_id_get (structure,
_FIELD_QUARK (SIGNAL_ID), G_TYPE_UINT, &signal_id,
_FIELD_QUARK (DETAILS), G_TYPE_UINT, &detail,
_FIELD_QUARK (OBJECT), GST_TYPE_MESSAGE, &fwd_message,
NULL);
g_signal_emit (_MESSAGE_SRC_GOBJECT (msg), signal_id, detail, fwd_message);
gst_message_unref (fwd_message);
}
void
clapper_app_bus_post_error_signal (ClapperAppBus *self,
GstObject *src, guint signal_id,
@@ -369,14 +286,10 @@ clapper_app_bus_message_func (GstBus *bus, GstMessage *msg, gpointer user_data G
_handle_refresh_streams_msg (msg, structure);
else if (quark == _STRUCTURE_QUARK (REFRESH_TIMELINE))
_handle_refresh_timeline_msg (msg, structure);
else if (quark == _STRUCTURE_QUARK (INSERT_PLAYLIST))
_handle_insert_playlist_msg (msg, structure);
else if (quark == _STRUCTURE_QUARK (SIMPLE_SIGNAL))
_handle_simple_signal_msg (msg, structure);
else if (quark == _STRUCTURE_QUARK (OBJECT_DESC_SIGNAL))
_handle_object_desc_signal_msg (msg, structure);
else if (quark == _STRUCTURE_QUARK (MESSAGE_SIGNAL))
_handle_message_signal_msg (msg, structure);
else if (quark == _STRUCTURE_QUARK (ERROR_SIGNAL))
_handle_error_signal_msg (msg, structure);
else if (quark == _STRUCTURE_QUARK (DESC_WITH_DETAILS_SIGNAL))

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -1,45 +0,0 @@
/* Clapper Playback Library
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once
#if !defined(__CLAPPER_INSIDE__) && !defined(CLAPPER_COMPILATION)
#error "Only <clapper/clapper.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <clapper/clapper-visibility.h>
#include <clapper/clapper-enhancer-proxy-list.h>
G_BEGIN_DECLS
CLAPPER_API
void clapper_init (int *argc, char **argv[]);
CLAPPER_API
gboolean clapper_init_check (int *argc, char **argv[]);
CLAPPER_DEPRECATED
gboolean clapper_enhancer_check (GType iface_type, const gchar *scheme, const gchar *host, const gchar **name);
CLAPPER_API
ClapperEnhancerProxyList * clapper_get_global_enhancer_proxies (void);
G_END_DECLS

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -1,107 +0,0 @@
/* Clapper Playback Library
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <glib.h>
#include <glib-object.h>
G_BEGIN_DECLS
G_GNUC_INTERNAL
void clapper_cache_initialize (void);
G_GNUC_INTERNAL
gboolean clapper_cache_is_disabled (void);
G_GNUC_INTERNAL
GMappedFile * clapper_cache_open (const gchar *filename, const gchar **data, GError **error);
G_GNUC_INTERNAL
gboolean clapper_cache_read_boolean (const gchar **data);
G_GNUC_INTERNAL
gint clapper_cache_read_int (const gchar **data);
G_GNUC_INTERNAL
guint clapper_cache_read_uint (const gchar **data);
G_GNUC_INTERNAL
gint64 clapper_cache_read_int64 (const gchar **data);
G_GNUC_INTERNAL
gdouble clapper_cache_read_double (const gchar **data);
G_GNUC_INTERNAL
const gchar * clapper_cache_read_string (const gchar **data);
G_GNUC_INTERNAL
const guint8 * clapper_cache_read_data (const gchar **data, gsize *size);
G_GNUC_INTERNAL
GType clapper_cache_read_enum (const gchar **data);
G_GNUC_INTERNAL
GType clapper_cache_read_flags (const gchar **data);
G_GNUC_INTERNAL
GType clapper_cache_read_iface (const gchar **data);
G_GNUC_INTERNAL
GParamSpec * clapper_cache_read_pspec (const gchar **data);
G_GNUC_INTERNAL
GByteArray * clapper_cache_create (void);
G_GNUC_INTERNAL
void clapper_cache_store_boolean (GByteArray *bytes, gboolean val);
G_GNUC_INTERNAL
void clapper_cache_store_int (GByteArray *bytes, gint val);
G_GNUC_INTERNAL
void clapper_cache_store_uint (GByteArray *bytes, guint val);
G_GNUC_INTERNAL
void clapper_cache_store_int64 (GByteArray *bytes, gint64 val);
G_GNUC_INTERNAL
void clapper_cache_store_double (GByteArray *bytes, gdouble val);
G_GNUC_INTERNAL
void clapper_cache_store_string (GByteArray *bytes, const gchar *val);
G_GNUC_INTERNAL
void clapper_cache_store_data (GByteArray *bytes, const guint8 *val, gsize val_size);
G_GNUC_INTERNAL
void clapper_cache_store_enum (GByteArray *bytes, GType enum_type);
G_GNUC_INTERNAL
void clapper_cache_store_flags (GByteArray *bytes, GType flags_type);
G_GNUC_INTERNAL
gboolean clapper_cache_store_iface (GByteArray *bytes, GType iface);
G_GNUC_INTERNAL
gboolean clapper_cache_store_pspec (GByteArray *bytes, GParamSpec *pspec);
G_GNUC_INTERNAL
gboolean clapper_cache_write (const gchar *filename, GByteArray *bytes, GError **error);
G_END_DECLS

View File

@@ -1,548 +0,0 @@
/* Clapper Playback Library
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#include "clapper-cache-private.h"
#include "clapper-version.h"
#include "clapper-extractable.h"
#include "clapper-playlistable.h"
#include "clapper-reactable.h"
#define CLAPPER_CACHE_HEADER "CLAPPER"
typedef enum
{
CLAPPER_CACHE_IFACE_EXTRACTABLE = 1,
CLAPPER_CACHE_IFACE_PLAYLISTABLE,
CLAPPER_CACHE_IFACE_REACTABLE,
} ClapperCacheIfaces;
static GArray *enum_registry = NULL;
static GArray *flags_registry = NULL;
static gboolean cache_disabled = FALSE;
void
clapper_cache_initialize (void)
{
const gchar *env = g_getenv ("CLAPPER_DISABLE_CACHE");
if (G_LIKELY (!env || !g_str_has_prefix (env, "1"))) {
enum_registry = g_array_new (FALSE, TRUE, sizeof (GEnumValue *));
flags_registry = g_array_new (FALSE, TRUE, sizeof (GFlagsValue *));
} else {
cache_disabled = TRUE;
}
}
gboolean
clapper_cache_is_disabled (void)
{
return cache_disabled;
}
GMappedFile *
clapper_cache_open (const gchar *filename, const gchar **data, GError **error)
{
GMappedFile *file;
if (G_UNLIKELY (cache_disabled))
return NULL;
if (!(file = g_mapped_file_new (filename, FALSE, error)))
return NULL;
if (G_UNLIKELY (g_mapped_file_get_length (file) == 0)) {
g_mapped_file_unref (file);
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
"File is empty");
return NULL;
}
*data = g_mapped_file_get_contents (file);
/* Header name check */
if (G_UNLIKELY (g_strcmp0 (*data, CLAPPER_CACHE_HEADER) != 0)) {
g_mapped_file_unref (file);
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
"Invalid file header");
return NULL;
}
*data += strlen (*data) + 1;
/* Header version check */
if (clapper_cache_read_uint (data) != CLAPPER_VERSION_HEX) {
g_mapped_file_unref (file);
/* Just different version, so no error set */
return NULL;
}
return file;
}
inline gboolean
clapper_cache_read_boolean (const gchar **data)
{
gboolean val = *(const gboolean *) *data;
*data += sizeof (gboolean);
return val;
}
inline gint
clapper_cache_read_int (const gchar **data)
{
gint val = *(const gint *) *data;
*data += sizeof (gint);
return val;
}
inline guint
clapper_cache_read_uint (const gchar **data)
{
guint val = *(const guint *) *data;
*data += sizeof (guint);
return val;
}
inline gint64
clapper_cache_read_int64 (const gchar **data)
{
gint64 val = *(const gint64 *) *data;
*data += sizeof (gint64);
return val;
}
inline gdouble
clapper_cache_read_double (const gchar **data)
{
gdouble val = *(const gdouble *) *data;
*data += sizeof (gdouble);
return val;
}
inline const gchar *
clapper_cache_read_string (const gchar **data)
{
const gboolean is_null = clapper_cache_read_boolean (data);
const gchar *str = NULL;
if (!is_null) {
str = *data;
*data += strlen (str) + 1;
}
return str;
}
inline const guint8 *
clapper_cache_read_data (const gchar **data, gsize *size)
{
const guint8 *val = NULL;
*size = *(const gsize *) *data;
*data += sizeof (gsize);
if (G_LIKELY (*size > 0)) {
val = (const guint8 *) *data;
*data += *size;
}
return val;
}
inline GType
clapper_cache_read_enum (const gchar **data)
{
GType type;
const gchar *enum_name;
guint i, n_values;
enum_name = clapper_cache_read_string (data);
n_values = clapper_cache_read_uint (data);
/* If not registered yet */
if ((type = g_type_from_name (enum_name)) == 0) {
GEnumValue *values = g_new0 (GEnumValue, n_values + 1);
for (i = 0; i < n_values; ++i) {
values[i].value = clapper_cache_read_int (data);
values[i].value_name = g_intern_string (clapper_cache_read_string (data));
values[i].value_nick = g_intern_string (clapper_cache_read_string (data));
}
g_array_append_val (enum_registry, values); // store statically
type = g_enum_register_static (g_intern_string (enum_name),
g_array_index (enum_registry, GEnumValue *, enum_registry->len - 1));
} else {
/* Skip over data */
for (i = 0; i < n_values; ++i) {
clapper_cache_read_int (data); // value
clapper_cache_read_string (data); // value_name
clapper_cache_read_string (data); // value_nick
}
}
return type;
}
inline GType
clapper_cache_read_flags (const gchar **data)
{
GType type;
const gchar *flags_name;
guint i, n_values;
flags_name = clapper_cache_read_string (data);
n_values = clapper_cache_read_uint (data);
/* If not registered yet */
if ((type = g_type_from_name (flags_name)) == 0) {
GFlagsValue *values = g_new0 (GFlagsValue, n_values + 1);
for (i = 0; i < n_values; ++i) {
values[i].value = clapper_cache_read_int (data);
values[i].value_name = g_intern_string (clapper_cache_read_string (data));
values[i].value_nick = g_intern_string (clapper_cache_read_string (data));
}
g_array_append_val (flags_registry, values); // store statically
type = g_flags_register_static (g_intern_string (flags_name),
g_array_index (flags_registry, GFlagsValue *, flags_registry->len - 1));
} else {
/* Skip over data */
for (i = 0; i < n_values; ++i) {
clapper_cache_read_int (data); // value
clapper_cache_read_string (data); // value_name
clapper_cache_read_string (data); // value_nick
}
}
return type;
}
GType
clapper_cache_read_iface (const gchar **data)
{
gint iface_id = clapper_cache_read_int (data);
switch (iface_id) {
case CLAPPER_CACHE_IFACE_EXTRACTABLE:
return CLAPPER_TYPE_EXTRACTABLE;
case CLAPPER_CACHE_IFACE_PLAYLISTABLE:
return CLAPPER_TYPE_PLAYLISTABLE;
case CLAPPER_CACHE_IFACE_REACTABLE:
return CLAPPER_TYPE_REACTABLE;
default:
return 0;
}
}
GParamSpec *
clapper_cache_read_pspec (const gchar **data)
{
GParamSpec *pspec;
GType value_type;
const gchar *name, *nick, *blurb;
GParamFlags flags;
value_type = *(const GType *) *data;
*data += sizeof (GType);
name = clapper_cache_read_string (data);
nick = clapper_cache_read_string (data);
blurb = clapper_cache_read_string (data);
flags = *(const GParamFlags *) *data;
*data += sizeof (GParamFlags);
/* NOTE: C does not guarantee order in which function arguments
* are evaluated, so read into variables and then create pspec */
switch (value_type) {
case G_TYPE_BOOLEAN:
pspec = g_param_spec_boolean (name, nick, blurb,
clapper_cache_read_boolean (data), flags);
break;
case G_TYPE_INT:{
gint minimum = clapper_cache_read_int (data);
gint maximum = clapper_cache_read_int (data);
gint default_value = clapper_cache_read_int (data);
pspec = g_param_spec_int (name, nick, blurb,
minimum, maximum, default_value, flags);
break;
}
case G_TYPE_UINT:{
guint minimum = clapper_cache_read_uint (data);
guint maximum = clapper_cache_read_uint (data);
guint default_value = clapper_cache_read_uint (data);
pspec = g_param_spec_uint (name, nick, blurb,
minimum, maximum, default_value, flags);
break;
}
case G_TYPE_DOUBLE:{
gdouble minimum = clapper_cache_read_double (data);
gdouble maximum = clapper_cache_read_double (data);
gdouble default_value = clapper_cache_read_double (data);
pspec = g_param_spec_double (name, nick, blurb,
minimum, maximum, default_value, flags);
break;
}
case G_TYPE_STRING:
pspec = g_param_spec_string (name, nick, blurb,
clapper_cache_read_string (data), flags);
break;
case G_TYPE_ENUM:{
GType enum_type = clapper_cache_read_enum (data);
gint default_value = clapper_cache_read_int (data);
pspec = g_param_spec_enum (name, nick, blurb,
enum_type, default_value, flags);
break;
}
case G_TYPE_FLAGS:{
GType flags_type = clapper_cache_read_flags (data);
guint default_value = clapper_cache_read_uint (data);
pspec = g_param_spec_flags (name, nick, blurb,
flags_type, default_value, flags);
break;
}
default:
return NULL;
}
return g_param_spec_ref_sink (pspec);
}
GByteArray *
clapper_cache_create (void)
{
GByteArray *bytes;
if (G_UNLIKELY (cache_disabled))
return NULL;
bytes = g_byte_array_new ();
/* NOTE: We do not store whether string is NULL here, since it never is */
g_byte_array_append (bytes, (const guint8 *) CLAPPER_CACHE_HEADER, 8); // 7 + 1
clapper_cache_store_uint (bytes, CLAPPER_VERSION_HEX);
return bytes;
}
inline void
clapper_cache_store_boolean (GByteArray *bytes, gboolean val)
{
g_byte_array_append (bytes, (const guint8 *) &val, sizeof (gboolean));
}
inline void
clapper_cache_store_int (GByteArray *bytes, gint val)
{
g_byte_array_append (bytes, (const guint8 *) &val, sizeof (gint));
}
inline void
clapper_cache_store_uint (GByteArray *bytes, guint val)
{
g_byte_array_append (bytes, (const guint8 *) &val, sizeof (guint));
}
inline void
clapper_cache_store_int64 (GByteArray *bytes, gint64 val)
{
g_byte_array_append (bytes, (const guint8 *) &val, sizeof (gint64));
}
inline void
clapper_cache_store_double (GByteArray *bytes, gdouble val)
{
g_byte_array_append (bytes, (const guint8 *) &val, sizeof (gdouble));
}
inline void
clapper_cache_store_string (GByteArray *bytes, const gchar *val)
{
/* Distinguish empty string from NULL */
const gboolean is_null = (val == NULL);
clapper_cache_store_boolean (bytes, is_null);
if (!is_null)
g_byte_array_append (bytes, (const guint8 *) val, strlen (val) + 1);
}
inline void
clapper_cache_store_data (GByteArray *bytes, const guint8 *val, gsize val_size)
{
g_byte_array_append (bytes, (const guint8 *) &val_size, sizeof (gsize));
if (G_LIKELY (val_size > 0))
g_byte_array_append (bytes, val, val_size);
}
inline void
clapper_cache_store_enum (GByteArray *bytes, GType enum_type)
{
GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_peek (enum_type));
guint i;
clapper_cache_store_string (bytes, g_type_name (enum_type));
clapper_cache_store_uint (bytes, enum_class->n_values);
for (i = 0; i < enum_class->n_values; ++i) {
clapper_cache_store_int (bytes, enum_class->values[i].value);
clapper_cache_store_string (bytes, enum_class->values[i].value_name);
clapper_cache_store_string (bytes, enum_class->values[i].value_nick);
}
}
inline void
clapper_cache_store_flags (GByteArray *bytes, GType flags_type)
{
GFlagsClass *flags_class = G_FLAGS_CLASS (g_type_class_peek (flags_type));
guint i;
clapper_cache_store_string (bytes, g_type_name (flags_type));
clapper_cache_store_uint (bytes, flags_class->n_values);
for (i = 0; i < flags_class->n_values; ++i) {
clapper_cache_store_int (bytes, flags_class->values[i].value);
clapper_cache_store_string (bytes, flags_class->values[i].value_name);
clapper_cache_store_string (bytes, flags_class->values[i].value_nick);
}
}
gboolean
clapper_cache_store_iface (GByteArray *bytes, GType iface)
{
gint iface_id = 0;
if (iface == CLAPPER_TYPE_EXTRACTABLE)
iface_id = CLAPPER_CACHE_IFACE_EXTRACTABLE;
else if (iface == CLAPPER_TYPE_PLAYLISTABLE)
iface_id = CLAPPER_CACHE_IFACE_PLAYLISTABLE;
else if (iface == CLAPPER_TYPE_REACTABLE)
iface_id = CLAPPER_CACHE_IFACE_REACTABLE;
else
return FALSE;
clapper_cache_store_int (bytes, iface_id);
return TRUE;
}
gboolean
clapper_cache_store_pspec (GByteArray *bytes, GParamSpec *pspec)
{
GParamFlags flags;
const gboolean is_enum = G_IS_PARAM_SPEC_ENUM (pspec);
const gboolean is_flags = (!is_enum && G_IS_PARAM_SPEC_FLAGS (pspec));
if (is_enum) {
GType enum_type = G_TYPE_ENUM;
g_byte_array_append (bytes, (const guint8 *) &enum_type, sizeof (GType));
} else if (is_flags) {
GType flags_type = G_TYPE_FLAGS;
g_byte_array_append (bytes, (const guint8 *) &flags_type, sizeof (GType));
} else {
g_byte_array_append (bytes, (const guint8 *) &pspec->value_type, sizeof (GType));
}
clapper_cache_store_string (bytes, g_param_spec_get_name (pspec));
clapper_cache_store_string (bytes, g_param_spec_get_nick (pspec));
clapper_cache_store_string (bytes, g_param_spec_get_blurb (pspec));
flags = pspec->flags;
flags &= ~G_PARAM_STATIC_STRINGS; // Data read from cache is never static
g_byte_array_append (bytes, (const guint8 *) &flags, sizeof (GParamFlags));
switch (pspec->value_type) {
case G_TYPE_BOOLEAN:{
GParamSpecBoolean *p = (GParamSpecBoolean *) pspec;
clapper_cache_store_boolean (bytes, p->default_value);
break;
}
case G_TYPE_INT:{
GParamSpecInt *p = (GParamSpecInt *) pspec;
clapper_cache_store_int (bytes, p->minimum);
clapper_cache_store_int (bytes, p->maximum);
clapper_cache_store_int (bytes, p->default_value);
break;
}
case G_TYPE_UINT:{
GParamSpecUInt *p = (GParamSpecUInt *) pspec;
clapper_cache_store_uint (bytes, p->minimum);
clapper_cache_store_uint (bytes, p->maximum);
clapper_cache_store_uint (bytes, p->default_value);
break;
}
case G_TYPE_DOUBLE:{
GParamSpecDouble *p = (GParamSpecDouble *) pspec;
clapper_cache_store_double (bytes, p->minimum);
clapper_cache_store_double (bytes, p->maximum);
clapper_cache_store_double (bytes, p->default_value);
break;
}
case G_TYPE_STRING:{
GParamSpecString *p = (GParamSpecString *) pspec;
clapper_cache_store_string (bytes, p->default_value);
break;
}
default:{
if (is_enum) {
GParamSpecEnum *p = (GParamSpecEnum *) pspec;
clapper_cache_store_enum (bytes, pspec->value_type);
clapper_cache_store_int (bytes, p->default_value);
break;
} else if (is_flags) {
GParamSpecFlags *p = (GParamSpecFlags *) pspec;
clapper_cache_store_flags (bytes, pspec->value_type);
clapper_cache_store_uint (bytes, p->default_value);
break;
}
return FALSE;
}
}
return TRUE;
}
gboolean
clapper_cache_write (const gchar *filename, GByteArray *bytes, GError **error)
{
gchar *dirname = g_path_get_dirname (filename);
gboolean has_dir;
has_dir = (g_mkdir_with_parents (dirname, 0755) == 0);
g_free (dirname);
if (!has_dir) {
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
"Could not create directory to store cache content");
return FALSE;
}
/* Using "g_file_set_contents" to replace file atomically */
return g_file_set_contents (filename, (const gchar *) bytes->data, bytes->len, error);
}

View File

@@ -1,44 +0,0 @@
/* Clapper Playback Library
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <glib.h>
#include <glib-object.h>
#include "clapper-enhancer-proxy-list.h"
#include "clapper-enhancer-proxy.h"
G_BEGIN_DECLS
G_GNUC_INTERNAL
ClapperEnhancerProxyList * clapper_enhancer_proxy_list_new_named (const gchar *name);
G_GNUC_INTERNAL
void clapper_enhancer_proxy_list_take_proxy (ClapperEnhancerProxyList *list, ClapperEnhancerProxy *proxy);
G_GNUC_INTERNAL
void clapper_enhancer_proxy_list_fill_from_global_proxies (ClapperEnhancerProxyList *list);
G_GNUC_INTERNAL
void clapper_enhancer_proxy_list_sort (ClapperEnhancerProxyList *list);
G_GNUC_INTERNAL
gboolean clapper_enhancer_proxy_list_has_proxy_with_interface (ClapperEnhancerProxyList *list, GType iface_type);
G_END_DECLS

View File

@@ -1,351 +0,0 @@
/* Clapper Playback Library
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
/**
* ClapperEnhancerProxyList:
*
* A list of enhancer proxies.
*
* Since: 0.10
*/
#include <gio/gio.h>
#include "clapper-basic-functions.h"
#include "clapper-enhancer-proxy-list-private.h"
#include "clapper-enhancer-proxy-private.h"
#define GST_CAT_DEFAULT clapper_enhancer_proxy_list_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperEnhancerProxyList
{
GstObject parent;
GPtrArray *proxies;
};
enum
{
PROP_0,
PROP_N_PROXIES,
PROP_LAST
};
static void clapper_enhancer_proxy_list_model_iface_init (GListModelInterface *iface);
#define parent_class clapper_enhancer_proxy_list_parent_class
G_DEFINE_TYPE_WITH_CODE (ClapperEnhancerProxyList, clapper_enhancer_proxy_list, GST_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, clapper_enhancer_proxy_list_model_iface_init));
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
static GType
clapper_enhancer_proxy_list_model_get_item_type (GListModel *model)
{
return CLAPPER_TYPE_ENHANCER_PROXY;
}
static guint
clapper_enhancer_proxy_list_model_get_n_items (GListModel *model)
{
return CLAPPER_ENHANCER_PROXY_LIST_CAST (model)->proxies->len;
}
static gpointer
clapper_enhancer_proxy_list_model_get_item (GListModel *model, guint index)
{
ClapperEnhancerProxyList *self = CLAPPER_ENHANCER_PROXY_LIST_CAST (model);
ClapperEnhancerProxy *proxy = NULL;
if (G_LIKELY (index < self->proxies->len))
proxy = gst_object_ref (g_ptr_array_index (self->proxies, index));
return proxy;
}
static void
clapper_enhancer_proxy_list_model_iface_init (GListModelInterface *iface)
{
iface->get_item_type = clapper_enhancer_proxy_list_model_get_item_type;
iface->get_n_items = clapper_enhancer_proxy_list_model_get_n_items;
iface->get_item = clapper_enhancer_proxy_list_model_get_item;
}
/*
* clapper_enhancer_proxy_list_new_named:
* @name: (nullable): name of the #GstObject
*
* Returns: (transfer full): a new #ClapperEnhancerProxyList instance
*/
ClapperEnhancerProxyList *
clapper_enhancer_proxy_list_new_named (const gchar *name)
{
ClapperEnhancerProxyList *list;
list = g_object_new (CLAPPER_TYPE_ENHANCER_PROXY_LIST,
"name", name, NULL);
gst_object_ref_sink (list);
return list;
}
void
clapper_enhancer_proxy_list_take_proxy (ClapperEnhancerProxyList *self, ClapperEnhancerProxy *proxy)
{
g_ptr_array_add (self->proxies, proxy);
gst_object_set_parent (GST_OBJECT_CAST (proxy), GST_OBJECT_CAST (self));
}
/*
* clapper_enhancer_proxy_list_fill_from_global_proxies:
*
* Fill list with unconfigured proxies from global proxies list.
*/
void
clapper_enhancer_proxy_list_fill_from_global_proxies (ClapperEnhancerProxyList *self)
{
ClapperEnhancerProxyList *global_list = clapper_get_global_enhancer_proxies ();
static guint _list_id = 0;
guint i;
for (i = 0; i < global_list->proxies->len; ++i) {
ClapperEnhancerProxy *proxy, *proxy_copy;
gchar obj_name[64];
proxy = clapper_enhancer_proxy_list_peek_proxy (global_list, i);
/* Name newly created proxy, very useful for debugging. Keep index per
* list, so it will be the same as the player that proxy belongs to. */
g_snprintf (obj_name, sizeof (obj_name), "%s-proxy%u",
clapper_enhancer_proxy_get_friendly_name (proxy), _list_id);
proxy_copy = clapper_enhancer_proxy_copy (proxy, obj_name);
clapper_enhancer_proxy_list_take_proxy (self, proxy_copy);
}
_list_id++;
}
static gint
_sort_values_by_name (ClapperEnhancerProxy *proxy_a, ClapperEnhancerProxy *proxy_b)
{
return g_ascii_strcasecmp (
clapper_enhancer_proxy_get_friendly_name (proxy_a),
clapper_enhancer_proxy_get_friendly_name (proxy_b));
}
/*
* clapper_enhancer_proxy_list_sort:
*
* Sort all list elements by enhancer friendly name.
*/
void
clapper_enhancer_proxy_list_sort (ClapperEnhancerProxyList *self)
{
g_ptr_array_sort_values (self->proxies, (GCompareFunc) _sort_values_by_name);
}
/*
* clapper_enhancer_proxy_list_has_proxy_with_interface:
* @iface_type: an interface #GType
*
* Check if any enhancer implementing given interface type is available.
*
* Returns: whether any enhancer proxy was found.
*/
gboolean
clapper_enhancer_proxy_list_has_proxy_with_interface (ClapperEnhancerProxyList *self, GType iface_type)
{
guint i;
for (i = 0; i < self->proxies->len; ++i) {
ClapperEnhancerProxy *proxy = clapper_enhancer_proxy_list_peek_proxy (self, i);
if (clapper_enhancer_proxy_target_has_interface (proxy, iface_type))
return TRUE;
}
return FALSE;
}
/**
* clapper_enhancer_proxy_list_get_proxy:
* @list: a #ClapperEnhancerProxyList
* @index: an enhancer proxy index
*
* Get the #ClapperEnhancerProxy at index.
*
* This behaves the same as [method@Gio.ListModel.get_item], and is here
* for code uniformity and convenience to avoid type casting by user.
*
* Returns: (transfer full) (nullable): The #ClapperEnhancerProxy at @index.
*
* Since: 0.10
*/
ClapperEnhancerProxy *
clapper_enhancer_proxy_list_get_proxy (ClapperEnhancerProxyList *self, guint index)
{
g_return_val_if_fail (CLAPPER_IS_ENHANCER_PROXY_LIST (self), NULL);
return g_list_model_get_item (G_LIST_MODEL (self), index);
}
/**
* clapper_enhancer_proxy_list_peek_proxy: (skip)
* @list: a #ClapperEnhancerProxyList
* @index: an enhancer proxy index
*
* Get the #ClapperEnhancerProxy at index.
*
* Similar to [method@Clapper.EnhancerProxyList.get_proxy], but does not take
* a new reference on proxy.
*
* Proxies in a list are only removed when a [class@Clapper.Player] instance
* they originate from is destroyed, so do not use returned object afterwards
* unless you take an additional reference on it.
*
* Returns: (transfer none) (nullable): The #ClapperEnhancerProxy at @index.
*
* Since: 0.10
*/
ClapperEnhancerProxy *
clapper_enhancer_proxy_list_peek_proxy (ClapperEnhancerProxyList *self, guint index)
{
g_return_val_if_fail (CLAPPER_IS_ENHANCER_PROXY_LIST (self), NULL);
return g_ptr_array_index (self->proxies, index);
}
/**
* clapper_enhancer_proxy_list_get_proxy_by_module:
* @list: a #ClapperEnhancerProxyList
* @module_name: an enhancer module name
*
* Get the #ClapperEnhancerProxy by module name as defined in its plugin file.
*
* A convenience function to find a #ClapperEnhancerProxy by its unique
* module name in the list.
*
* Returns: (transfer full) (nullable): The #ClapperEnhancerProxy with requested module name.
*
* Since: 0.10
*/
ClapperEnhancerProxy *
clapper_enhancer_proxy_list_get_proxy_by_module (ClapperEnhancerProxyList *self, const gchar *module_name)
{
guint i;
g_return_val_if_fail (CLAPPER_IS_ENHANCER_PROXY_LIST (self), NULL);
g_return_val_if_fail (module_name != NULL, NULL);
for (i = 0; i < self->proxies->len; ++i) {
ClapperEnhancerProxy *proxy = g_ptr_array_index (self->proxies, i);
if (strcmp (clapper_enhancer_proxy_get_module_name (proxy), module_name) == 0)
return gst_object_ref (proxy);
}
return NULL;
}
/**
* clapper_enhancer_proxy_list_get_n_proxies:
* @list: a #ClapperEnhancerProxyList
*
* Get the number of proxies in #ClapperEnhancerProxyList.
*
* This behaves the same as [method@Gio.ListModel.get_n_items], and is here
* for code uniformity and convenience to avoid type casting by user.
*
* Returns: The number of proxies in #ClapperEnhancerProxyList.
*
* Since: 0.10
*/
guint
clapper_enhancer_proxy_list_get_n_proxies (ClapperEnhancerProxyList *self)
{
g_return_val_if_fail (CLAPPER_IS_ENHANCER_PROXY_LIST (self), 0);
return g_list_model_get_n_items (G_LIST_MODEL (self));
}
static void
_proxy_remove_func (ClapperEnhancerProxy *proxy)
{
gst_object_unparent (GST_OBJECT_CAST (proxy));
gst_object_unref (proxy);
}
static void
clapper_enhancer_proxy_list_init (ClapperEnhancerProxyList *self)
{
self->proxies = g_ptr_array_new_with_free_func ((GDestroyNotify) _proxy_remove_func);
}
static void
clapper_enhancer_proxy_list_finalize (GObject *object)
{
ClapperEnhancerProxyList *self = CLAPPER_ENHANCER_PROXY_LIST_CAST (object);
GST_TRACE_OBJECT (self, "Finalize");
g_ptr_array_unref (self->proxies);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
clapper_enhancer_proxy_list_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
ClapperEnhancerProxyList *self = CLAPPER_ENHANCER_PROXY_LIST_CAST (object);
switch (prop_id) {
case PROP_N_PROXIES:
g_value_set_uint (value, clapper_enhancer_proxy_list_get_n_proxies (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_enhancer_proxy_list_class_init (ClapperEnhancerProxyListClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperenhancerproxylist", 0,
"Clapper Enhancer Proxy List");
gobject_class->get_property = clapper_enhancer_proxy_list_get_property;
gobject_class->finalize = clapper_enhancer_proxy_list_finalize;
/**
* ClapperEnhancerProxyList:n-proxies:
*
* Number of proxies in the list.
*
* Since: 0.10
*/
param_specs[PROP_N_PROXIES] = g_param_spec_uint ("n-proxies",
NULL, NULL, 0, G_MAXUINT, 0,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
}

View File

@@ -1,52 +0,0 @@
/* Clapper Playback Library
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once
#if !defined(__CLAPPER_INSIDE__) && !defined(CLAPPER_COMPILATION)
#error "Only <clapper/clapper.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <gst/gst.h>
#include <clapper/clapper-visibility.h>
#include <clapper/clapper-enhancer-proxy.h>
G_BEGIN_DECLS
#define CLAPPER_TYPE_ENHANCER_PROXY_LIST (clapper_enhancer_proxy_list_get_type())
#define CLAPPER_ENHANCER_PROXY_LIST_CAST(obj) ((ClapperEnhancerProxyList *)(obj))
CLAPPER_API
G_DECLARE_FINAL_TYPE (ClapperEnhancerProxyList, clapper_enhancer_proxy_list, CLAPPER, ENHANCER_PROXY_LIST, GstObject)
CLAPPER_API
ClapperEnhancerProxy * clapper_enhancer_proxy_list_get_proxy (ClapperEnhancerProxyList *list, guint index);
CLAPPER_API
ClapperEnhancerProxy * clapper_enhancer_proxy_list_peek_proxy (ClapperEnhancerProxyList *list, guint index);
CLAPPER_API
ClapperEnhancerProxy * clapper_enhancer_proxy_list_get_proxy_by_module (ClapperEnhancerProxyList *list, const gchar *module_name);
CLAPPER_API
guint clapper_enhancer_proxy_list_get_n_proxies (ClapperEnhancerProxyList *list);
G_END_DECLS

View File

@@ -1,56 +0,0 @@
/* Clapper Playback Library
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <glib.h>
#include <glib-object.h>
#include <gst/gst.h>
#include "clapper-enhancer-proxy.h"
G_BEGIN_DECLS
G_GNUC_INTERNAL
ClapperEnhancerProxy * clapper_enhancer_proxy_new_global_take (GObject *peas_info); // Using parent type for building without libpeas
G_GNUC_INTERNAL
ClapperEnhancerProxy * clapper_enhancer_proxy_copy (ClapperEnhancerProxy *src_proxy, const gchar *copy_name);
G_GNUC_INTERNAL
gboolean clapper_enhancer_proxy_fill_from_cache (ClapperEnhancerProxy *proxy);
G_GNUC_INTERNAL
gboolean clapper_enhancer_proxy_fill_from_instance (ClapperEnhancerProxy *proxy, GObject *enhancer);
G_GNUC_INTERNAL
void clapper_enhancer_proxy_export_to_cache (ClapperEnhancerProxy *proxy);
G_GNUC_INTERNAL
GObject * clapper_enhancer_proxy_get_peas_info (ClapperEnhancerProxy *proxy);
G_GNUC_INTERNAL
gboolean clapper_enhancer_proxy_has_locally_set (ClapperEnhancerProxy *proxy, const gchar *property_name);
G_GNUC_INTERNAL
GstStructure * clapper_enhancer_proxy_make_current_config (ClapperEnhancerProxy *proxy);
G_GNUC_INTERNAL
void clapper_enhancer_proxy_apply_config_to_enhancer (ClapperEnhancerProxy *proxy, const GstStructure *config, GObject *enhancer);
G_END_DECLS

File diff suppressed because it is too large Load Diff

View File

@@ -1,85 +0,0 @@
/* Clapper Playback Library
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once
#if !defined(__CLAPPER_INSIDE__) && !defined(CLAPPER_COMPILATION)
#error "Only <clapper/clapper.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <gio/gio.h>
#include <gst/gst.h>
#include <clapper/clapper-visibility.h>
G_BEGIN_DECLS
#define CLAPPER_TYPE_ENHANCER_PROXY (clapper_enhancer_proxy_get_type())
#define CLAPPER_ENHANCER_PROXY_CAST(obj) ((ClapperEnhancerProxy *)(obj))
CLAPPER_API
G_DECLARE_FINAL_TYPE (ClapperEnhancerProxy, clapper_enhancer_proxy, CLAPPER, ENHANCER_PROXY, GstObject)
CLAPPER_API
const gchar * clapper_enhancer_proxy_get_friendly_name (ClapperEnhancerProxy *proxy);
CLAPPER_API
const gchar * clapper_enhancer_proxy_get_module_name (ClapperEnhancerProxy *proxy);
CLAPPER_API
const gchar * clapper_enhancer_proxy_get_module_dir (ClapperEnhancerProxy *proxy);
CLAPPER_API
const gchar * clapper_enhancer_proxy_get_description (ClapperEnhancerProxy *proxy);
CLAPPER_API
const gchar * clapper_enhancer_proxy_get_version (ClapperEnhancerProxy *proxy);
CLAPPER_API
const gchar * clapper_enhancer_proxy_get_extra_data (ClapperEnhancerProxy *proxy, const gchar *key);
CLAPPER_API
gboolean clapper_enhancer_proxy_extra_data_lists_value (ClapperEnhancerProxy *proxy, const gchar *key, const gchar *value);
CLAPPER_API
GType * clapper_enhancer_proxy_get_target_interfaces (ClapperEnhancerProxy *proxy, guint *n_interfaces);
CLAPPER_API
gboolean clapper_enhancer_proxy_target_has_interface (ClapperEnhancerProxy *proxy, GType iface_type);
CLAPPER_API
GParamSpec ** clapper_enhancer_proxy_get_target_properties (ClapperEnhancerProxy *proxy, guint *n_properties);
CLAPPER_API
GSettings * clapper_enhancer_proxy_get_settings (ClapperEnhancerProxy *proxy);
CLAPPER_API
void clapper_enhancer_proxy_set_locally (ClapperEnhancerProxy *proxy, const gchar *first_property_name, ...) G_GNUC_NULL_TERMINATED;
CLAPPER_API
void clapper_enhancer_proxy_set_locally_with_table (ClapperEnhancerProxy *proxy, GHashTable *table);
CLAPPER_API
void clapper_enhancer_proxy_set_target_creation_allowed (ClapperEnhancerProxy *proxy, gboolean allowed);
CLAPPER_API
gboolean clapper_enhancer_proxy_get_target_creation_allowed (ClapperEnhancerProxy *proxy);
G_END_DECLS

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once
@@ -21,15 +22,21 @@
#include <glib.h>
#include <glib-object.h>
#include "clapper-enhancer-proxy-list.h"
#include "clapper-enhancer-proxy.h"
G_BEGIN_DECLS
G_GNUC_INTERNAL
void clapper_enhancers_loader_initialize (ClapperEnhancerProxyList *proxies);
void clapper_enhancers_loader_initialize (void);
G_GNUC_INTERNAL
GObject * clapper_enhancers_loader_create_enhancer (ClapperEnhancerProxy *proxy, GType iface_type);
gboolean clapper_enhancers_loader_has_enhancers (GType iface_type);
G_GNUC_INTERNAL
gchar ** clapper_enhancers_loader_get_schemes (GType iface_type);
G_GNUC_INTERNAL
gboolean clapper_enhancers_loader_check (GType iface_type, const gchar *scheme, const gchar *host, const gchar **name);
G_GNUC_INTERNAL
GObject * clapper_enhancers_loader_create_enhancer_for_uri (GType iface_type, GUri *uri);
G_END_DECLS

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "config.h"
@@ -27,13 +28,11 @@ static HMODULE _enhancers_dll_handle = NULL;
#endif
#include "clapper-enhancers-loader-private.h"
#include "clapper-enhancer-proxy-list-private.h"
#include "clapper-enhancer-proxy-private.h"
// Supported interfaces
#include "clapper-extractable.h"
#include "clapper-playlistable.h"
#include "clapper-reactable.h"
#define ENHANCER_INTERFACES "X-Interfaces"
#define ENHANCER_SCHEMES "X-Schemes"
#define ENHANCER_HOSTS "X-Hosts"
#define ENHANCER_IFACE_NAME_FROM_TYPE(type) (g_type_name (type) + 7) // strip "Clapper" prefix
#define GST_CAT_DEFAULT clapper_enhancers_loader_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
@@ -53,39 +52,16 @@ _import_enhancers (const gchar *enhancers_path)
g_strfreev (dir_paths);
}
static GObject *
_force_create_enhancer (ClapperEnhancerProxy *proxy, GType iface_type)
{
GObject *enhancer = NULL;
PeasPluginInfo *info = (PeasPluginInfo *) clapper_enhancer_proxy_get_peas_info (proxy);
g_mutex_lock (&load_lock);
if (!peas_plugin_info_is_loaded (info) && !peas_engine_load_plugin (_engine, info)) {
GST_ERROR ("Could not load enhancer: %s", peas_plugin_info_get_module_name (info));
} else if (!peas_engine_provides_extension (_engine, info, iface_type)) {
GST_LOG ("No \"%s\" enhancer in module: %s", g_type_name (iface_type),
peas_plugin_info_get_module_name (info));
} else {
enhancer = peas_engine_create_extension (_engine, info, iface_type, NULL);
}
g_mutex_unlock (&load_lock);
return enhancer;
}
/*
* clapper_enhancers_loader_initialize:
*
* Initializes #PeasEngine with directories that store enhancers.
*/
void
clapper_enhancers_loader_initialize (ClapperEnhancerProxyList *proxies)
clapper_enhancers_loader_initialize (void)
{
const gchar *enhancers_path;
gchar *custom_path = NULL;
guint i, n_items;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperenhancersloader", 0,
"Clapper Enhancer Loader");
@@ -98,8 +74,9 @@ clapper_enhancers_loader_initialize (ClapperEnhancerProxyList *proxies)
win_base_dir = g_win32_get_package_installation_directory_of_module (
_enhancers_dll_handle);
/* FIXME: Avoid hardcoded major version */
custom_path = g_build_filename (win_base_dir,
"lib", CLAPPER_API_NAME, "enhancers", NULL);
"lib", "clapper-0.0", "enhancers", NULL);
enhancers_path = custom_path; // assign temporarily
g_free (win_base_dir);
@@ -127,72 +104,321 @@ clapper_enhancers_loader_initialize (ClapperEnhancerProxyList *proxies)
_import_enhancers (enhancers_path);
}
n_items = g_list_model_get_n_items ((GListModel *) _engine);
for (i = 0; i < n_items; ++i) {
PeasPluginInfo *info = (PeasPluginInfo *) g_list_model_get_item ((GListModel *) _engine, i);
ClapperEnhancerProxy *proxy;
gboolean filled;
if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= GST_LEVEL_INFO) {
GListModel *list = (GListModel *) _engine;
guint i, n_items = g_list_model_get_n_items (list);
/* Clapper supports only 1 proxy per plugin. Each plugin can
* ship 1 class, but it can implement more than 1 interface. */
proxy = clapper_enhancer_proxy_new_global_take ((GObject *) info);
/* Try to fill missing data from cache (fast).
* Otherwise make an instance and fill missing data from it (slow). */
if (!(filled = clapper_enhancer_proxy_fill_from_cache (proxy))) {
GObject *enhancer;
const GType main_types[] = { CLAPPER_TYPE_EXTRACTABLE, CLAPPER_TYPE_PLAYLISTABLE, CLAPPER_TYPE_REACTABLE };
guint j;
/* We cannot ask libpeas for "any" of our main interfaces, so try each one until found */
for (j = 0; j < G_N_ELEMENTS (main_types); ++j) {
if ((enhancer = _force_create_enhancer (proxy, main_types[j]))) {
filled = clapper_enhancer_proxy_fill_from_instance (proxy, enhancer);
g_object_unref (enhancer);
clapper_enhancer_proxy_export_to_cache (proxy);
break;
}
}
for (i = 0; i < n_items; ++i) {
PeasPluginInfo *info = (PeasPluginInfo *) g_list_model_get_item (list, i);
GST_INFO ("Found enhancer: %s (%s)", peas_plugin_info_get_name (info),
peas_plugin_info_get_external_data (info, ENHANCER_INTERFACES));
g_object_unref (info);
}
if (G_LIKELY (filled)) {
GST_INFO ("Found enhancer: \"%s\" (%s)",
clapper_enhancer_proxy_get_friendly_name (proxy),
clapper_enhancer_proxy_get_module_name (proxy));
clapper_enhancer_proxy_list_take_proxy (proxies, proxy);
} else {
GST_WARNING ("Enhancer init failed: \"%s\" (%s)",
clapper_enhancer_proxy_get_friendly_name (proxy),
clapper_enhancer_proxy_get_module_name (proxy));
gst_object_unref (proxy);
}
GST_INFO ("Clapper enhancers initialized, found: %u", n_items);
}
clapper_enhancer_proxy_list_sort (proxies);
GST_INFO ("Clapper enhancers initialized, found: %u",
clapper_enhancer_proxy_list_get_n_proxies (proxies));
g_free (custom_path);
}
static inline gboolean
_is_name_listed (const gchar *name, const gchar *list_str)
{
gsize name_len = strlen (name);
guint i = 0;
while (list_str[i] != '\0') {
guint end = i;
while (list_str[end] != ';' && list_str[end] != '\0')
++end;
/* Compare letters count until separator and prefix of whole string */
if (end - i == name_len && g_str_has_prefix (list_str + i, name))
return TRUE;
i = end;
/* Move to the next letter after ';' */
if (list_str[i] != '\0')
++i;
}
return FALSE;
}
/*
* clapper_enhancers_loader_create_enhancer:
* @iface_type: a requested #GType
* @info: a #PeasPluginInfo
* clapper_enhancers_loader_get_info:
* @iface_type: an interface #GType
* @scheme: an URI scheme
* @host: (nullable): an URI host
*
* Creates a new enhancer object using @info.
* Returns: (transfer full) (nullable): available #PeasPluginInfo or %NULL.
*/
static PeasPluginInfo *
clapper_enhancers_loader_get_info (GType iface_type, const gchar *scheme, const gchar *host)
{
GListModel *list = (GListModel *) _engine;
PeasPluginInfo *found_info = NULL;
guint i, n_plugins = g_list_model_get_n_items (list);
const gchar *iface_name;
gboolean is_https;
if (n_plugins == 0) {
GST_INFO ("No Clapper enhancers found");
return NULL;
}
iface_name = ENHANCER_IFACE_NAME_FROM_TYPE (iface_type);
/* Strip common subdomains, so plugins do not
* have to list all combinations */
if (host) {
if (g_str_has_prefix (host, "www."))
host += 4;
else if (g_str_has_prefix (host, "m."))
host += 2;
}
GST_INFO ("Enhancer check, iface: %s, scheme: %s, host: %s",
iface_name, scheme, GST_STR_NULL (host));
is_https = (g_str_has_prefix (scheme, "http")
&& (scheme[4] == '\0' || (scheme[4] == 's' && scheme[5] == '\0')));
if (!host && is_https)
return NULL;
for (i = 0; i < n_plugins; ++i) {
PeasPluginInfo *info = (PeasPluginInfo *) g_list_model_get_item (list, i);
const gchar *iface_names, *schemes, *hosts;
if (!(iface_names = peas_plugin_info_get_external_data (info, ENHANCER_INTERFACES))) {
GST_DEBUG ("Skipping enhancer without interfaces: %s", peas_plugin_info_get_name (info));
g_object_unref (info);
continue;
}
if (!_is_name_listed (iface_name, iface_names)) {
g_object_unref (info);
continue;
}
if (!(schemes = peas_plugin_info_get_external_data (info, ENHANCER_SCHEMES))) {
GST_DEBUG ("Skipping enhancer without schemes: %s", peas_plugin_info_get_name (info));
g_object_unref (info);
continue;
}
if (!_is_name_listed (scheme, schemes)) {
g_object_unref (info);
continue;
}
if (is_https) {
if (!(hosts = peas_plugin_info_get_external_data (info, ENHANCER_HOSTS))) {
GST_DEBUG ("Skipping enhancer without hosts: %s", peas_plugin_info_get_name (info));
g_object_unref (info);
continue;
}
if (!_is_name_listed (host, hosts)) {
g_object_unref (info);
continue;
}
}
found_info = info;
break;
}
return found_info;
}
/*
* clapper_enhancers_loader_has_enhancers:
* @iface_type: an interface #GType
*
* Check if any enhancer implementing given interface type is available.
*
* Returns: whether any valid enhancer was found.
*/
gboolean
clapper_enhancers_loader_has_enhancers (GType iface_type)
{
GListModel *list = (GListModel *) _engine;
const gchar *iface_name = ENHANCER_IFACE_NAME_FROM_TYPE (iface_type);
guint i, n_plugins;
GST_DEBUG ("Checking for any enhancers of type: \"%s\"", iface_name);
n_plugins = g_list_model_get_n_items (list);
for (i = 0; i < n_plugins; ++i) {
PeasPluginInfo *info = (PeasPluginInfo *) g_list_model_get_item (list, i);
const gchar *iface_names;
if (!(iface_names = peas_plugin_info_get_external_data (info, ENHANCER_INTERFACES))) {
g_object_unref (info);
continue;
}
if (!_is_name_listed (iface_name, iface_names)) {
g_object_unref (info);
continue;
}
/* Additional validation */
if (!peas_plugin_info_get_external_data (info, ENHANCER_SCHEMES)
|| !peas_plugin_info_get_external_data (info, ENHANCER_HOSTS)) {
g_object_unref (info);
continue;
}
GST_DEBUG ("Found valid enhancers of type: \"%s\"", iface_name);
g_object_unref (info);
return TRUE;
}
GST_DEBUG ("No available enhancers of type: \"%s\"", iface_name);
return FALSE;
}
/*
* clapper_enhancers_loader_get_schemes:
* @iface_type: an interface #GType
*
* Get all supported schemes for a given interface type.
* The returned array consists of unique strings (no duplicates).
*
* Returns: (transfer full): all supported schemes by enhancers of type.
*/
gchar **
clapper_enhancers_loader_get_schemes (GType iface_type)
{
GListModel *list = (GListModel *) _engine;
GSList *found_schemes = NULL, *fs;
const gchar *iface_name = ENHANCER_IFACE_NAME_FROM_TYPE (iface_type);
gchar **schemes_strv;
guint i, n_plugins, n_schemes;
GST_DEBUG ("Checking supported URI schemes for \"%s\"", iface_name);
n_plugins = g_list_model_get_n_items (list);
for (i = 0; i < n_plugins; ++i) {
PeasPluginInfo *info = (PeasPluginInfo *) g_list_model_get_item (list, i);
const gchar *iface_names, *schemes;
gchar **tmp_strv;
gint j;
if (!(iface_names = peas_plugin_info_get_external_data (info, ENHANCER_INTERFACES))) {
g_object_unref (info);
continue;
}
if (!_is_name_listed (iface_name, iface_names)) {
g_object_unref (info);
continue;
}
if (!(schemes = peas_plugin_info_get_external_data (info, ENHANCER_SCHEMES))) {
g_object_unref (info);
continue;
}
tmp_strv = g_strsplit (schemes, ";", 0);
for (j = 0; tmp_strv[j]; ++j) {
const gchar *scheme = tmp_strv[j];
if (!found_schemes || !g_slist_find_custom (found_schemes,
scheme, (GCompareFunc) strcmp)) {
found_schemes = g_slist_append (found_schemes, g_strdup (scheme));
GST_INFO ("Found supported URI scheme: %s", scheme);
}
}
g_strfreev (tmp_strv);
g_object_unref (info);
}
n_schemes = g_slist_length (found_schemes);
schemes_strv = g_new0 (gchar *, n_schemes + 1);
fs = found_schemes;
for (i = 0; i < n_schemes; ++i) {
schemes_strv[i] = fs->data;
fs = fs->next;
}
GST_DEBUG ("Total found URI schemes: %u", n_schemes);
/* Since string pointers were taken,
* free list without content */
g_slist_free (found_schemes);
return schemes_strv;
}
/*
* clapper_enhancers_loader_check:
* @iface_type: a requested #GType
* @scheme: an URI scheme
* @host: (nullable): an URI host
* @name: (out) (optional) (transfer none): return location for found enhancer name
*
* Checks if any enhancer can handle @uri without initializing loader
* or creating enhancer instance, thus this can be used freely from any thread.
*
* Returns: whether enhancer for given scheme and host is available.
*/
gboolean
clapper_enhancers_loader_check (GType iface_type,
const gchar *scheme, const gchar *host, const gchar **name)
{
PeasPluginInfo *info;
if ((info = clapper_enhancers_loader_get_info (iface_type, scheme, host))) {
if (name)
*name = peas_plugin_info_get_name (info);
g_object_unref (info);
return TRUE;
}
return FALSE;
}
/*
* clapper_enhancers_loader_create_enhancer_for_uri:
* @iface_type: a requested #GType
* @uri: a #GUri
*
* Creates a new enhancer object for given URI.
*
* Enhancer should only be created and used within single thread.
*
* Returns: (transfer full) (nullable): a new enhancer instance.
*/
GObject *
clapper_enhancers_loader_create_enhancer (ClapperEnhancerProxy *proxy, GType iface_type)
clapper_enhancers_loader_create_enhancer_for_uri (GType iface_type, GUri *uri)
{
if (!clapper_enhancer_proxy_get_target_creation_allowed (proxy))
return NULL;
GObject *enhancer = NULL;
PeasPluginInfo *info;
const gchar *scheme = g_uri_get_scheme (uri);
const gchar *host = g_uri_get_host (uri);
return _force_create_enhancer (proxy, iface_type);
if ((info = clapper_enhancers_loader_get_info (iface_type, scheme, host))) {
g_mutex_lock (&load_lock);
if (!peas_plugin_info_is_loaded (info) && !peas_engine_load_plugin (_engine, info)) {
GST_ERROR ("Could not load enhancer: %s", peas_plugin_info_get_name (info));
} else if (!peas_engine_provides_extension (_engine, info, iface_type)) {
GST_ERROR ("No \"%s\" enhancer in plugin: %s", ENHANCER_IFACE_NAME_FROM_TYPE (iface_type),
peas_plugin_info_get_name (info));
} else {
enhancer = peas_engine_create_extension (_engine, info, iface_type, NULL);
}
g_mutex_unlock (&load_lock);
g_object_unref (info);
}
return enhancer;
}

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once
@@ -126,47 +127,4 @@ typedef enum
CLAPPER_DISCOVERER_DISCOVERY_NONCURRENT,
} ClapperDiscovererDiscoveryMode;
/* NOTE: GStreamer uses param flags 8-16, so start with 17. */
/**
* ClapperEnhancerParamFlags:
* @CLAPPER_ENHANCER_PARAM_GLOBAL: Use this flag for enhancer properties that should have global access scope.
* Such are meant for application `USER` to configure.
* @CLAPPER_ENHANCER_PARAM_LOCAL: Use this flag for enhancer properties that should have local access scope.
* Such are meant for `APPLICATION` to configure.
* @CLAPPER_ENHANCER_PARAM_FILEPATH: Use this flag for enhancer properties that store string with a file path.
* Applications can use this as a hint to show file selection instead of a text entry.
* @CLAPPER_ENHANCER_PARAM_DIRPATH: Use this flag for enhancer properties that store string with a directory path.
* Applications can use this as a hint to show directory selection instead of a text entry.
*
* Additional [flags@GObject.ParamFlags] to be set in enhancer plugins implementations.
*
* Since: 0.10
*/
typedef enum
{
CLAPPER_ENHANCER_PARAM_GLOBAL = 1 << 17,
CLAPPER_ENHANCER_PARAM_LOCAL = 1 << 18,
CLAPPER_ENHANCER_PARAM_FILEPATH = 1 << 19,
CLAPPER_ENHANCER_PARAM_DIRPATH = 1 << 20,
} ClapperEnhancerParamFlags;
/**
* ClapperReactableItemUpdatedFlags:
* @CLAPPER_REACTABLE_ITEM_UPDATED_TITLE: Media item title was updated.
* @CLAPPER_REACTABLE_ITEM_UPDATED_DURATION: Media item duration was updated.
* @CLAPPER_REACTABLE_ITEM_UPDATED_TIMELINE: Media item timeline was updated.
* @CLAPPER_REACTABLE_ITEM_UPDATED_TAGS: Media item tags were updated.
*
* Flags informing which properties were updated within [class@Clapper.MediaItem].
*
* Since: 0.10
*/
typedef enum
{
CLAPPER_REACTABLE_ITEM_UPDATED_TITLE = 1 << 0,
CLAPPER_REACTABLE_ITEM_UPDATED_DURATION = 1 << 1,
CLAPPER_REACTABLE_ITEM_UPDATED_TIMELINE = 1 << 2,
CLAPPER_REACTABLE_ITEM_UPDATED_TAGS = 1 << 3,
} ClapperReactableItemUpdatedFlags;
G_END_DECLS

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

View File

@@ -12,8 +12,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once

Some files were not shown because too many files have changed in this diff Show More