/*
 * This an unstable interface of wlroots. No guarantees are made regarding the
 * future consistency of this API.
 */
#ifndef WLR_USE_UNSTABLE
#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
#endif

#ifndef WLR_TYPES_WLR_CURSOR_H
#define WLR_TYPES_WLR_CURSOR_H

#include <wayland-server-core.h>
#include <wlr/types/wlr_output_layout.h>

struct wlr_input_device;
struct wlr_surface;
struct wlr_xcursor_manager;

/**
 * wlr_cursor implements the behavior of the "cursor", that is, the image on the
 * screen typically moved about with a mouse or so. It provides tracking for
 * this in global coordinates, and integrates with struct wlr_output,
 * struct wlr_output_layout, and struct wlr_input_device. You can use it to
 * abstract multiple input devices over a single cursor, constrain cursor
 * movement to the usable area of a struct wlr_output_layout and communicate
 * position updates to the hardware cursor, constrain specific input devices to
 * specific outputs or regions of the screen, and so on.
 */

struct wlr_box;
struct wlr_cursor_state;

struct wlr_cursor {
	struct wlr_cursor_state *state;
	double x, y;

	/**
	 * The interpretation of these signals is the responsibility of the
	 * compositor, but some helpers are provided for your benefit. If you
	 * receive a relative motion event, for example, you may want to call
	 * wlr_cursor_move(). If you receive an absolute event, call
	 * wlr_cursor_warp_absolute(). If you pass an input device into these
	 * functions, it will apply the region/output constraints associated with
	 * that device to the resulting cursor motion. If an output layout is
	 * attached, these functions will constrain the resulting cursor motion to
	 * within the usable space of the output layout.
	 *
	 * Re-broadcasting these signals to, for example, a struct wlr_seat, is also
	 * your responsibility.
	 */
	struct {
		struct wl_signal motion; // struct wlr_pointer_motion_event
		struct wl_signal motion_absolute; // struct wlr_pointer_motion_absolute_event
		struct wl_signal button; // struct wlr_pointer_button_event
		struct wl_signal axis; // struct wlr_pointer_axis_event
		struct wl_signal frame;
		struct wl_signal swipe_begin; // struct wlr_pointer_swipe_begin_event
		struct wl_signal swipe_update; // struct wlr_pointer_swipe_update_event
		struct wl_signal swipe_end; // struct wlr_pointer_swipe_end_event
		struct wl_signal pinch_begin; // struct wlr_pointer_pinch_begin_event
		struct wl_signal pinch_update; // struct wlr_pointer_pinch_update_event
		struct wl_signal pinch_end; // struct wlr_pointer_pinch_end_event
		struct wl_signal hold_begin; // struct wlr_pointer_hold_begin_event
		struct wl_signal hold_end; // struct wlr_pointer_hold_end_event

		struct wl_signal touch_up; // struct wlr_touch_up_event
		struct wl_signal touch_down; // struct wlr_touch_down_event
		struct wl_signal touch_motion; // struct wlr_touch_motion_event
		struct wl_signal touch_cancel; // struct wlr_touch_cancel_event
		struct wl_signal touch_frame;

		struct wl_signal tablet_tool_axis; // struct wlr_tablet_tool_axis_event
		struct wl_signal tablet_tool_proximity; // struct wlr_tablet_tool_proximity_event
		struct wl_signal tablet_tool_tip; // struct wlr_tablet_tool_tip_event
		struct wl_signal tablet_tool_button; // struct wlr_tablet_tool_button_event
	} events;

	void *data;
};

struct wlr_cursor *wlr_cursor_create(void);

void wlr_cursor_destroy(struct wlr_cursor *cur);

/**
 * Warp the cursor to the given x and y in layout coordinates. If x and y are
 * out of the layout boundaries or constraints, no warp will happen.
 *
 * `dev` may be passed to respect device mapping constraints. If `dev` is NULL,
 * device mapping constraints will be ignored.
 *
 * Returns true when the cursor warp was successful.
 */
bool wlr_cursor_warp(struct wlr_cursor *cur, struct wlr_input_device *dev,
	double lx, double ly);

/**
 * Convert absolute 0..1 coordinates to layout coordinates.
 *
 * `dev` may be passed to respect device mapping constraints. If `dev` is NULL,
 * device mapping constraints will be ignored.
 */
