/*
 * Copyright © 2025 Red Hat, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#pragma once

#include "config.h"

#include <libevdev/libevdev.h>
#include <libudev.h>
#include <stdbool.h>
#include <stdint.h>

#include "util-time.h"

/* Forward declarations instead of #includes to make
 * this header self-contained (bindgen, etc.) */
struct evdev_frame;
enum evdev_usage;
struct libinput;
struct libinput_device;
struct libinput_tablet_tool;
struct libinput_plugin;
enum libinput_log_priority;
enum libinput_feature;

#define plugin_log_debug(p_, ...) plugin_log_msg((p_), LIBINPUT_LOG_PRIORITY_DEBUG, __VA_ARGS__)
#define plugin_log_info(p_, ...) plugin_log_msg((p_), LIBINPUT_LOG_PRIORITY_INFO, __VA_ARGS__)
#define plugin_log_error(p_, ...) plugin_log_msg((p_), LIBINPUT_LOG_PRIORITY_ERROR, __VA_ARGS__)
#define plugin_log_bug_kernel(p_, ...) plugin_log_msg((p_), LIBINPUT_LOG_PRIORITY_ERROR, "kernel bug: " __VA_ARGS__)
#define plugin_log_bug_libinput(p_, ...) plugin_log_msg((p_), LIBINPUT_LOG_PRIORITY_ERROR, "libinput bug: " __VA_ARGS__)
#define plugin_log_bug_client(p_, ...) plugin_log_msg((p_), LIBINPUT_LOG_PRIORITY_ERROR, "client bug: " __VA_ARGS__)
#define plugin_log_bug(p_, ...) plugin_log_msg((p_), LIBINPUT_LOG_PRIORITY_ERROR, "plugin bug: " __VA_ARGS__)

void
plugin_log_msg(struct libinput_plugin *plugin,
	       enum libinput_log_priority priority,
	       const char *format,
	       ...);

struct libinput_plugin_interface {
	void (*run)(struct libinput_plugin *plugin);
	/**
	 * Notification that the plugin is about to be destroyed.
	 * When this function is called, the plugin has already
	 * been unregistered. The plugin should free any
	 * resources allocated but not the struct libinput_plugin
	 * itself.
	 */
	void (*destroy)(struct libinput_plugin *plugin);
	/**
	 * Notification about a newly added device that has **not** yet
	 * been added by libinput as struct libinput_device.
	 */
	void (*device_new)(struct libinput_plugin *plugin,
			   struct libinput_device *device,
			   struct libevdev *evdev,
			   struct udev_device *udev_device);
	/**
	 * Notification that a device (previously announced with device_new)
	 * was ignored by libinput and was **never** added as struct
	 * libinput_device.
	 *
	 * If a device was added (device_added) then this callback will
	 * not be called for that device.
	 */
	void (*device_ignored)(struct libinput_plugin *plugin,
			       struct libinput_device *device);
	/**
	 * Notification that a device was added to libinput. Called
	 * after the device_new callback if the device matches libinput's
	 * expectations.
	 */
	void (*device_added)(struct libinput_plugin *plugin,
			     struct libinput_device *device);
	/**
	 * Notification that a previously added device was removed.
	 */
	void (*device_removed)(struct libinput_plugin *plugin,
			       struct libinput_device *device);
	/**
	 * Notification that a device submitted a frame event.
	 */
	void (*evdev_frame)(struct libinput_plugin *plugin,
			    struct libinput_device *device,
			    struct evdev_frame *frame);

	/**
	 * Notification that a configuration option on a tool
	 * was modified.
	 */
	void (*tool_configured)(struct libinput_plugin *plugin,
				struct libinput_tablet_tool *tool);

	/**
	 * Notification that the given feature was disabled on
	 * the given device.
	 */
	void (*feature_disabled)(struct libinput_plugin *plugin,
				 struct libinput_device *device,
				 enum libinput_feature feature);
};

/**
 * Returns a new plugin with the given interface and, optionally,
 * the user data. The returned plugin has a refcount of at least 1
 * and must be unref'd by the caller.
 * Should an error occur, the plugin must be unregistered by
 * the caller:
 *
 * ```
 * struct libinput_plugin *plugin = libinput_plugin_new(libinput, ...);
 * if (some_error_condition) {
 *     libinput_plugin_unregister(plugin);
 * }
 * libinput_plugin_unref(plugin);
 * ```
 */
