# HG changeset patch
# Node ID 4d3c98d1f1a84c3e6a2c75c08cd0ea798b1a0f2b
# Parent  2cd48d29fc4ec98fea0e7a6dd8803ea9d80358ce
diff --git a/ChangeLog b/ChangeLog
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,4 @@
-0.11.3: ????-??-??
+0.12.0: ????-??-??
   * Nothing yet, be the first!
 
 0.11.2: 2025-02-04
diff --git a/ibis/ibismodechange.c b/ibis/ibismodechange.c
new file mode 100644
--- /dev/null
+++ b/ibis/ibismodechange.c
@@ -0,0 +1,62 @@
+/*
+ * Ibis - IRCv3 Library
+ * Copyright (C) 2022-2025 Ibis Developers
+ *
+ * Ibis 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 "ibismodechange.h"
+
+G_DEFINE_BOXED_TYPE(IbisModeChange,
+                    ibis_mode_change,
+                    ibis_mode_change_copy,
+                    ibis_mode_change_free);
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+IbisModeChange *
+ibis_mode_change_copy(IbisModeChange *mode_change) {
+	if(mode_change == NULL) {
+		return NULL;
+	}
+
+	return ibis_mode_change_new(mode_change->mode, mode_change->add,
+	                            mode_change->parameter);
+}
+
+void
+ibis_mode_change_free(IbisModeChange *mode_change) {
+	g_return_if_fail(mode_change != NULL);
+
+	g_free(mode_change->parameter);
+	g_free(mode_change);
+}
+
+IbisModeChange *
+ibis_mode_change_new(const char mode, gboolean add, const char *parameter) {
+	IbisModeChange *mode_change = NULL;
+
+	mode_change = g_new(IbisModeChange, 1);
+
+	mode_change->mode = mode;
+	mode_change->add = add;
+	mode_change->parameter = g_strdup(parameter);
+
+	return mode_change;
+}
diff --git a/ibis/ibismodechange.h b/ibis/ibismodechange.h
new file mode 100644
--- /dev/null
+++ b/ibis/ibismodechange.h
@@ -0,0 +1,101 @@
+/*
+ * Ibis - IRCv3 Library
+ * Copyright (C) 2022-2025 Ibis Developers
+ *
+ * Ibis 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/>.
+ */
+
+#if !defined(IBIS_GLOBAL_HEADER_INSIDE) && !defined(IBIS_COMPILATION)
+# error "only <ibis.h> may be included directly"
+#endif
+
+#ifndef IBIS_MODE_CHANGE_H
+#define IBIS_MODE_CHANGE_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "ibisversion.h"
+
+G_BEGIN_DECLS
+
+#define IBIS_TYPE_MODE_CHANGE (ibis_mode_change_get_type());
+
+IBIS_AVAILABLE_IN_0_12
+GType ibis_mode_change_get_type(void) G_GNUC_CONST;
+
+typedef struct _IbisModeChange IbisModeChange;
+
+/**
+ * IbisModeChange:
+ * @mode: the mode character
+ * @add: true if this mode is being added, false if being removed
+ * @parameter: the parameter if the mode requires one
+ *
+ * A data structure describing a single mode change.
+ *
+ * Since: 0.12
+ */
+IBIS_AVAILABLE_TYPE_IN_0_12
+struct _IbisModeChange {
+	char mode;
+	gboolean add;
+	char *parameter;
+};
+
+/**
+ * ibis_mode_change_copy:
+ * @mode_change: the instance to copy
+ *
+ * Creates a copy of a mode change.
+ *
+ * Returns: (transfer full): The new copy.
+ *
+ * Since: 0.12
+ */
+IBIS_AVAILABLE_IN_0_12
+IbisModeChange *ibis_mode_change_copy(IbisModeChange *mode_change);
+
+/**
+ * ibis_mode_change_free:
+ *
+ * Frees a mode change.
+ *
+ * Since: 0.12
+ */
+IBIS_AVAILABLE_IN_0_12
+void ibis_mode_change_free(IbisModeChange *mode_change);
+
+/**
+ * ibis_mode_change_new:
+ * @mode: the mode character
+ * @add: true if this mode is being added; otherwise false
+ * @parameter: (nullable): an optional parameter
+ *
+ * Creates a new mode.
+ *
+ * Returns: (transfer full): The new mode.
+ *
+ * Since: 0.12
+ */
+IBIS_AVAILABLE_IN_0_12
+IbisModeChange *ibis_mode_change_new(const char mode, gboolean add, const char *parameter);
+
+G_END_DECLS
+
+#endif /* IBIS_MODE_CHANGE_H */
diff --git a/ibis/ibisversion.h b/ibis/ibisversion.h
--- a/ibis/ibisversion.h
+++ b/ibis/ibisversion.h
@@ -389,6 +389,30 @@
 #define IBIS_AVAILABLE_TYPE_IN_0_11
 #endif
 