void wlr_cursor_absolute_to_layout_coords(struct wlr_cursor *cur,
	struct wlr_input_device *dev, double x, double y, double *lx, double *ly);


/**
 * Warp the cursor to the given x and y coordinates. If the given point is out
 * of the layout boundaries or constraints, the closest point will be used.
 * If one coordinate is NAN, it will be ignored.
 *
 * `dev` may be passed to respect device mapping constraints. If `dev` is NULL,
 * device mapping constraints will be ignored.
 */
void wlr_cursor_warp_closest(struct wlr_cursor *cur,
	struct wlr_input_device *dev, double x, double y);

/**
 * Warp the cursor to the given x and y in absolute 0..1 coordinates. If the
 * given point is out of the layout boundaries or constraints, the closest point
 * will be used. If one coordinate is NAN, it will be ignored.
 *
 * `dev` may be passed to respect device mapping constraints. If `dev` is NULL,
 * device mapping constraints will be ignored.
 */
void wlr_cursor_warp_absolute(struct wlr_cursor *cur,
	struct wlr_input_device *dev, double x, double y);

/**
 * Move the cursor in the direction of the given x and y layout coordinates. If
 * one coordinate is NAN, it will be ignored.
 *
 * `dev` may be passed to respect device mapping constraints. If `dev` is NULL,
 * device mapping constraints will be ignored.
 */
void wlr_cursor_move(struct wlr_cursor *cur, struct wlr_input_device *dev,
	double delta_x, double delta_y);

/**
 * Set the cursor buffer.
 *
 * The buffer is used on all outputs and is scaled accordingly. The hotspot is
 * expressed in logical coordinates. A NULL buffer hides the cursor.
 */
void wlr_cursor_set_buffer(struct wlr_cursor *cur, struct wlr_buffer *buffer,
	int32_t hotspot_x, int32_t hotspot_y, float scale);

/**
 * Hide the cursor image.
 */
void wlr_cursor_unset_image(struct wlr_cursor *cur);

/**
 * Set the cursor image from an XCursor theme.
 *
 * The image will be loaded from the struct wlr_xcursor_manager.
 */
void wlr_cursor_set_xcursor(struct wlr_cursor *cur,
	struct wlr_xcursor_manager *manager, const char *name);

/**
 * Set the cursor surface. The surface can be committed to update the cursor
 * image. The surface position is subtracted from the hotspot. A NULL surface
 * commit hides the cursor.
 */
void wlr_cursor_set_surface(struct wlr_cursor *cur, struct wlr_surface *surface,
	int32_t hotspot_x, int32_t hotspot_y);

/**
 * Attaches this input device to this cursor. The input device must be one of:
 *
 * - WLR_INPUT_DEVICE_POINTER
 * - WLR_INPUT_DEVICE_TOUCH
 * - WLR_INPUT_DEVICE_TABLET
 */
void wlr_cursor_attach_input_device(struct wlr_cursor *cur,
		struct wlr_input_device *dev);

void wlr_cursor_detach_input_device(struct wlr_cursor *cur,
		struct wlr_input_device *dev);
/**
 * Uses the given layout to establish the boundaries and movement semantics of
 * this cursor. Cursors without an output layout allow infinite movement in any
 * direction and do not support absolute input events.
 */
void wlr_cursor_attach_output_layout(struct wlr_cursor *cur,
	struct wlr_output_layout *l);

/**
 * Attaches this cursor to the given output, which must be among the outputs in
 * the current output_layout for this cursor. This call is invalid for a cursor
 * without an associated output layout.
 */
void wlr_cursor_map_to_output(struct wlr_cursor *cur,
	struct wlr_output *output);

/**
 * Maps all input from a specific input device to a given output. The input
 * device must be attached to this cursor and the output must be among the
 * outputs in the attached output layout.
 */
void wlr_cursor_map_input_to_output(struct wlr_cursor *cur,
	struct wlr_input_device *dev, struct wlr_output *output);

/**
 * Maps this cursor to an arbitrary region on the associated
 * struct wlr_output_layout.
 */
void wlr_cursor_map_to_region(struct wlr_cursor *cur, const struct wlr_box *box);

/**
 * Maps inputs from this input device to an arbitrary region on the associated
 * struct wlr_output_layout.
 */
void wlr_cursor_map_input_to_region(struct wlr_cursor *cur,
	struct wlr_input_device *dev, const struct wlr_box *box);

#endif
