4a230d362a
This patch currently supports the following requests: * Run custom commands with arguments (similar to key bind functions) * Get monitor properties * Get all available layouts * Get available tags * Get client properties * Subscribe to tag change, client focus change, and layout change, monitor focus change, focused title change, and client state change events This patch includes a dwm-msg cli program that supports all of the above requests for easy integration into shell scripts. The messages are sent in a JSON format to promote integration to increase scriptability in languages like Python/JavaScript. The patch requires YAJL for JSON parsing and a system with epoll support. Portability is planned to be increased in the future. This patch is best applied after all other patches to avoid merge conflicts. For more info on the IPC implementation and how to send/receive messages, documentation can be found at https://github.com/mihirlad55/dwm-ipc
321 lines
10 KiB
C
321 lines
10 KiB
C
#ifndef IPC_H_
|
|
#define IPC_H_
|
|
|
|
#include <stdint.h>
|
|
#include <sys/epoll.h>
|
|
#include <yajl/yajl_gen.h>
|
|
|
|
#include "IPCClient.h"
|
|
|
|
// clang-format off
|
|
#define IPC_MAGIC "DWM-IPC"
|
|
#define IPC_MAGIC_ARR { 'D', 'W', 'M', '-', 'I', 'P', 'C'}
|
|
#define IPC_MAGIC_LEN 7 // Not including null char
|
|
|
|
#define IPCCOMMAND(FUNC, ARGC, TYPES) \
|
|
{ #FUNC, {FUNC }, ARGC, (ArgType[ARGC])TYPES }
|
|
// clang-format on
|
|
|
|
typedef enum IPCMessageType {
|
|
IPC_TYPE_RUN_COMMAND = 0,
|
|
IPC_TYPE_GET_MONITORS = 1,
|
|
IPC_TYPE_GET_TAGS = 2,
|
|
IPC_TYPE_GET_LAYOUTS = 3,
|
|
IPC_TYPE_GET_DWM_CLIENT = 4,
|
|
IPC_TYPE_SUBSCRIBE = 5,
|
|
IPC_TYPE_EVENT = 6
|
|
} IPCMessageType;
|
|
|
|
typedef enum IPCEvent {
|
|
IPC_EVENT_TAG_CHANGE = 1 << 0,
|
|
IPC_EVENT_CLIENT_FOCUS_CHANGE = 1 << 1,
|
|
IPC_EVENT_LAYOUT_CHANGE = 1 << 2,
|
|
IPC_EVENT_MONITOR_FOCUS_CHANGE = 1 << 3,
|
|
IPC_EVENT_FOCUSED_TITLE_CHANGE = 1 << 4,
|
|
IPC_EVENT_FOCUSED_STATE_CHANGE = 1 << 5
|
|
} IPCEvent;
|
|
|
|
typedef enum IPCSubscriptionAction {
|
|
IPC_ACTION_UNSUBSCRIBE = 0,
|
|
IPC_ACTION_SUBSCRIBE = 1
|
|
} IPCSubscriptionAction;
|
|
|
|
/**
|
|
* Every IPC packet starts with this structure
|
|
*/
|
|
typedef struct dwm_ipc_header {
|
|
uint8_t magic[IPC_MAGIC_LEN];
|
|
uint32_t size;
|
|
uint8_t type;
|
|
} __attribute((packed)) dwm_ipc_header_t;
|
|
|
|
typedef enum ArgType {
|
|
ARG_TYPE_NONE = 0,
|
|
ARG_TYPE_UINT = 1,
|
|
ARG_TYPE_SINT = 2,
|
|
ARG_TYPE_FLOAT = 3,
|
|
ARG_TYPE_PTR = 4,
|
|
ARG_TYPE_STR = 5
|
|
} ArgType;
|
|
|
|
/**
|
|
* An IPCCommand function can have either of these function signatures
|
|
*/
|
|
typedef union ArgFunction {
|
|
void (*single_param)(const Arg *);
|
|
void (*array_param)(const Arg *, int);
|
|
} ArgFunction;
|
|
|
|
typedef struct IPCCommand {
|
|
char *name;
|
|
ArgFunction func;
|
|
unsigned int argc;
|
|
ArgType *arg_types;
|
|
} IPCCommand;
|
|
|
|
typedef struct IPCParsedCommand {
|
|
char *name;
|
|
Arg *args;
|
|
ArgType *arg_types;
|
|
unsigned int argc;
|
|
} IPCParsedCommand;
|
|
|
|
/**
|
|
* Initialize the IPC socket and the IPC module
|
|
*
|
|
* @param socket_path Path to create the socket at
|
|
* @param epoll_fd File descriptor for epoll
|
|
* @param commands Address of IPCCommands array defined in config.h
|
|
* @param commands_len Length of commands[] array
|
|
*
|
|
* @return int The file descriptor of the socket if it was successfully created,
|
|
* -1 otherwise
|
|
*/
|
|
int ipc_init(const char *socket_path, const int p_epoll_fd,
|
|
IPCCommand commands[], const int commands_len);
|
|
|
|
/**
|
|
* Uninitialize the socket and module. Free allocated memory and restore static
|
|
* variables to their state before ipc_init
|
|
*/
|
|
void ipc_cleanup();
|
|
|
|
/**
|
|
* Get the file descriptor of the IPC socket
|
|
*
|
|
* @return int File descriptor of IPC socket, -1 if socket not created.
|
|
*/
|
|
int ipc_get_sock_fd();
|
|
|
|
/**
|
|
* Get address to IPCClient with specified file descriptor
|
|
*
|
|
* @param fd File descriptor of IPC Client
|
|
*
|
|
* @return Address to IPCClient with specified file descriptor, -1 otherwise
|
|
*/
|
|
IPCClient *ipc_get_client(int fd);
|
|
|
|
/**
|
|
* Check if an IPC client exists with the specified file descriptor
|
|
*
|
|
* @param fd File descriptor
|
|
*
|
|
* @return int 1 if client exists, 0 otherwise
|
|
*/
|
|
int ipc_is_client_registered(int fd);
|
|
|
|
/**
|
|
* Disconnect an IPCClient from the socket and remove the client from the list
|
|
* of known connected clients
|
|
*
|
|
* @param c Address of IPCClient
|
|
*
|
|
* @return 0 if the client's file descriptor was closed successfully, the
|
|
* result of executing close() on the file descriptor otherwise.
|
|
*/
|
|
int ipc_drop_client(IPCClient *c);
|
|
|
|
/**
|
|
* Accept an IPC Client requesting to connect to the socket and add it to the
|
|
* list of clients
|
|
*
|
|
* @return File descriptor of new client, -1 on error
|
|
*/
|
|
int ipc_accept_client();
|
|
|
|
/**
|
|
* Read an incoming message from an accepted IPC client
|
|
*
|
|
* @param c Address of IPCClient
|
|
* @param msg_type Address to IPCMessageType variable which will be assigned
|
|
* the message type of the received message
|
|
* @param msg_size Address to uint32_t variable which will be assigned the size
|
|
* of the received message
|
|
* @param msg Address to char* variable which will be assigned the address of
|
|
* the received message. This must be freed using free().
|
|
*
|
|
* @return 0 on success, -1 on error reading message, -2 if reading the message
|
|
* resulted in EAGAIN, EINTR, or EWOULDBLOCK.
|
|
*/
|
|
int ipc_read_client(IPCClient *c, IPCMessageType *msg_type, uint32_t *msg_size,
|
|
char **msg);
|
|
|
|
/**
|
|
* Write any pending buffer of the client to the client's socket
|
|
*
|
|
* @param c Client whose buffer to write
|
|
*
|
|
* @return Number of bytes written >= 0, -1 otherwise. errno will still be set
|
|
* from the write operation.
|
|
*/
|
|
ssize_t ipc_write_client(IPCClient *c);
|
|
|
|
/**
|
|
* Prepare a message in the specified client's buffer.
|
|
*
|
|
* @param c Client to prepare message for
|
|
* @param msg_type Type of message to prepare
|
|
* @param msg_size Size of the message in bytes. Should not exceed
|
|
* MAX_MESSAGE_SIZE
|
|
* @param msg Message to prepare (not including header). This pointer can be
|
|
* freed after the function invocation.
|
|
*/
|
|
void ipc_prepare_send_message(IPCClient *c, const IPCMessageType msg_type,
|
|
const uint32_t msg_size, const char *msg);
|
|
|
|
/**
|
|
* Prepare an error message in the specified client's buffer
|
|
*
|
|
* @param c Client to prepare message for
|
|
* @param msg_type Type of message
|
|
* @param format Format string following vsprintf
|
|
* @param ... Arguments for format string
|
|
*/
|
|
void ipc_prepare_reply_failure(IPCClient *c, IPCMessageType msg_type,
|
|
const char *format, ...);
|
|
|
|
/**
|
|
* Prepare a success message in the specified client's buffer
|
|
*
|
|
* @param c Client to prepare message for
|
|
* @param msg_type Type of message
|
|
*/
|
|
void ipc_prepare_reply_success(IPCClient *c, IPCMessageType msg_type);
|
|
|
|
/**
|
|
* Send a tag_change_event to all subscribers. Should be called only when there
|
|
* has been a tag state change.
|
|
*
|
|
* @param mon_num The index of the monitor (Monitor.num property)
|
|
* @param old_state The old tag state
|
|
* @param new_state The new (now current) tag state
|
|
*/
|
|
void ipc_tag_change_event(const int mon_num, TagState old_state,
|
|
TagState new_state);
|
|
|
|
/**
|
|
* Send a client_focus_change_event to all subscribers. Should be called only
|
|
* when the client focus changes.
|
|
*
|
|
* @param mon_num The index of the monitor (Monitor.num property)
|
|
* @param old_client The old DWM client selection (Monitor.oldsel)
|
|
* @param new_client The new (now current) DWM client selection
|
|
*/
|
|
void ipc_client_focus_change_event(const int mon_num, Client *old_client,
|
|
Client *new_client);
|
|
|
|
/**
|
|
* Send a layout_change_event to all subscribers. Should be called only
|
|
* when there has been a layout change.
|
|
*
|
|
* @param mon_num The index of the monitor (Monitor.num property)
|
|
* @param old_symbol The old layout symbol
|
|
* @param old_layout Address to the old Layout
|
|
* @param new_symbol The new (now current) layout symbol
|
|
* @param new_layout Address to the new Layout
|
|
*/
|
|
void ipc_layout_change_event(const int mon_num, const char *old_symbol,
|
|
const Layout *old_layout, const char *new_symbol,
|
|
const Layout *new_layout);
|
|
|
|
/**
|
|
* Send a monitor_focus_change_event to all subscribers. Should be called only
|
|
* when the monitor focus changes.
|
|
*
|
|
* @param last_mon_num The index of the previously selected monitor
|
|
* @param new_mon_num The index of the newly selected monitor
|
|
*/
|
|
void ipc_monitor_focus_change_event(const int last_mon_num,
|
|
const int new_mon_num);
|
|
|
|
/**
|
|
* Send a focused_title_change_event to all subscribers. Should only be called
|
|
* if a selected client has a title change.
|
|
*
|
|
* @param mon_num Index of the client's monitor
|
|
* @param client_id Window XID of client
|
|
* @param old_name Old name of the client window
|
|
* @param new_name New name of the client window
|
|
*/
|
|
void ipc_focused_title_change_event(const int mon_num, const Window client_id,
|
|
const char *old_name, const char *new_name);
|
|
|
|
/**
|
|
* Send a focused_state_change_event to all subscribers. Should only be called
|
|
* if a selected client has a state change.
|
|
*
|
|
* @param mon_num Index of the client's monitor
|
|
* @param client_id Window XID of client
|
|
* @param old_state Old state of the client
|
|
* @param new_state New state of the client
|
|
*/
|
|
void ipc_focused_state_change_event(const int mon_num, const Window client_id,
|
|
const ClientState *old_state,
|
|
const ClientState *new_state);
|
|
/**
|
|
* Check to see if an event has occured and call the *_change_event functions
|
|
* accordingly
|
|
*
|
|
* @param mons Address of Monitor pointing to start of linked list
|
|
* @param lastselmon Address of pointer to previously selected monitor
|
|
* @param selmon Address of selected Monitor
|
|
*/
|
|
void ipc_send_events(Monitor *mons, Monitor **lastselmon, Monitor *selmon);
|
|
|
|
/**
|
|
* Handle an epoll event caused by a registered IPC client. Read, process, and
|
|
* handle any received messages from clients. Write pending buffer to client if
|
|
* the client is ready to receive messages. Drop clients that have sent an
|
|
* EPOLLHUP.
|
|
*
|
|
* @param ev Associated epoll event returned by epoll_wait
|
|
* @param mons Address of Monitor pointing to start of linked list
|
|
* @param selmon Address of selected Monitor
|
|
* @param lastselmon Address of pointer to previously selected monitor
|
|
* @param tags Array of tag names
|
|
* @param tags_len Length of tags array
|
|
* @param layouts Array of available layouts
|
|
* @param layouts_len Length of layouts array
|
|
*
|
|
* @return 0 if event was successfully handled, -1 on any error receiving
|
|
* or handling incoming messages or unhandled epoll event.
|
|
*/
|
|
int ipc_handle_client_epoll_event(struct epoll_event *ev, Monitor *mons,
|
|
Monitor **lastselmon, Monitor *selmon,
|
|
const char *tags[], const int tags_len,
|
|
const Layout *layouts, const int layouts_len);
|
|
|
|
/**
|
|
* Handle an epoll event caused by the IPC socket. This function only handles an
|
|
* EPOLLIN event indicating a new client requesting to connect to the socket.
|
|
*
|
|
* @param ev Associated epoll event returned by epoll_wait
|
|
*
|
|
* @return 0, if the event was successfully handled, -1 if not an EPOLLIN event
|
|
* or if a new IPC client connection request could not be accepted.
|
|
*/
|
|
int ipc_handle_socket_epoll_event(struct epoll_event *ev);
|
|
|
|
#endif /* IPC_H_ */
|