aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--LIBAXL_REQUEST_CREATE_WINDOW.360
-rw-r--r--Makefile15
-rw-r--r--common.h26
-rw-r--r--libaxl-consts.h17
-rw-r--r--libaxl-display-info.h301
-rw-r--r--libaxl-replies.h4
-rw-r--r--libaxl-requests.h4
-rw-r--r--libaxl-types.h2
-rw-r--r--libaxl.h282
-rw-r--r--libaxl_fileno.c8
-rw-r--r--libaxl_generate_id.c20
-rw-r--r--libaxl_get_decnet_object.c8
-rw-r--r--libaxl_get_tcp_port.c4
-rw-r--r--libaxl_info.c9
-rw-r--r--libaxl_next_depth.c4
-rw-r--r--libaxl_next_screen.c4
-rw-r--r--libaxl_protocol_version.c8
-rw-r--r--libaxl_protocol_version_major.c8
-rw-r--r--libaxl_protocol_version_minor.c8
-rw-r--r--libaxl_receive_handshake.c211
-rw-r--r--libaxl_send_handshake.c115
21 files changed, 1054 insertions, 64 deletions
diff --git a/LIBAXL_REQUEST_CREATE_WINDOW.3 b/LIBAXL_REQUEST_CREATE_WINDOW.3
index e5d64ff..210b5c3 100644
--- a/LIBAXL_REQUEST_CREATE_WINDOW.3
+++ b/LIBAXL_REQUEST_CREATE_WINDOW.3
@@ -23,37 +23,37 @@ LIBAXL_REQUEST_CREATE_WINDOW - Create an unmapped window
#define LIBAXL_REQUEST_CREATE_WINDOW 1
struct libaxl_request_create_window {
- uint8_t \fIopcode\fP;
- uint8_t \fIdepth\fP;
- uint16_t \fI_request_length\fP;
- libaxl_window_t \fIwid\fP;
- libaxl_window_t \fIparent\fP;
- int16_t \fIx\fP;
- int16_t \fIy\fP;
- uint16_t \fIwidth\fP;
- uint16_t \fIheight\fP;
- uint16_t \fIborder_width\fP;
- uint16_t \fIclass\fP;
- libaxl_visual_id_t \fIvisual\fP;
- uint32_t \fIvalue_mask\fP;
+ uint8_t \fIopcode\fP;
+ uint8_t \fIdepth\fP;
+ uint16_t \fI_request_length\fP;
+ libaxl_window_t \fIwid\fP;
+ libaxl_window_t \fIparent\fP;
+ int16_t \fIx\fP;
+ int16_t \fIy\fP;
+ uint16_t \fIwidth\fP;
+ uint16_t \fIheight\fP;
+ uint16_t \fIborder_width\fP;
+ uint16_t \fIclass\fP;
+ libaxl_visual_t \fIvisual\fP;
+ uint32_t \fIvalue_mask\fP;
- libaxl_pixmap_t \fIbackground_pixmap\fP;
- uint32_t \fIbackground_pixel\fP;
- libaxl_pixmap_t \fIborder_pixmap\fP;
- uint32_t \fIborder_pixel\fP;
- uint8_t \fIbit_gravity\fP;
- uint8_t \fIwin_gravity\fP;
- uint8_t \fIbacking_store\fP;
- uint8_t \fI__omitted1\fP;
- uint32_t \fIbacking_planes\fP;
- uint32_t \fIbacking_pixel\fP;
- libaxl_bool_t \fIoverride_redirect\fP;
- libaxl_bool_t \fIsave_under\fP;
- uint16_t \fI__omitted2\fP;
- uint32_t \fIevent_mask\fP;
- uint32_t \fIdo_not_propagate_mask\fP;
- libaxl_colormap_t \fIcolormap\fP;
- libaxl_cursor_t \fIcursor\fP;
+ libaxl_pixmap_t \fIbackground_pixmap\fP;
+ uint32_t \fIbackground_pixel\fP;
+ libaxl_pixmap_t \fIborder_pixmap\fP;
+ uint32_t \fIborder_pixel\fP;
+ uint8_t \fIbit_gravity\fP;
+ uint8_t \fIwin_gravity\fP;
+ uint8_t \fIbacking_store\fP;
+ uint8_t \fI__omitted1\fP;
+ uint32_t \fIbacking_planes\fP;
+ uint32_t \fIbacking_pixel\fP;
+ libaxl_bool_t \fIoverride_redirect\fP;
+ libaxl_bool_t \fIsave_under\fP;
+ uint16_t \fI__omitted2\fP;
+ uint32_t \fIevent_mask\fP;
+ uint32_t \fIdo_not_propagate_mask\fP;
+ libaxl_colormap_t \fIcolormap\fP;
+ libaxl_cursor_t \fIcursor\fP;
};
.fi
.SH DESCRIPTION
diff --git a/Makefile b/Makefile
index aaac76d..d7ea856 100644
--- a/Makefile
+++ b/Makefile
@@ -13,9 +13,21 @@ include $(OSCONFIGFILE)
OBJ =\
libaxl_context_create.o\
libaxl_context_free.o\
+ libaxl_fileno.o\
libaxl_flush.o\
+ libaxl_generate_id.o\
+ libaxl_get_decnet_object.o\
+ libaxl_get_tcp_port.o\
+ libaxl_info.o\
+ libaxl_next_depth.o\
+ libaxl_next_screen.o\
libaxl_parse_display.o\
+ libaxl_protocol_version_major.o\
+ libaxl_protocol_version_minor.o\
+ libaxl_protocol_version.o\
libaxl_receive.o\
+ libaxl_receive_handshake.o\
+ libaxl_send_handshake.o\
libaxl_send_request.o
LIB_HDR =\
@@ -31,7 +43,8 @@ LIB_HDR =\
libaxl-requests.h\
libaxl-requests-ptr-structs.h\
libaxl-requests-structs.h\
- libaxl-types.h
+ libaxl-types.h\
+ libaxl-display-info.h
HDR =\
common.h\
diff --git a/common.h b/common.h
index 2a46bfc..c265cee 100644
--- a/common.h
+++ b/common.h
@@ -9,6 +9,7 @@
#include <fcntl.h>
#include <limits.h>
#include <stdatomic.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -54,18 +55,21 @@ DEFINE_CHECKED(ssize_t, __checked_ssize_t)
#define X_TCP_PORT 6000
-struct libaxl_connection {
- int fd;
- uint16_t last_seqnum;
+struct libaxl_connection { /* TODO add serialisation functions, add close and detach functions */
+ int fd;
+ uint16_t last_seqnum;
LIBAXL_CONNECTION_RWLOCK; /* INIT_LIBAXL_CONNECTION_RWLOCK(&.) */
- LIBAXL_CONTEXT *pending_out;
- size_t in_progress;
- size_t in_buf_size;
- char *in_buf;
- uint32_t xid_base; /* TODO information for these are send by server when connecting { */
- uint32_t xid_bits;
- uint32_t xid_shift; /* } */
- uint8_t request_map[1UL << 16];
+ LIBAXL_CONTEXT *pending_out;
+ size_t in_progress;
+ size_t in_buf_size;
+ char *in_buf;
+ uint32_t xid_base;
+ uint32_t xid_max;
+ uint32_t xid_shift;
+ volatile _Atomic uint32_t xid_last; /* atomic_init(&.xid_last, 0) */
+ uint8_t request_map[1UL << 16];
+ struct libaxl_display_info info;
+ char *info_buf;
};
struct libaxl_context {
diff --git a/libaxl-consts.h b/libaxl-consts.h
index 26c1933..cf6a119 100644
--- a/libaxl-consts.h
+++ b/libaxl-consts.h
@@ -79,6 +79,14 @@
#define LIBAXL_KEYBOARD 1
#define LIBAXL_POINTER 2
+/* struct libaxl_visual_type.class */
+#define LIBAXL_STATIC_GRAY 0
+#define LIBAXL_GRAY_SCALE 1
+#define LIBAXL_STATIC_COLOR 2
+#define LIBAXL_PSEUDO_COLOR 3
+#define LIBAXL_TRUE_COLOR 4
+#define LIBAXL_DIRECT_COLOR 5
+
/* circatulation place */
#define LIBAXL_TOP 0
#define LIBAXL_BOTTOM 1
@@ -208,7 +216,8 @@
#define LIBAXL_INCLUDE_INFERIORS 1
/* backing store */
-#define LIBAXL_NOT_USEFUL 0
+#define LIBAXL_NOT_USEFUL 0 /* Not used in `struct libaxl_screen.backing_stores` */
+#define LIBAXL_NEVER 0 /* Used in `struct libaxl_screen.backing_stores` */
#define LIBAXL_WHEN_MAPPED 1
#define LIBAXL_ALWAYS 2
@@ -230,9 +239,9 @@
#define LIBAXL_NONLINEAR 3
#define LIBAXL_NONLINEAR_VIRTUAL 4
/* window focus details */
-#define LIBAXL_FOCUS_POINTER 5 /* Just called Pointer in the specification*/
-#define LIBAXL_FOCUS_POINTER_ROOT 6 /* Just called PointerRoot in the specification*/
-#define LIBAXL_FOCUS_NONE 7 /* Just called None in the specification*/
+#define LIBAXL_FOCUS_POINTER 5 /* Just called Pointer in the specification */
+#define LIBAXL_FOCUS_POINTER_ROOT 6 /* Just called PointerRoot in the specification */
+#define LIBAXL_FOCUS_NONE 7 /* Just called None in the specification */
/* misc. */
#define LIBAXL_ANY_PROPERTY_TYPE 0
diff --git a/libaxl-display-info.h b/libaxl-display-info.h
new file mode 100644
index 0000000..71cf80f
--- /dev/null
+++ b/libaxl-display-info.h
@@ -0,0 +1,301 @@
+/* See LICENSE file for copyright and license details. */
+
+/**
+ * The highest number of the `struct_version` field in
+ * `struct libaxl_display_info` that this header file
+ * supports
+ */
+#define LIBAXL_DISPLAY_INFO_VERSION 0
+
+/**
+ * Standard byte orders
+ */
+enum libaxl_byte_order {
+ /**
+ * Least significant byte first
+ */
+ LIBAXL_LSB_FIRST,
+
+ /**
+ * Most significant byte first
+ */
+ LIBAXL_MSB_FIRST
+};
+
+/**
+ * Standard bit orders
+ */
+enum libaxl_bit_order {
+ /**
+ * Least significant bit leftmost
+ */
+ LIBAXL_LSB_LEFTMOST,
+
+ /**
+ * Most significant bit leftmost
+ */
+ LIBAXL_MSB_LEFTMOST
+};
+
+struct libaxl_format {
+ uint8_t depth;
+ uint8_t bits_per_pixel;
+ uint8_t scanline_pad;
+ uint8_t __unused[5];
+};
+
+struct libaxl_visual_type {
+ libaxl_visual_t visual_id;
+ uint8_t class; /* LIBAXL_STATIC_GRAY, LIBAXL_GRAY_SCALE, LIBAXL_STATIC_COLOR,
+ * LIBAXL_PSEUDO_COLOR, LIBAXL_TRUE_COLOR, LIBAXL_DIRECT_COLOR */
+ uint8_t bits_per_rgb_value;
+ uint16_t colormap_entries;
+ uint32_t red_mask;
+ uint32_t green_mask;
+ uint32_t blue_mask;
+ uint32_t __unused;
+};
+
+struct libaxl_depth {
+ uint8_t depth;
+ uint8_t __pad;
+ uint16_t number_of_visuals;
+ uint32_t __unused;
+ struct libaxl_visual_type visuals[];
+};
+
+/**
+ * Screen information
+ */
+struct libaxl_screen {
+ /**
+ * The root window of the screen
+ *
+ * This is always an LIBAXL_INPUT_OUTPUT window
+ */
+ libaxl_window_t root;
+
+ /**
+ * The map initially associated with the root window
+ *
+ * Windows with minimal colour requires may want to
+ * use this colormap if they use the same depth as
+ * the root window.
+ */
+ libaxl_colormap_t default_colormap;
+
+ /**
+ * Bright colour usable for implementing monochrome
+ * applications using `.default_colormap`
+ *
+ * This colour is permanently allocated
+ *
+ * May be set to a dark colour on some screens
+ * (that what the colours to be inverted); the
+ * actual RGB value is unspecified and may any
+ * colour and not necessarily black or white
+ */
+ uint32_t white_pixel;
+
+ /**
+ * Dark colour usable for implementing monochrome
+ * applications using `.default_colormap`
+ *
+ * This colour is permanently allocated
+ *
+ * May be set to a bright colour on some screens
+ * (that what the colours to be inverted); the
+ * actual RGB value is unspecified and may any
+ * colour and not necessarily black or white
+ */
+ uint32_t black_pixel;
+
+ uint32_t current_input_masks; /* TODO SETofEVENTS */
+
+ /**
+ * The width, in pixels, of the root window;
+ * which cannot be changed in the core protocol
+ *
+ * This value will not update when root window
+ * is resized, but new connections may get an
+ * update value
+ */
+ uint16_t width_in_pixels;
+
+ /**
+ * The height, in pixels, of the root window;
+ * which cannot be changed in the core protocol
+ *
+ * This value will not update when root window
+ * is resized, but new connections may get an
+ * update value
+ */
+ uint16_t height_in_pixels;
+
+ /**
+ * The width, in millimeters, of the root window;
+ * which cannot be changed in the core protocol
+ *
+ * This value will not update when root window
+ * is resized or the monitor is changed
+ *
+ * Note that the horizontal and vertical DPI
+ * (dots per inch) of a monitor are not
+ * necessarily equal to each other
+ */
+ uint16_t width_in_millimeters;
+
+ /**
+ * The height, in millimeters, of the root window;
+ * which cannot be changed in the core protocol
+ *
+ * This value will not update when root window
+ * is resized or the monitor is changed
+ *
+ * Note that the horizontal and vertical DPI
+ * (dots per inch) of a monitor are not
+ * necessarily equal to each other
+ */
+ uint16_t height_in_millimeters;
+
+ uint16_t min_installed_maps;
+ uint16_t max_installed_maps;
+ libaxl_visual_t root_visual;
+ uint8_t backing_stores;
+ libaxl_bool_t save_under;
+ uint8_t root_depth;
+ uint8_t number_of_allowed_depths;
+ struct libaxl_depth allowed_depths[];
+};
+
+/**
+ * Information about the display, display server, and connection
+ */
+struct libaxl_display_info {
+ /**
+ * The version number of this structure that this library uses.
+ * When using dynamic linking, is important use the version that
+ * is the lowest of the number stored in this field and the
+ * number stored in the LIBAXL_DISPLAY_INFO_VERSION constant.
+ */
+ int struct_version; /* TODO set when creating LIBAXL_CONNECTION */
+
+ /* SINCE STRUCT VERSION 0: */
+
+ /**
+ * The major number of version of the protocol negotiated
+ * between the client and the server. This is not necessarily
+ * the same number as returned in the second parameter of
+ * the libaxl_receive_handshake() function.
+ */
+ int protocol_version_major;
+
+ /**
+ * The minor number of version of the protocol negotiated
+ * between the client and the server. This is not necessarily
+ * the same number as returned in the third parameter of
+ * the libaxl_receive_handshake() function.
+ */
+ int protocol_version_minor;
+
+ /**
+ * The version of the protocol negotiated between the client
+ * and the server, encoded as the sum of the major number
+ * multipled by 10000 and the minor number.
+ */
+ int protocol_version;
+
+ /**
+ * The byte order generally used in communication with the
+ * display server
+ */
+ enum libaxl_byte_order protocol_byte_order;
+
+ /**
+ * The byte order used in images
+ */
+ enum libaxl_byte_order image_byte_order;
+
+ /**
+ * The smallest keycode transmitted by the server
+ *
+ * Will never be less than 8
+ */
+ unsigned int min_keycode;
+
+ /**
+ * The greatest keycode transmitted by the server
+ *
+ * This field has the `unsigned int` type rather than the
+ * `libaxl_keycode_t` type because the latter is restricted
+ * to 8-bits, and a future version of the protocol could
+ * theoretically use a larger value
+ */
+ unsigned int max_keycode;
+
+ /**
+ * Some identification of the owner of the server implementation
+ */
+ const char *vendor;
+
+ /**
+ * Release number for the implementation. The vendor controls
+ * the semantics of this number.
+ */
+ uint32_t vendor_release;
+
+ /**
+ * The bit order used in the units in the scanlines of bitmaps
+ */
+ enum libaxl_bit_order bitmap_format_bit_order;
+
+ /**
+ * Number of bits (will be a multiple of 8) that the scanlines
+ * in bitmaps are quantised into units of
+ *
+ * Will never greater than `bitmap_format_scanline_pad`
+ */
+ size_t bitmap_format_scanline_unit;
+
+ /**
+ * Number of bits (will be a multiple of 8), that scanlines in
+ * bitmaps are padded to
+ */
+ size_t bitmap_format_scanline_pad;
+
+ /**
+ * The server may retain recent pointer motion history with
+ * finer granularity than is reported by LIBAXL_EVENT_MOTION_NOTIFY
+ * events, and which may be requested with
+ * LIBAXL_REQUEST_GET_MOTION_EVENTS requests
+ */
+ size_t motion_buffer_size;
+
+ /**
+ * The maximum value that can may appear in the length field
+ * in requests (that field is filled in by the library). Any
+ * requests larger than this (it is in byte-quads, meaning
+ * that it is multipled by 4 to get limit in bytes) generate
+ * a LIBAXL_ERROR_LENGTH error and the request is discarded.
+ *
+ * This field will always be at least 4096 (meaning 16384 bytes)
+ */
+ size_t maximum_request_length;
+
+ /**
+ * The number of elements in `.formats`
+ */
+ size_t nformats;
+
+ const struct libaxl_format *formats; /* TODO doc */
+
+ /**
+ * The number of elements in `.screens`
+ */
+ size_t nscreens;
+
+ /**
+ * List of all screens in the display
+ */
+ const struct libaxl_format *screens;
+};
diff --git a/libaxl-replies.h b/libaxl-replies.h
index 7b30ee1..30e02a1 100644
--- a/libaxl-replies.h
+++ b/libaxl-replies.h
@@ -7,7 +7,7 @@ struct libaxl_reply_get_window_attributes {
uint8_t backing_store; /* LIBAXL_NOT_USEFUL, LIBAXL_WHEN_MAPPED, or LIBAXL_ALWAYS */
uint16_t sequence_number;
uint32_t _reply_length; /* = 3 */
- libaxl_visual_id_t visual;
+ libaxl_visual_t visual;
uint16_t class; /* LIBAXL_INPUT_OUTPUT (= 1) or LIBAXL_INPUT_ONLY (= 2) */
uint8_t bit_gravity;
uint8_t win_gravity;
@@ -264,7 +264,7 @@ struct libaxl_reply_get_image {
uint8_t depth;
uint16_t sequence_number;
uint32_t _reply_length;
- libaxl_visual_id_t visual; /* Can be LIBAXL_NONE */
+ libaxl_visual_t visual; /* Can be LIBAXL_NONE */
uint8_t __unused[20];
size_t size_of_data;
uint8_t *data;
diff --git a/libaxl-requests.h b/libaxl-requests.h
index 21984f2..f0a6279 100644
--- a/libaxl-requests.h
+++ b/libaxl-requests.h
@@ -13,7 +13,7 @@ struct libaxl_request_create_window {
uint16_t height;
uint16_t border_width;
uint16_t class; /* LIBAXL_COPY_FROM_PARENT, LIBAXL_INPUT_OUTPUT, or LIBAXL_INPUT_ONLY */
- libaxl_visual_id_t visual; /* Can be LIBAXL_COPY_FROM_PARENT */
+ libaxl_visual_t visual; /* Can be LIBAXL_COPY_FROM_PARENT */
uint32_t value_mask; /* Determines which below are included, all are encoded as uint32_t */
#define LIBAXL_REQUEST_CREATE_WINDOW_BACKGROUND_PIXMAP 0x00000001UL
#define LIBAXL_REQUEST_CREATE_WINDOW_BACKGROUND_PIXEL 0x00000002UL
@@ -1008,7 +1008,7 @@ struct libaxl_request_create_colormap {
uint16_t _request_length; /* = 4 */
libaxl_colormap_t mid;
libaxl_window_t window;
- libaxl_visual_id_t visual;
+ libaxl_visual_t visual;
};
struct libaxl_request_free_colormap {
diff --git a/libaxl-types.h b/libaxl-types.h
index 1826430..c55a319 100644
--- a/libaxl-types.h
+++ b/libaxl-types.h
@@ -13,7 +13,7 @@ typedef libaxl_id_t libaxl_colormap_t;
typedef libaxl_id_t libaxl_drawable_t; /* libaxl_window_t or libaxl_pixmap_t */
typedef libaxl_id_t libaxl_fontable_t; /* libaxl_font_t or libaxl_gcontext_t */
typedef libaxl_id_t libaxl_atom_t;
-typedef libaxl_id_t libaxl_visual_id_t;
+typedef libaxl_id_t libaxl_visual_t;
typedef uint32_t libaxl_timestamp_t;
typedef uint32_t libaxl_keysym_t;
typedef uint8_t libaxl_keycode_t;
diff --git a/libaxl.h b/libaxl.h
index d60cb17..7ecc19d 100644
--- a/libaxl.h
+++ b/libaxl.h
@@ -2,6 +2,9 @@
#ifndef LIBAXL_H
#define LIBAXL_H
+#include <stddef.h>
+#include <stdint.h>
+
#if defined(__GNUC__)
# define _LIBAXL_GCC_ONLY(...) __VA_ARGS__
#else
@@ -15,6 +18,32 @@
#include "libaxl-errors.h"
#include "libaxl-requests.h"
#include "libaxl-replies.h"
+#include "libaxl-display-info.h"
+
+/**
+ * The major version number of the highest version of X protocol
+ * of the library supports (the lowest version is 11.0). If you
+ * are using dynamic linking, you make also want to use the
+ * libaxl_protocol_major() function.
+ */
+#define LIBAXL_PROTOCOL_MAJOR 11
+
+/**
+ * The minor version number of the highest version of X protocol
+ * of the library supports (the lowest version is 11.0). If you
+ * are using dynamic linking, you make also want to use the
+ * libaxl_protocol_minor() function.
+ */
+#define LIBAXL_PROTOCOL_MINOR 0
+
+/**
+ * The version number of the highest version of X protocol of
+ * the library supports, as one integer where the major number
+ * is multiped by 100000, (the lowest version is 11.0, which is
+ * encoded as 1100000). If you are using dynamic linking, you
+ * make also want to use the libaxl_protocol_version() function.
+ */
+#define LIBAXL_PROTOCOL_VERSION ((LIBAXL_PROTOCOL_MAJOR) * 100000 + (LIBAXL_PROTOCOL_MINOR))
/* libaxl error codes */
#define LIBAXL_ERROR_SYSTEM -1 /* use `errno` */
@@ -28,8 +57,20 @@
#define LIBAXL_ERROR_MALFORMATTED_REQUEST -9
#define LIBAXL_ERROR_CONNECTION_CLOSED -10
#define LIBAXL_ERROR_INVALID_REPLY_OPCODE -11
+#define LIBAXL_ERROR_INVALID_HANDSHAKE_RESPONSE -12
+#define LIBAXL_ERROR_OUT_OF_RESOURCE_IDS -13
-union libaxl_input { /* TODO man */
+#define LIBAXL_HANDSHAKE_FAILED 0
+#define LIBAXL_HANDSHAKE_SUCCESS 1
+#define LIBAXL_HANDSHAKE_AUTHENTICATE 2
+
+/*
+ * The largest possible return of the libaxl_get_decnet_object()
+ * function, plus 1 for the terminal NUL byte
+ */
+#define LIBAXL_DECNET_OBJECT_MAX (5 + 3 * sizeof(int) - sizeof(int) / 2)
+
+union libaxl_input { /* TODO doc, man */
uint8_t type;
union libaxl_error error; /* if .type = LIBAXL_ERROR */
union libaxl_reply reply; /* if .type = LIBAXL_REPLY */
@@ -50,6 +91,118 @@ typedef struct libaxl_connection LIBAXL_CONNECTION;
typedef struct libaxl_context LIBAXL_CONTEXT;
/**
+ * Returns the major version number of the highest version of
+ * X protocol of the library supports (the lowest version is
+ * 11.0). If you are using static linking, you make also want
+ * to use the LIBAXL_PROTOCOL_MAJOR constant.
+ *
+ * @return The major version number of highest support version of the protocol
+ */
+int libaxl_protocol_version_major(void);
+
+/**
+ * Returns the minor version number of the highest version of
+ * X protocol of the library supports (the lowest version is
+ * 11.0). If you are using static linking, you make also want
+ * to use the LIBAXL_PROTOCOL_MINOR constant.
+ *
+ * @return The minor version number of highest support version of the protocol
+ */
+int libaxl_protocol_version_minor(void);
+
+/**
+ * Returns the minor version number of the highest version of
+ * X protocol of the library supports (the lowest version is 11.0,
+ * which is encoded as 1100000). If you are using static linking,
+ * you make also want to use the LIBAXL_PROTOCOL_VERSION constant.
+ *
+ * @return The version number of highest support version of the protocol,
+ * encoded as a sum of the major number multipled by 100000 (1e5)
+ * and the minor number, e.g. 1100000 for 11.0 (X11)
+ */
+int libaxl_protocol_version(void);
+
+/**
+ * Get the file description used for a connection to
+ * the X display server
+ *
+ * @param conn The connection to the display server
+ * @return The number of the file descriptor that holds
+ * the connection to the display server
+ */
+_LIBAXL_GCC_ONLY(__attribute__((__nonnull__, __warn_unused_result__)))
+int libaxl_fileno(LIBAXL_CONNECTION *); /* TODO man */
+
+/**
+ * Get information about a connection and the display
+ * and display server it is connected to
+ *
+ * This information is sent to the client during the
+ * connection handshake, and is never updated hence
+ *
+ * @param conn The connection to the display server
+ * @param version Should be `LIBAXL_DISPLAY_INFO_VERSION`; used to
+ * identify if old fields that have been replaces
+ * must allocated and filled in with information
+ * from the new fields that the application does
+ ' not know about
+ * @return Information about the display, the display
+ * server, and the connection; `NULL` on error
+ *
+ * On failure, no error is returned, but the error
+ * is always LIBAXL_ERROR_SYSTEM
+ */
+_LIBAXL_GCC_ONLY(__attribute__((__nonnull__, __warn_unused_result__)))
+const struct libaxl_display_info *libaxl_info(LIBAXL_CONNECTION *, int); /* TODO man */
+
+/**
+ * Get the next element in a `struct libaxl_depth *`
+ *
+ * Usage example for iterating of all elements in
+ * `screen->allowed_depths` where `screen` is a
+ * `struct libaxl_screen *`:
+ *
+ * struct libaxl_depth *depth;
+ * size_t i = 0;
+ * for (depth = screen->allowed_depths; i < screen->number_of_allowed_depths; depth = libaxl_next_depth(depth)) {
+ * ... do something with `depth` or `break` if `i` is of a desired value (index) ...
+ * }
+ *
+ * @param depth The element before the element to return
+ * @return The element after `depth`
+ */
+inline const struct libaxl_depth *
+libaxl_next_depth(const struct libaxl_depth *depth) /* TODO man */
+{
+ return (const struct libaxl_depth *)(uintptr_t)&depth->visuals[depth->number_of_visuals];
+}
+
+/**
+ * Get the next element in a `struct libaxl_screen *`
+ *
+ * Usage example for iterating of all elements in `info->screens`
+ * where `info` is a `struct libaxl_display_info *`:
+ *
+ * struct libaxl_screen *screen;
+ * size_t i = 0;
+ * for (screen = info->screens; i < info->nscreens; screen = libaxl_next_screen(screen)) {
+ * ... do something with `screen` or `break` if `i` is of a desired value (index) ...
+ * }
+ *
+ * @param screen The element before the element to return
+ * @return The element after `screen`
+ */
+inline const struct libaxl_screen *
+libaxl_next_screen(const struct libaxl_screen *screen) /* TODO man */
+{
+ uint8_t n = screen->number_of_allowed_depths;
+ const struct libaxl_depth *depth = screen->allowed_depths;
+ while (n--)
+ depth = libaxl_next_depth(depth);
+ return (const struct libaxl_screen *)(uintptr_t)depth;
+}
+
+/**
* Create context, for a thread, to use when accessing
* the display server
*
@@ -70,20 +223,26 @@ void libaxl_context_free(LIBAXL_CONTEXT *); /* TODO man */
* Generate a resource ID
*
* The generated resource ID can be deallocated
- * with the `libaxl_deallocate_id` function
+ * with the libaxl_deallocate_id() function
*
* @param ctx The thread's context for the connection
* @return The generated resource ID, 0 on failure
+ *
+ * On failure, no error is returned, but the error
+ * is always LIBAXL_ERROR_OUT_OF_RESOURCE_IDS
*/
-libaxl_id_t libaxl_generate_id(LIBAXL_CONTEXT *); /* TODO man, implement */
+_LIBAXL_GCC_ONLY(__attribute__((__nonnull__, __warn_unused_result__)))
+libaxl_id_t libaxl_generate_id(LIBAXL_CONTEXT *); /* TODO man */
/**
* Deallocate a resource ID so that it can be reused later
*
- * @param ctx The thread's context for the connection
- * @param id The generated resource ID to deallocate
+ * @param ctx The thread's context for the connection
+ * @param id The generated resource ID to deallocate
+ * @return 0 on success, a negative libaxl error code on failure
*/
-void libaxl_deallocate_id(LIBAXL_CONTEXT *, libaxl_id_t); /* TODO man, implement */
+_LIBAXL_GCC_ONLY(__attribute__((__nonnull__)))
+int libaxl_deallocate_id(LIBAXL_CONTEXT *, libaxl_id_t); /* TODO man, implement */
/**
* Parse a display name string
@@ -96,7 +255,7 @@ void libaxl_deallocate_id(LIBAXL_CONTEXT *, libaxl_id_t); /* TODO man, implement
*
* <path>[.<screen>]
*
- * @param name The display name string, $DISPLAY will be used if `NULL` or empty
+ * @param name The display name string, $DISPLAY will be used if NULL or empty
* @param hostp Output parameter for the host of or (if the protocol is "unix") path
* to the display, remember to free after successful completion
* @param protocolp Output parameter for the network protocol used to access the display,
@@ -109,6 +268,62 @@ _LIBAXL_GCC_ONLY(__attribute__((__nonnull__(2, 3, 4, 5))))
int libaxl_parse_display(const char *, char **, char **, int *, int *); /* TODO man */
/**
+ * Get the TCP port that is used for a display
+ *
+ * @param display The display's number, the function will assume that it is a valid number
+ * @return The TCP port used for the display
+ */
+_LIBAXL_GCC_ONLY(__attribute__((__warn_unused_result__)))
+inline uint16_t libaxl_get_tcp_port(int display) /* TODO man */
+{
+ return (uint16_t)(display + 6000);
+}
+
+/**
+ * Get the DECnet object name that is used for a display
+ *
+ * @param buf Buffer that the object name shall be written to, it is recommended
+ * that is size, if static, is LIBAXL_DECNET_OBJECT_MAX (which is
+ * always sufficient), otherwise it should be at least the return value
+ * of this function (for the same last argument but with NULL and 0
+ * as the first two arguments) plus 1
+ * @param size The size of the buffer in the `buf` argument
+ * @param display The display's number
+ * @return The length of the object name, will not exceed LIBAXL_DECNET_OBJECT_MAX
+ * less 1; an additional uncounted NUL byte will be written to the buffer
+ * if it is large enough; or -1 on failure (specifically snprintf(3), which
+ * the function calls, by fail with EOVERFLOW if the `size` argument is
+ * greater than {INT_MAX})
+ */
+int libaxl_get_decnet_object(char *, size_t, int); /* TODO man */
+
+/**
+ * This function is to be called once and only once, excluding any
+ * failure that does not set `errno` to EINPROGRESS and excluding
+ * any call for which the display server respond that the handshake
+ * failed or requires authentication, immediately after connecting
+ * to the socket for the display
+ *
+ * The libaxl_receive_handshake() shall be called immediately after
+ * calling this function
+ *
+ * @param ctx The thread's context for the connection to the display to send the handshake over
+ * @param auth_name The protocol name of the authorisation the client expects the server to use;
+ * valid authorisation mechanisms are not part of the core X protocol
+ * @param auth_name_len The length of `auth_name`, 0 if `auth_name` is NULL
+ * @param auth_data The authorisation data, which is specific to the choosen authorisation mechanism
+ * @param auth_data_len The length of `auth_data`, 0 if `auth_data` is NULL
+ * @param flags Flags to use for the 4th parameter when calling send(3)
+ * @return 0 on success, a negative libaxl error code on failure
+ *
+ * If the function returns LIBAXL_ERROR_SYSTEM and sets `errno`
+ * to EINPROGRESS, the message has been saved (possibly partially
+ * sent) and pending to be sent by calling libaxl_flush().
+ */
+_LIBAXL_GCC_ONLY(__attribute__((__nonnull__(1))))
+int libaxl_send_handshake(LIBAXL_CONTEXT *restrict, const char *, size_t, const char *, size_t, int); /* TODO man */
+
+/**
* Send a request to the display server
*
* May deadlock if called from an asynchronously called signal handler
@@ -150,14 +365,55 @@ _LIBAXL_GCC_ONLY(__attribute__((__nonnull__)))
int libaxl_flush(LIBAXL_CONNECTION *restrict, int); /* TODO man */
/**
+ * Receive the server's part of the handshake, this function
+ * shall be called once immediately after each successful call
+ * to the libaxl_send_handshake() function or successful call
+ * to the libaxl_flush() function due to EINPROGRESS failure
+ * of call to the libaxl_send_handshake() function
+ *
+ * @param ctx The thread's context for the connection to the display server
+ * @param majorp Output parameter for the major version number for a version
+ * of the X protocol that the display server supports, which is
+ * not necessarily the version that the library uses. Will only
+ * be set if the function returns LIBAXL_HANDSHAKE_FAILED or
+ * LIBAXL_HANDSHAKE_SUCCESS.
+ * @param minorp Output parameter for the minor version number for a version
+ * of the X protocol that the display server supports, which is
+ * not necessarily the version that the library uses. Will only
+ * be set if the function returns LIBAXL_HANDSHAKE_FAILED or
+ * LIBAXL_HANDSHAKE_SUCCESS.
+ * @param reasonp Output parameter for the reason the handshake or authorisation
+ * failed. Will be set to NULL unless the function returns
+ * LIBAXL_HANDSHAKE_FAILED or LIBAXL_HANDSHAKE_AUTHENTICATE.
+ * Remember to free after successful completion (non-negative return)
+ * @param flags Flags to use for the 4th parameter when calling recv(3)
+ * @return 0 on success, a negative libaxl error code on failure
+ *
+ * Behaviour is unspecified if SO_PEEK_OFF is active on the
+ * connection to the display server or if the MSG_PEEK flag
+ * is used
+ */
+_LIBAXL_GCC_ONLY(__attribute__((__nonnull__(1))))
+int libaxl_receive_handshake(LIBAXL_CONTEXT *restrict, int *restrict, int *restrict, char **restrict, int); /* TODO man */
+
+/**
* Receive the next pending message from the display server
*
- * @param ctx The thread's context for the connection to the display server
- * @param msgp Output parameter for the received message; the message will
- * be vaild until the next time this function is called with the
- * same `ctx` parameter or until the libaxl_context_free() function
- * is called with the same `ctx` paramter (whichever comes first)
- * @param flags Flags to use for the 4th parameter when calling recv(3)
+ * Be aware: order of replies and events is not guaranteed,
+ * and events caused by a request are sent before replies
+ * and errors, however when multiple events are generated,
+ * by some action, they are send in an uninterrupted sequence
+ * (no other event, error, or reply is send between those event).
+ * This means that it is important to check the returned
+ * sequence number.
+ *
+ * @param ctx The thread's context for the connection to the display server
+ * @param msgp Output parameter for the received message; the message will
+ * be vaild until the next time this function is called with the
+ * same `ctx` parameter or until the libaxl_context_free() function
+ * is called with the same `ctx` paramter (whichever comes first)
+ * @param flags Flags to use for the 4th parameter when calling recv(3)
+ * @return 0 on success, a negative libaxl error code on failure
*
* Behaviour is unspecified if SO_PEEK_OFF is active on the
* connection to the display server or if the MSG_PEEK flag
diff --git a/libaxl_fileno.c b/libaxl_fileno.c
new file mode 100644
index 0000000..14bf978
--- /dev/null
+++ b/libaxl_fileno.c
@@ -0,0 +1,8 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+int
+libaxl_fileno(LIBAXL_CONNECTION *conn)
+{
+ return conn->fd;
+}
diff --git a/libaxl_generate_id.c b/libaxl_generate_id.c
new file mode 100644
index 0000000..232ef4d
--- /dev/null
+++ b/libaxl_generate_id.c
@@ -0,0 +1,20 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+libaxl_id_t
+libaxl_generate_id(LIBAXL_CONTEXT *ctx)
+{
+ LIBAXL_CONNECTION *conn = ctx->conn;
+ uint32_t id;
+
+ id = atomic_fetch_add(&conn->xid_last, 1);
+ if (id <= conn->xid_max)
+ return (id << conn->xid_shift) | conn->xid_base;
+
+ atomic_fetch_sub(&conn->xid_last, 1);
+
+ liberror_save_backtrace(NULL);
+ liberror_set_error("Request has been buffered and is ready to be sent",
+ "libaxl_generate_id", "libaxl", LIBAXL_ERROR_OUT_OF_RESOURCE_IDS);
+ return 0;
+}
diff --git a/libaxl_get_decnet_object.c b/libaxl_get_decnet_object.c
new file mode 100644
index 0000000..a744fd2
--- /dev/null
+++ b/libaxl_get_decnet_object.c
@@ -0,0 +1,8 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+int
+libaxl_get_decnet_object(char *buf, size_t size, int display)
+{
+ return snprintf(buf, size, "X$X%i", display);
+}
diff --git a/libaxl_get_tcp_port.c b/libaxl_get_tcp_port.c
new file mode 100644
index 0000000..2fd3eca
--- /dev/null
+++ b/libaxl_get_tcp_port.c
@@ -0,0 +1,4 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+extern inline uint16_t libaxl_get_tcp_port(int display);
diff --git a/libaxl_info.c b/libaxl_info.c
new file mode 100644
index 0000000..585f2f1
--- /dev/null
+++ b/libaxl_info.c
@@ -0,0 +1,9 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+const struct libaxl_display_info *
+libaxl_info(LIBAXL_CONNECTION *conn, int version)
+{
+ (void) version;
+ return &conn->info;
+}
diff --git a/libaxl_next_depth.c b/libaxl_next_depth.c
new file mode 100644
index 0000000..c14c977
--- /dev/null
+++ b/libaxl_next_depth.c
@@ -0,0 +1,4 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+extern inline const struct libaxl_depth *libaxl_next_depth(const struct libaxl_depth *depth);
diff --git a/libaxl_next_screen.c b/libaxl_next_screen.c
new file mode 100644
index 0000000..3c129c3
--- /dev/null
+++ b/libaxl_next_screen.c
@@ -0,0 +1,4 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+extern inline const struct libaxl_screen *libaxl_next_screen(const struct libaxl_screen *screen);
diff --git a/libaxl_protocol_version.c b/libaxl_protocol_version.c
new file mode 100644
index 0000000..feaa953
--- /dev/null
+++ b/libaxl_protocol_version.c
@@ -0,0 +1,8 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+int
+libaxl_protocol_version(void)
+{
+ return LIBAXL_PROTOCOL_VERSION;
+}
diff --git a/libaxl_protocol_version_major.c b/libaxl_protocol_version_major.c
new file mode 100644
index 0000000..7dc00da
--- /dev/null
+++ b/libaxl_protocol_version_major.c
@@ -0,0 +1,8 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+int
+libaxl_protocol_version_major(void)
+{
+ return LIBAXL_PROTOCOL_MAJOR;
+}
diff --git a/libaxl_protocol_version_minor.c b/libaxl_protocol_version_minor.c
new file mode 100644
index 0000000..de1e688
--- /dev/null
+++ b/libaxl_protocol_version_minor.c
@@ -0,0 +1,8 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+int
+libaxl_protocol_version_minor(void)
+{
+ return LIBAXL_PROTOCOL_MINOR;
+}
diff --git a/libaxl_receive_handshake.c b/libaxl_receive_handshake.c
new file mode 100644
index 0000000..5e193e2
--- /dev/null
+++ b/libaxl_receive_handshake.c
@@ -0,0 +1,211 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+struct response {
+ uint8_t status;
+ uint8_t length_of_reason; /* Only used in Failed, and not in Authenticate */
+ uint16_t protocol_major_version; /* Not in Authenticate */
+ uint16_t protocol_minor_version; /* Not in Authenticate */
+ union {
+ struct {
+ uint16_t length_of_addition_data;
+ char additional_data[];
+ };
+ struct {
+ uint16_t __length_of_addition_data__1;
+ char reason[];
+ };
+ struct {
+ uint16_t __length_of_addition_data__2;
+ uint32_t release_number;
+ uint32_t resource_id_base;
+ uint32_t resource_id_mask;
+ uint32_t motion_buffer_size;
+ uint16_t length_of_vendor;
+ uint16_t maximum_request_length;
+ uint8_t number_of_roots;
+ uint8_t number_of_pixmap_formats;
+ uint8_t image_byte_order;
+ uint8_t bitmap_format_bit_order;
+ uint8_t bitmap_format_scanline_unit;
+ uint8_t bitmap_format_scanline_pad;
+ uint8_t min_keycode;
+ uint8_t max_keycode;
+ uint32_t __unused;
+ char vendor[];
+ };
+ };
+};
+
+int
+libaxl_receive_handshake(LIBAXL_CONTEXT *restrict ctx, int *restrict majorp, int *restrict minorp, char **restrict reasonp, int flags)
+{
+ LIBAXL_CONNECTION *conn = ctx->conn;
+ char *restrict inbuf, *new;
+ size_t n, t, len, vendor_len, out_off, in_off;
+ ssize_t r;
+ int read_stage = 0, saved_errno;
+ struct response *resp;
+#ifdef MSG_TRUNC
+ int flag_trunc;
+#endif
+
+#ifdef MSG_TRUNC
+ flags ^= flag_trunc = flags & MSG_TRUNC;
+#endif
+
+ WLOCK_CONNECTION_RECV(conn);
+
+ inbuf = conn->in_buf;
+ n = offsetof(struct response, additional_data);
+
+ if (conn->in_buf_size < n) {
+ inbuf = liberror_realloc(inbuf, n);
+ if (!inbuf) {
+ WUNLOCK_CONNECTION_RECV(conn);
+ return LIBAXL_ERROR_SYSTEM;
+ }
+ conn->in_buf = inbuf;
+ conn->in_buf_size = n;
+ }
+
+continue_read:
+ while (conn->in_progress < n) {
+ r = recv(conn->fd, &inbuf[conn->in_progress], n - conn->in_progress, flags);
+ if (r <= 0) {
+ WUNLOCK_CONNECTION_RECV(conn);
+ liberror_save_backtrace(NULL);
+ if (!r) {
+ liberror_set_error("The connection to the display server has been closed",
+ "libaxl_receive_handshake", "libaxl", LIBAXL_ERROR_CONNECTION_CLOSED);
+ return LIBAXL_ERROR_CONNECTION_CLOSED;
+ }
+ liberror_recv_failed(conn->fd, &inbuf[conn->in_progress], n - conn->in_progress, flags, "<display server>");
+ return LIBAXL_ERROR_SYSTEM;
+ }
+ conn->in_progress += (size_t)r;
+ }
+
+ if (read_stage == 0) {
+ n += (size_t)((struct response *)inbuf)->length_of_addition_data * 4;
+ read_stage = 1;
+ goto continue_read;
+ }
+
+ conn->in_progress = 0;
+
+#ifdef MSG_TRUNC
+ if (flag_trunc) {
+ WUNLOCK_CONNECTION_RECV(conn);
+ return 0;
+ }
+#endif
+
+ t = conn->in_buf_size;
+ conn->in_buf_size = ctx->in_buf_size;
+ ctx->in_buf_size = t;
+
+ conn->in_buf = ctx->in_buf;
+ ctx->in_buf = inbuf;
+
+ WUNLOCK_CONNECTION_RECV(conn);
+
+ resp = (struct response *)inbuf;
+
+ if (majorp)
+ *majorp = (int)resp->protocol_major_version;
+ if (minorp)
+ *minorp = (int)resp->protocol_minor_version;
+ if (reasonp)
+ *reasonp = NULL;
+
+ switch (resp->status) {
+ case LIBAXL_HANDSHAKE_FAILED:
+ len = (size_t)resp->length_of_reason;
+ if (n < len + offsetof(struct response, reason))
+ goto invalid;
+ goto return_reason;
+
+ case LIBAXL_HANDSHAKE_AUTHENTICATE:
+ len = n - offsetof(struct response, reason);
+ return_reason:
+ if (reasonp) {
+ *reasonp = liberror_malloc(len + 1);
+ if (!*reasonp)
+ return LIBAXL_ERROR_SYSTEM;
+ memcpy(*reasonp, resp->reason, len);
+ (*reasonp)[len] = '\0';
+ }
+ break;
+
+ case LIBAXL_HANDSHAKE_SUCCESS:
+ if (resp->resource_id_base & resp->resource_id_mask ||
+ (resp->resource_id_base | resp->resource_id_mask) >> 29)
+ goto invalid;
+
+ conn->xid_base = resp->resource_id_base;
+ for (conn->xid_shift = 0; !(resp->resource_id_mask & 1); conn->xid_shift += 1)
+ resp->resource_id_mask >>= 1;
+ conn->xid_max = resp->resource_id_mask;
+ conn->info.vendor_release = resp->release_number;
+ conn->info.min_keycode = resp->min_keycode;
+ conn->info.max_keycode = resp->max_keycode;
+ conn->info.image_byte_order = resp->image_byte_order;
+ conn->info.motion_buffer_size = resp->motion_buffer_size;
+ conn->info.maximum_request_length = resp->maximum_request_length;
+ conn->info.bitmap_format_bit_order = resp->bitmap_format_bit_order;
+ conn->info.bitmap_format_scanline_unit = resp->bitmap_format_scanline_unit;
+ conn->info.bitmap_format_scanline_pad = resp->bitmap_format_scanline_pad;
+
+ /* These restricts are less restrictive than the protocol specifices,
+ * in case the they are modified later on */
+ if (resp->resource_id_mask ||
+ conn->xid_max + 1 < UINT16_C(1) << 18 ||
+ (conn->xid_max & (conn->xid_max + 1)) ||
+ resp->min_keycode < 8 ||
+ resp->image_byte_order > 1 ||
+ resp->maximum_request_length < 4096 ||
+ resp->bitmap_format_bit_order > 1 ||
+ resp->bitmap_format_scanline_unit % 8 ||
+ resp->bitmap_format_scanline_pad % 8 ||
+ resp->bitmap_format_scanline_unit > resp->bitmap_format_scanline_pad)
+ goto invalid;
+
+ vendor_len = resp->length_of_vendor;
+ conn->info.nscreens = resp->number_of_roots;
+ conn->info.nformats = resp->number_of_pixmap_formats;
+
+ memmove(inbuf, resp->vendor, out_off = in_off = vendor_len);
+ inbuf[out_off++] = '\0';
+ out_off += (4 - out_off % 4) % 4;
+ in_off += (4 - in_off % 4) % 4;
+ in_off += offsetof(struct response, vendor);
+ t = out_off;
+ memmove(&inbuf[out_off], &inbuf[in_off], n - in_off);
+ out_off += n - in_off;
+
+ saved_errno = errno;
+ new = realloc(inbuf, out_off);
+ errno = saved_errno;
+ if (new)
+ inbuf = new;
+
+ conn->info_buf = inbuf;
+ conn->info.vendor = inbuf;
+ conn->info.formats = (void *)&inbuf[t];
+ conn->info.screens = (void *)&inbuf[t + conn->info.nformats * 8];
+
+ ctx->in_buf_size = 0;
+ ctx->in_buf = NULL;
+ break;
+
+ default:
+ invalid:
+ liberror_set_error("Invalid handshake response received from the display server",
+ "libaxl_receive_handshake", "libaxl", LIBAXL_ERROR_INVALID_HANDSHAKE_RESPONSE);
+ errno = EBADMSG;
+ return LIBAXL_ERROR_INVALID_HANDSHAKE_RESPONSE;
+ }
+
+ return (int)resp->status;
+}
diff --git a/libaxl_send_handshake.c b/libaxl_send_handshake.c
new file mode 100644
index 0000000..80d48aa
--- /dev/null
+++ b/libaxl_send_handshake.c
@@ -0,0 +1,115 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+static int
+send_all(int fd, void *restrict data, size_t n, int flags, size_t *restrict progressp)
+{
+ char *bs = data;
+ ssize_t r;
+
+ while (n) {
+ r = liberror_send(fd, bs, n, flags, "<display server>");
+ if (r < 0)
+ return -1;
+ bs += r, n -= (size_t)r;
+ *progressp += (size_t)r;
+ }
+
+ return 0;
+}
+
+int
+libaxl_send_handshake(LIBAXL_CONTEXT *restrict ctx, const char *auth_name, size_t auth_name_len,
+ const char *auth_data, size_t auth_data_len, int flags)
+{
+ LIBAXL_CONNECTION *conn = ctx->conn;
+ LIBAXL_CONTEXT *pending, **pendingp;
+ size_t len, o = 0;
+ char *buf;
+
+ RLOCK_CONNECTION_SEND(conn);
+ pending = conn->pending_out;
+ conn->info.protocol_version_major = LIBAXL_PROTOCOL_MAJOR;
+ conn->info.protocol_version_minor = LIBAXL_PROTOCOL_MINOR;
+ conn->info.protocol_version = LIBAXL_PROTOCOL_VERSION;
+ conn->info.protocol_byte_order = LIBAXL_MSB_FIRST; /* TODO Use LIBAXL_LSB_FIRST if preferable */
+ RUNLOCK_CONNECTION_SEND(conn);
+ if (pending) {
+ liberror_save_backtrace(NULL);
+ liberror_set_error(pending == ctx
+ ? "Request from the previous call is pending to be flush"
+ : "A request from another thread is pending to be flushed",
+ "libaxl_send_handshake", "errno", EALREADY);
+ errno = EALREADY;
+ return LIBAXL_ERROR_SYSTEM;
+ }
+
+ if (auth_name_len > UINT16_MAX) {
+ liberror_save_backtrace(NULL);
+ liberror_set_error("Authorisation protocol name is too long", "libaxl_send_handshake", "errno", EINVAL);
+ errno = EINVAL;
+ return LIBAXL_ERROR_SYSTEM;
+ }
+
+ if (auth_data_len > UINT16_MAX) {
+ liberror_save_backtrace(NULL);
+ liberror_set_error("Authorisation data is too long", "libaxl_send_handshake", "errno", EINVAL);
+ errno = EINVAL;
+ return LIBAXL_ERROR_SYSTEM;
+ }
+
+ buf = ctx->out_buf;
+ len = 12 + (auth_name_len + 3) / 4 * 4 + (auth_data_len + 3) / 4 * 4;
+ if (len > ctx->out_buf_size) {
+ buf = liberror_realloc(buf, len);
+ if (!buf)
+ return LIBAXL_ERROR_SYSTEM;
+ ctx->out_buf = buf;
+ ctx->out_buf_size = len;
+ }
+
+ buf[o++] = 'B'; /* TODO Use 'l' (LSB first) if preferable */
+ buf[o++] = 0;
+ *(uint16_t *)&buf[o] = htons(LIBAXL_PROTOCOL_MAJOR);
+ o += 2;
+ *(uint16_t *)&buf[o] = htons(LIBAXL_PROTOCOL_MINOR);
+ o += 2;
+ *(uint16_t *)&buf[o] = htons((uint16_t)auth_name_len);
+ o += 2;
+ *(uint16_t *)&buf[o] = htons((uint16_t)auth_data_len);
+ o += 2;
+ buf[o++] = 0;
+ buf[o++] = 0;
+ memcpy(&buf[o], auth_name, auth_name_len);
+ for (o += auth_name_len; o % 4; o++)
+ buf[o] = 0;
+ memcpy(&buf[o], auth_data, auth_data_len);
+ for (o += auth_data_len; o % 4; o++)
+ buf[o] = 0;
+
+ ctx->out_length = o;
+ ctx->out_progress = 0;
+
+ WLOCK_CONNECTION_SEND(conn);
+
+ if (send_all(conn->fd, ctx->out_buf, ctx->out_length, flags, &ctx->out_progress)) {
+ ctx->next_pending_out = NULL;
+ pendingp = &conn->pending_out;
+ while (*pendingp)
+ pendingp = &(*pendingp)->next_pending_out;
+ *pendingp = ctx;
+ ctx->refcount += 1;
+
+ WUNLOCK_CONNECTION_SEND(conn);
+
+ liberror_save_backtrace(NULL);
+ liberror_set_error("Handshake has been buffered and is ready to be sent",
+ "libaxl_send_request", "errno", EINPROGRESS);
+ errno = EINPROGRESS;
+ return LIBAXL_ERROR_SYSTEM;
+ }
+
+ WUNLOCK_CONNECTION_SEND(conn);
+
+ return 0;
+}