struct libinput_plugin *
libinput_plugin_new(struct libinput *libinput,
		    const char *name,
		    const struct libinput_plugin_interface *interface,
		    void *user_data);

const char *
libinput_plugin_get_name(struct libinput_plugin *plugin);

struct libinput *
libinput_plugin_get_context(struct libinput_plugin *plugin);

void
libinput_plugin_unregister(struct libinput_plugin *plugin);

void
libinput_plugin_set_user_data(struct libinput_plugin *plugin, void *user_data);
void *
libinput_plugin_get_user_data(struct libinput_plugin *plugin);

struct libinput_plugin *
libinput_plugin_ref(struct libinput_plugin *plugin);

struct libinput_plugin *
libinput_plugin_unref(struct libinput_plugin *plugin);

#ifdef DEFINE_UNREF_CLEANUP_FUNC
DEFINE_UNREF_CLEANUP_FUNC(libinput_plugin);
#endif

void
libinput_plugin_enable_device_event_frame(struct libinput_plugin *plugin,
					  struct libinput_device *device,
					  bool enable);

void
libinput_plugin_disable_device_feature(struct libinput_plugin *plugin,
				       struct libinput_device *device,
				       enum libinput_feature feature);

/**
 * Mask this plugin's evdev_frame function to be called only
 * if the frame **contains** the given evdev usage. Plugins
 * that e.g. only care about button events should use this function
 * to avoid being called for every motion event.
 *
 * By default the mask includes all events. Calling this function
 * changes the behavior to *only* include frames with the usages.
 *
 * Note that the frame passed to evdev_frame function contains all
 * events of that frame (i.e. including events that are not specified
 * by this mask).
 */
void
libinput_plugin_enable_evdev_usage(struct libinput_plugin *plugin,
				   enum evdev_usage usage);

/**
 * Inject a new event frame from the given plugin. This
 * frame is treated as if it was just sent by the kernel's
 * event node and is processed immediately, interrupting
 * any other processing from this device.
 *
 * This function can be called any time but unlike
 * libinput_plugin_append_evdev_frame() and
 * libinput_plugin_prepend_evdev_frame() it starts
 * processing the frame at the bottom of the plugin
 * stack.
 *
 * The injected event will also be sent to
 * the current plugin, a guard needs to be in
 * place to prevent recursion.
 *
 * Injecting events may cause other plugins to
 * behave unexpectedly (see below). In almost all cases
 * it is better to use libinput_plugin_append_evdev_frame()
 * or libinput_plugin_prepend_evdev_frame() to only
 * affect the plugins logically *after* this
 * plugin.
 *
 * Assuming several plugins P1, P2, and P3 and
 * an event frame E, and P2 calling this function
 * with an injected event I:
 * - P1: receives E, optionally modifies E
 * - P2: receives E, injects I
 *   - P1: receives I, optionally modifies I
 *   - P2: receives I, optionally modifies I
 *   - P3: receives I, optionally modifies I
 * - P2: continues processing E, optionally modifies E
 * - P3: receives E, optionally modifies E
 *
 * For P1 the injected event I is received after E,
 * for P3 the injected event I is received before E.
 *
 * An example for event injection being harmful:
 * A plugin may monitor tablet proximity state and prepent
 * proximity-in events if the tablet does not send proximity-in
 * events. This plugin stops monitoring events once it sees correct
 * proximity-in events.
 * If another plugin were to inject a proximity event (e.g. to fake a
 * different tool coming into proximity), the plugin would stop
 * monitoring. Future proximity events from the tablet will then
 * have the wrong proximity.
 * This can be avoided by appending or prepending the events instead
 * of injecting them.
 */
void
libinput_plugin_inject_evdev_frame(struct libinput_plugin *libinput,
				   struct libinput_device *device,
				   struct evdev_frame *frame);