+/**
+ * IBIS_VERSION_0_12:
+ *
+ * A macro that evaluates to the 0.12 version of ibis, in a format that can be
+ * used by the C pre-processor.
+ *
+ * Since: 0.12
+ */
+#define IBIS_VERSION_0_12 (G_ENCODE_VERSION(0, 12))
+
+#if IBIS_VERSION_MAX_ALLOWED < IBIS_VERSION_0_12
+#define IBIS_AVAILABLE_IN_0_12 IBIS_UNAVAILABLE(0, 12)
+#define IBIS_AVAILABLE_STATIC_INLINE_IN_0_12 IBIS_UNAVAILABLE_STATIC_INLINE(0, 12)
+#define IBIS_AVAILABLE_MACRO_IN_0_12 IBIS_UNAVAILABLE_MACRO(0, 12)
+#define IBIS_AVAILABLE_ENUMERATOR_IN_0_12 IBIS_UNAVAILABLE_ENUMERATOR(0, 12)
+#define IBIS_AVAILABLE_TYPE_IN_0_12 IBIS_UNAVAILABLE_TYPE(0, 12)
+#else
+#define IBIS_AVAILABLE_IN_0_12 _IBIS_EXTERN
+#define IBIS_AVAILABLE_STATIC_INLINE_IN_0_12
+#define IBIS_AVAILABLE_MACRO_IN_0_12
+#define IBIS_AVAILABLE_ENUMERATOR_IN_0_12
+#define IBIS_AVAILABLE_TYPE_IN_0_12
+#endif
+
 G_BEGIN_DECLS
 
 /**
diff --git a/ibis/meson.build b/ibis/meson.build
--- a/ibis/meson.build
+++ b/ibis/meson.build
@@ -5,6 +5,7 @@
   'ibisfeatures.c',
   'ibisformatting.c',
   'ibismessage.c',
+  'ibismodechange.c',
   'ibisnormalize.c',
   'ibissasl.c',
   'ibissource.c',
@@ -22,6 +23,7 @@
   'ibisfeatures.h',
   'ibisformatting.h',
   'ibismessage.h',
+  'ibismodechange.h',
   'ibisnormalize.h',
   'ibissasl.h',
   'ibissource.h',
diff --git a/ibis/tests/meson.build b/ibis/tests/meson.build
--- a/ibis/tests/meson.build
+++ b/ibis/tests/meson.build
@@ -4,6 +4,7 @@
   'features',
   'formatting',
   'message',
+  'mode_change',
   'normalize',
   'parser',
   'source',
diff --git a/ibis/tests/test_mode_change.c b/ibis/tests/test_mode_change.c
new file mode 100644
--- /dev/null
+++ b/ibis/tests/test_mode_change.c
@@ -0,0 +1,73 @@
+/*
+ * Ibis - IRCv3 Library
+ * Copyright (C) 2022-2025 Ibis Developers
+ *
+ * Ibis 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 Lesser 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
+ * 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 <glib.h>
+
+#include <ibis.h>
+
+/******************************************************************************
+ * Tests
+ *****************************************************************************/
+static void
+test_ibis_mode_change_new(void) {
+	IbisModeChange *mode_change = NULL;
+
+	mode_change = ibis_mode_change_new('o', TRUE, "grim");
+
+	g_assert_nonnull(mode_change);
+	g_assert_cmpint(mode_change->mode, ==, 'o');
+	g_assert_true(mode_change->add);
+	g_assert_cmpstr(mode_change->parameter, ==, "grim");
+
+	ibis_mode_change_free(mode_change);
+}
+
+static void
+test_ibis_mode_change_copy(void) {
+	IbisModeChange *mode_change = NULL;
+	IbisModeChange *copy = NULL;
+
+	mode_change = ibis_mode_change_new('o', TRUE, "grim");
+
+	copy = ibis_mode_change_copy(mode_change);
+	ibis_mode_change_free(mode_change);
+
+	g_assert_nonnull(copy);
+	g_assert_cmpint(copy->mode, ==, 'o');
+	g_assert_true(copy->add);
+	g_assert_cmpstr(copy->parameter, ==, "grim");
+
+	ibis_mode_change_free(copy);
+}
+
+/******************************************************************************
+ * Main
+ *****************************************************************************/
+int
+main(int argc, char *argv[]) {
+	g_test_init(&argc, &argv, NULL);
+
+	g_test_add_func("/ibis/mode-change/new", test_ibis_mode_change_new);
+	g_test_add_func("/ibis/mode-change/copy", test_ibis_mode_change_copy);
+
+	return g_test_run();
+}
diff --git a/meson.build b/meson.build
--- a/meson.build
+++ b/meson.build
@@ -2,7 +2,7 @@
   default_options : ['c_std=c17', 'warning_level=2'],
   license : 'LGPL-2.1-or-later',
   meson_version : '>=1.0.0',
-  version : '0.11.3-dev')
+  version : '0.12.0-dev')
 
 toplevel_inc = include_directories('.')
 
