Sat, 09 Aug 2025 17:37:27 +0800
Fix the birb header path
The birb header referred would only work with birb provided by wrap casuing
build to fail because of system-installed birb dependency. The commit points
it to the correct path <birb.h>.
See: https://keep.imfreedom.org/birb/birb/file/5bf00c7d7f80/birb/meson.build#l77
/* * Purple - Internet Messaging Library * Copyright (C) Pidgin Developers <devel@pidgin.im> * * Purple is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * source distribution. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 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 General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this library; if not, see <https://www.gnu.org/licenses/>. */ #include <birb.h> #include "purplescheduledtask.h" #include "purpleenums.h" struct _PurpleScheduledTask { GObject parent; gboolean cancellable; GDateTime *execute_at; char *id; gboolean persistent; guint source_id; PurpleScheduledTaskState state; char *subtitle; PurpleTags *tags; char *task_type; char *title; }; enum { PROP_0, PROP_CANCELLABLE, PROP_EXECUTE_AT, PROP_ID, PROP_PERSISTENT, PROP_STATE, PROP_SUBTITLE, PROP_TAGS, PROP_TASK_TYPE, PROP_TITLE, N_PROPERTIES, }; static GParamSpec *properties[N_PROPERTIES] = {NULL, }; enum { SIG_EXECUTE, N_SIGNALS, }; static guint signals[N_SIGNALS] = {0, }; G_DEFINE_QUARK(purple-scheduled-task-error, purple_scheduled_task_error) /****************************************************************************** * Helpers *****************************************************************************/ static void purple_scheduled_task_set_cancellable(PurpleScheduledTask *task, gboolean cancellable) { g_return_if_fail(PURPLE_IS_SCHEDULED_TASK(task)); if(task->cancellable != cancellable) { task->cancellable = cancellable; g_object_notify_by_pspec(G_OBJECT(task), properties[PROP_CANCELLABLE]); } } static void purple_scheduled_task_set_id(PurpleScheduledTask *task, const char *id) { g_return_if_fail(PURPLE_IS_SCHEDULED_TASK(task)); if(g_set_str(&task->id, id)) { g_object_notify_by_pspec(G_OBJECT(task), properties[PROP_ID]); } } static void purple_scheduled_task_set_task_type(PurpleScheduledTask *task, const char *task_type) { g_return_if_fail(PURPLE_IS_SCHEDULED_TASK(task)); if(g_set_str(&task->task_type, task_type)) { g_object_notify_by_pspec(G_OBJECT(task), properties[PROP_TASK_TYPE]); } } static void purple_scheduled_task_set_title(PurpleScheduledTask *task, const char *title) { g_return_if_fail(PURPLE_IS_SCHEDULED_TASK(task)); if(g_set_str(&task->title, title)) { g_object_notify_by_pspec(G_OBJECT(task), properties[PROP_TITLE]); } } /****************************************************************************** * Callbacks *****************************************************************************/ static gboolean purple_scheduled_task_timeout_cb(gpointer data) { PurpleScheduledTask *task = data; task->state = PURPLE_SCHEDULED_TASK_STATE_EXECUTING; g_object_notify_by_pspec(G_OBJECT(task), properties[PROP_STATE]); g_signal_emit(data, signals[SIG_EXECUTE], 0); task->state = PURPLE_SCHEDULED_TASK_STATE_EXECUTED; g_object_notify_by_pspec(G_OBJECT(task), properties[PROP_STATE]); task->source_id = 0; return G_SOURCE_REMOVE; } /****************************************************************************** * GObject Implementation *****************************************************************************/ G_DEFINE_FINAL_TYPE(PurpleScheduledTask, purple_scheduled_task, G_TYPE_OBJECT) static void purple_scheduled_task_dispose(GObject *obj) { PurpleScheduledTask *task = PURPLE_SCHEDULED_TASK(obj); g_clear_handle_id(&task->source_id, g_source_remove); G_OBJECT_CLASS(purple_scheduled_task_parent_class)->dispose(obj); } static void purple_scheduled_task_finalize(GObject *obj) { PurpleScheduledTask *task = PURPLE_SCHEDULED_TASK(obj); g_clear_pointer(&task->execute_at, g_date_time_unref); g_clear_pointer(&task->id, g_free); g_clear_pointer(&task->subtitle, g_free); g_clear_object(&task->tags); g_clear_pointer(&task->task_type, g_free); g_clear_pointer(&task->title, g_free); G_OBJECT_CLASS(purple_scheduled_task_parent_class)->finalize(obj); } static void purple_scheduled_task_get_property(GObject *obj, guint param_id, GValue *value, GParamSpec *pspec) { PurpleScheduledTask *task = PURPLE_SCHEDULED_TASK(obj); switch(param_id) { case PROP_CANCELLABLE: g_value_set_boolean(value, purple_scheduled_task_get_cancellable(task)); break; case PROP_EXECUTE_AT: g_value_set_boxed(value, purple_scheduled_task_get_execute_at(task)); break; case PROP_ID: g_value_set_string(value, purple_scheduled_task_get_id(task)); break; case PROP_PERSISTENT: g_value_set_boolean(value, purple_scheduled_task_get_persistent(task)); break; case PROP_STATE: g_value_set_enum(value, purple_scheduled_task_get_state(task)); break; case PROP_SUBTITLE: g_value_set_string(value, purple_scheduled_task_get_subtitle(task)); break; case PROP_TAGS: g_value_set_object(value, purple_scheduled_task_get_tags(task)); break; case PROP_TASK_TYPE: g_value_set_string(value, purple_scheduled_task_get_task_type(task)); break; case PROP_TITLE: g_value_set_string(value, purple_scheduled_task_get_title(task)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); break; } } static void purple_scheduled_task_set_property(GObject *obj, guint param_id, const GValue *value, GParamSpec *pspec) { PurpleScheduledTask *task = PURPLE_SCHEDULED_TASK(obj); switch(param_id) { case PROP_CANCELLABLE: purple_scheduled_task_set_cancellable(task, g_value_get_boolean(value)); break; case PROP_ID: purple_scheduled_task_set_id(task, g_value_get_string(value)); break; case PROP_PERSISTENT: purple_scheduled_task_set_persistent(task, g_value_get_boolean(value)); break; case PROP_SUBTITLE: purple_scheduled_task_set_subtitle(task, g_value_get_string(value)); break; case PROP_TASK_TYPE: purple_scheduled_task_set_task_type(task, g_value_get_string(value)); break; case PROP_TITLE: purple_scheduled_task_set_title(task, g_value_get_string(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); break; } } static void purple_scheduled_task_init(PurpleScheduledTask *task) { task->state = PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED; task->tags = purple_tags_new(); } static void purple_scheduled_task_class_init(PurpleScheduledTaskClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); obj_class->dispose = purple_scheduled_task_dispose; obj_class->finalize = purple_scheduled_task_finalize; obj_class->get_property = purple_scheduled_task_get_property; obj_class->set_property = purple_scheduled_task_set_property; /** * PurpleScheduledTask:cancellable: * * Whether or not the task can be cancelled by the user. * * Since: 3.0 */ properties[PROP_CANCELLABLE] = g_param_spec_boolean( "cancellable", NULL, NULL, TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); /** * PurpleScheduledTask:execute-at: * * The date time when the task will be executed. * * Since: 3.0 */ properties[PROP_EXECUTE_AT] = g_param_spec_boxed( "execute-at", NULL, NULL, G_TYPE_DATE_TIME, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * PurpleScheduledTask:id: * * The id of the task. * * This is primarily for internal use. * * Since: 3.0 */ properties[PROP_ID] = g_param_spec_string( "id", NULL, NULL, NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); /** * PurpleScheduledTask:persistent: * * Whether or not the task should remembered across invocations of the * program. * * Since: 3.0 */ properties[PROP_PERSISTENT] = g_param_spec_boolean( "persistent", NULL, NULL, FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); /** * PurpleScheduledTask:state: * * The state of the task. * * Since: 3.0 */ properties[PROP_STATE] = g_param_spec_enum( "state", NULL, NULL, PURPLE_TYPE_SCHEDULED_TASK_STATE, PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); /** * PurpleScheduledTask:subtitle: * * The subtitle for the task. * * Since: 3.0 */ properties[PROP_SUBTITLE] = g_param_spec_string( "subtitle", NULL, NULL, NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); /** * PurpleScheduledTask:tags: * * The tags for the task. * * Since: 3.0 */ properties[PROP_TAGS] = g_param_spec_object( "tags", NULL, NULL, PURPLE_TYPE_TAGS, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * PurpleScheduledTask:task-type: * * The type of the task. * * Since: 3.0 */ properties[PROP_TASK_TYPE] = g_param_spec_string( "task-type", NULL, NULL, NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); /** * PurpleScheduledTask:title: * * The title of the task. * * Since: 3.0 */ properties[PROP_TITLE] = g_param_spec_string( "title", NULL, NULL, NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); g_object_class_install_properties(obj_class, N_PROPERTIES, properties); /** * PurpleScheduledTask::execute: * @task: the instance * * This signal is emitted the task is executing. * * The execution of the task is scheduled with * [method@ScheduledTask.schedule]. * * Since: 3.0 */ signals[SIG_EXECUTE] = g_signal_new_class_handler( "execute", G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, NULL, NULL, NULL, NULL, G_TYPE_NONE, 0); } /****************************************************************************** * Public API *****************************************************************************/ void purple_scheduled_task_cancel(PurpleScheduledTask *task) { GObject *obj = NULL; g_return_if_fail(PURPLE_IS_SCHEDULED_TASK(task)); if(task->state != PURPLE_SCHEDULED_TASK_STATE_SCHEDULED) { return; } if(!task->cancellable) { return; } g_clear_pointer(&task->execute_at, g_date_time_unref); g_clear_handle_id(&task->source_id, g_source_remove); task->state = PURPLE_SCHEDULED_TASK_STATE_CANCELLED; obj = G_OBJECT(task); g_object_freeze_notify(obj); g_object_notify_by_pspec(obj, properties[PROP_EXECUTE_AT]); g_object_notify_by_pspec(obj, properties[PROP_STATE]); g_object_thaw_notify(obj); } gboolean purple_scheduled_task_get_cancellable(PurpleScheduledTask *task) { g_return_val_if_fail(PURPLE_IS_SCHEDULED_TASK(task), FALSE); /* execute-at gets set to NULL when executed, so if it's NULL we can't * cancel a task that's already been executed. */ if(task->cancellable && task->execute_at == NULL) { return FALSE; } return task->cancellable; } GDateTime * purple_scheduled_task_get_execute_at(PurpleScheduledTask *task) { g_return_val_if_fail(PURPLE_IS_SCHEDULED_TASK(task), NULL); return task->execute_at; } const char * purple_scheduled_task_get_id(PurpleScheduledTask *task) { g_return_val_if_fail(PURPLE_IS_SCHEDULED_TASK(task), NULL); /* If we weren't given an ID at construction generate one on the fly. */ if(birb_str_is_empty(task->id)) { task->id = g_uuid_string_random(); } return task->id; } gboolean purple_scheduled_task_get_persistent(PurpleScheduledTask *task) { g_return_val_if_fail(PURPLE_IS_SCHEDULED_TASK(task), FALSE); return task->persistent; } PurpleScheduledTaskState purple_scheduled_task_get_state(PurpleScheduledTask *task) { g_return_val_if_fail(PURPLE_IS_SCHEDULED_TASK(task), PURPLE_SCHEDULED_TASK_STATE_UNSCHEDULED); return task->state; } const char * purple_scheduled_task_get_subtitle(PurpleScheduledTask *task) { g_return_val_if_fail(PURPLE_IS_SCHEDULED_TASK(task), NULL); return task->subtitle; } PurpleTags * purple_scheduled_task_get_tags(PurpleScheduledTask *task) { g_return_val_if_fail(PURPLE_IS_SCHEDULED_TASK(task), NULL); return task->tags; } const char * purple_scheduled_task_get_task_type(PurpleScheduledTask *task) { g_return_val_if_fail(PURPLE_IS_SCHEDULED_TASK(task), NULL); return task->task_type; } const char * purple_scheduled_task_get_title(PurpleScheduledTask *task) { g_return_val_if_fail(PURPLE_IS_SCHEDULED_TASK(task), NULL); return task->title; } PurpleScheduledTask * purple_scheduled_task_new(const char *task_type, const char *title, gboolean cancellable) { g_return_val_if_fail(!birb_str_is_empty(task_type), NULL); g_return_val_if_fail(!birb_str_is_empty(title), NULL); return g_object_new( PURPLE_TYPE_SCHEDULED_TASK, "cancellable", cancellable, "task-type", task_type, "title", title, NULL); } gboolean purple_scheduled_task_schedule(PurpleScheduledTask *task, GDateTime *execute_at, GError **error) { GDateTime *now = NULL; GObject *obj = NULL; GTimeSpan difference = 0; g_return_val_if_fail(PURPLE_IS_SCHEDULED_TASK(task), FALSE); g_return_val_if_fail(execute_at != NULL, FALSE); if(task->state == PURPLE_SCHEDULED_TASK_STATE_EXECUTING) { g_set_error(error, PURPLE_SCHEDULED_TASK_ERROR, PURPLE_SCHEDULED_TASK_ERROR_RESCHEDULE_EXECUTING_TASK, "can not reschedule a task that is currently executing"); return FALSE; } if(task->state == PURPLE_SCHEDULED_TASK_STATE_SCHEDULED) { purple_scheduled_task_cancel(task); } /* Check if our execute_at is valid. */ now = g_date_time_new_now_local(); difference = g_date_time_difference(execute_at, now); g_date_time_unref(now); if(difference < 0) { char *iso8601 = g_date_time_format_iso8601(execute_at); g_set_error(error, PURPLE_SCHEDULED_TASK_ERROR, PURPLE_SCHEDULED_TASK_ERROR_EXECUTE_AT_IN_PAST, "%s is in the past", iso8601); g_free(iso8601); return FALSE; } /* Save the execute_at. */ g_clear_pointer(&task->execute_at, g_date_time_unref); task->execute_at = g_date_time_ref(execute_at); task->source_id = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, difference / G_TIME_SPAN_SECOND, purple_scheduled_task_timeout_cb, g_object_ref(task), g_object_unref); task->state = PURPLE_SCHEDULED_TASK_STATE_SCHEDULED; obj = G_OBJECT(task); g_object_freeze_notify(obj); g_object_notify_by_pspec(obj, properties[PROP_EXECUTE_AT]); g_object_notify_by_pspec(obj, properties[PROP_STATE]); g_object_thaw_notify(obj); return TRUE; } gboolean purple_scheduled_task_schedule_relative(PurpleScheduledTask *task, GTimeSpan when, GError **error) { GDateTime *now = NULL; GDateTime *execute_at = NULL; gboolean ret = FALSE; g_return_val_if_fail(PURPLE_IS_SCHEDULED_TASK(task), FALSE); now = g_date_time_new_now_local(); execute_at = g_date_time_add(now, when); g_date_time_unref(now); ret = purple_scheduled_task_schedule(task, execute_at, error); g_date_time_unref(execute_at); return ret; } void purple_scheduled_task_set_persistent(PurpleScheduledTask *task, gboolean persistent) { g_return_if_fail(PURPLE_IS_SCHEDULED_TASK(task)); if(task->persistent != persistent) { task->persistent = persistent; g_object_notify_by_pspec(G_OBJECT(task), properties[PROP_PERSISTENT]); } } void purple_scheduled_task_set_subtitle(PurpleScheduledTask *task, const char *subtitle) { g_return_if_fail(PURPLE_IS_SCHEDULED_TASK(task)); if(g_set_str(&task->subtitle, subtitle)) { g_object_notify_by_pspec(G_OBJECT(task), properties[PROP_SUBTITLE]); } }