/**
 * Queue an event frame for the next plugin in sequence, after
 * the current event frame being processed.
 *
 * This function can only be called from within the evdev_frame()
 * callback or within a plugin's timer func and may be used to
 * queue new frames. If called multiple times with frames F1, F2, ...
 * while processing the current frame C, frame sequence to be passed
 * to the next plugin is C, F1, F2, ...
 *
 * Assuming several plugins P1, P2, P3, and P4 and an event frame E,
 * and P2 and P3 calling this function with an queued event frame Q1 and Q2,
 * respectively.
 *
 * - P1: receives E, optionally modifies E
 * - P2: receives E, appends Q1, optionally modifies E
 * - P3: receives E, appends Q2, optionally modifies E
 * - P3: receives Q1, optionally modifies Q1
 * - P4: receives E, optionally modifies E
 * - P4: receives Q2, optionally modifies Q2
 * - P4: receives Q1, optionally modifies Q1
 *
 * Once plugin processing is complete, the event sequence passed
 * back to libinput is thus [E, Q2, Q1].
 *
 * To discard the original event frame, the plugin needs to
 * call evdev_frame_reset() on the frame passed to it.
 *
 * It is a plugin bug to call this function from outside the
 * evdev_frame() callback or a timer callback.
 *
 * If called within a plugin's timer callback, any frames generated by
 * the plugin will only be seen by plugins after this plugin. These
 * frames will be processed in the usual evdev_fame() callback and there
 * is no indication that the events were queued from within a timer
 * callback. Using the above example:
 *
 * - P1: <idle>
 * - P2: timer callback invoked, appends Q1
 * - P3: receives Q1, appends Q2, optionally modifies Q1
 * - P4: receives Q2, optionally modifies Q2
 * - P4: receives Q1, optionally modifies Q1
 *
 * Because there is no current frame during a timer callback
 * libinput_plugin_append_evdev_frame() and
 * libinput_plugin_prepend_evdev_frame() are functionally equivalent.
 * If both functions are used, all events from
 * libinput_plugin_prepend_evdev_frame() will be queued before
 * events from libinput_plugin_append_evdev_frame().
 */
void
libinput_plugin_append_evdev_frame(struct libinput_plugin *libinput,
				   struct libinput_device *device,
				   struct evdev_frame *frame);

/**
 * Identical to libinput_plugin_append_evdev_frame(), but prepends
 * the event frame to the current event frame being processed.
 * If called multiple times with frames F1, F2, ... while processing
 * the current frame C, frame sequence to be passed to the next
 * plugin is F1, F2, ..., C
 */
void
libinput_plugin_prepend_evdev_frame(struct libinput_plugin *libinput,
				    struct libinput_device *device,
				    struct evdev_frame *frame);

/**
 * Create a new timer for the given plugin.
 *
 * The timer needs to be set with libinput_plugin_timer_set()
 * before it can be used.
 *
 * The refcount of the returned timer is at least 1, and the caller
 * must call libinput_plugin_timer_unref() to release it.
 *
 * The func given is the callback invoked when the timer expires.
 * It is passed the user_data given here or in a subsequent
 * call to libinput_plugin_timer_set_user_data().
 *
 * Note that event generating inside a timer is subject to
 * specific behaviors, see the documentation
 * to libinput_plugin_append_evdev_frame(), libinput_plugin_prepend_evdev_frame()
 * and libinput_plugin_inject_evdev_frame() for details.
 */
struct libinput_plugin_timer *
libinput_plugin_timer_new(struct libinput_plugin *plugin,
			  const char *name,
			  void (*func)(struct libinput_plugin *plugin,
				       usec_t now,
				       void *user_data),
			  void *user_data);

struct libinput_plugin_timer *
libinput_plugin_timer_ref(struct libinput_plugin_timer *timer);

struct libinput_plugin_timer *
libinput_plugin_timer_unref(struct libinput_plugin_timer *timer);

#ifdef DEFINE_UNREF_CLEANUP_FUNC
DEFINE_UNREF_CLEANUP_FUNC(libinput_plugin_timer);
#endif

/* Set timer expire time, in absolute us CLOCK_MONOTONIC */
void
libinput_plugin_timer_set(struct libinput_plugin_timer *timer, usec_t expire);

void
libinput_plugin_timer_set_user_data(struct libinput_plugin_timer *timer,
				    void *user_data);
void *
libinput_plugin_timer_get_user_data(struct libinput_plugin_timer *timer);

void
libinput_plugin_timer_cancel(struct libinput_plugin_timer *timer);
