ZSHCPL = $(PREFIX)/share/zsh/site-functions
WM_SRC = bspwm.c helpers.c settings.c monitor.c desktop.c tree.c stack.c history.c \
- events.c pointer.c window.c messages.c query.c restore.c rule.c ewmh.c
+ events.c pointer.c window.c messages.c query.c restore.c rule.c ewmh.c subscribe.c
WM_OBJ = $(WM_SRC:.c=.o)
CL_SRC = bspc.c helpers.c
CL_OBJ = $(CL_SRC:.c=.o)
int main(int argc, char *argv[])
{
- int sock_fd;
+ int fd;
struct sockaddr_un sock_address;
- char msg[BUFSIZ];
- char rsp[BUFSIZ];
+ char msg[BUFSIZ], rsp[BUFSIZ];
if (argc < 2)
err("No arguments given.\n");
msg_len += n;
}
- if ((sock_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
err("Failed to create the socket.\n");
- if (connect(sock_fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1)
+ if (connect(fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1)
err("Failed to connect to the socket.\n");
- if (send(sock_fd, msg, msg_len, 0) == -1)
+ if (send(fd, msg, msg_len, 0) == -1)
err("Failed to send the data.\n");
- int ret = EXIT_SUCCESS;
-
- int n = recv(sock_fd, rsp, sizeof(rsp), 0);
- if (n == -1) {
- err("Failed to get the response.\n");
- } else if (n > 0) {
- if (n == 1 && rsp[0] == MESSAGE_FAILURE) {
+ int ret = EXIT_SUCCESS, nb;
+ while ((nb = recv(fd, rsp, sizeof(rsp), 0)) > 0) {
+ if (nb == 1 && rsp[0] == MESSAGE_FAILURE) {
ret = EXIT_FAILURE;
} else {
- rsp[n] = '\0';
+ rsp[nb] = '\0';
printf("%s\n", rsp);
+ fflush(stdout);
}
}
- if (sock_fd)
- close(sock_fd);
-
+ close(fd);
return ret;
}
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <ctype.h>
-#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#endif
#include <sys/socket.h>
#include <sys/un.h>
+#include <signal.h>
#include <unistd.h>
#include "types.h"
#include "desktop.h"
#include "monitor.h"
#include "settings.h"
#include "messages.h"
+#include "subscribe.h"
#include "events.h"
#include "common.h"
-#include "tree.h"
#include "window.h"
#include "history.h"
#include "stack.h"
{
fd_set descriptors;
char socket_path[MAXLEN];
- char *fifo_path = NULL;
config_path[0] = '\0';
- status_prefix = NULL;
int sock_fd, ret_fd, dpy_fd, sel, n;
struct sockaddr_un sock_address;
size_t rsp_len = 0;
xcb_generic_event_t *event;
char opt;
- while ((opt = getopt(argc, argv, "hvc:s:p:")) != -1) {
+ while ((opt = getopt(argc, argv, "hvc:")) != -1) {
switch (opt) {
case 'h':
- printf(WM_NAME " [-h|-v|-c CONFIG_PATH|-s PANEL_FIFO|-p PANEL_PREFIX]\n");
+ printf(WM_NAME " [-h|-v|-c CONFIG_PATH]\n");
exit(EXIT_SUCCESS);
break;
case 'v':
case 'c':
snprintf(config_path, sizeof(config_path), "%s", optarg);
break;
- case 's':
- fifo_path = optarg;
- break;
- case 'p':
- status_prefix = optarg;
- break;
}
}
sel = MAX(sock_fd, dpy_fd) + 1;
- if (fifo_path != NULL) {
- int fifo_fd = open(fifo_path, O_RDWR | O_NONBLOCK);
- if (fifo_fd != -1)
- status_fifo = fdopen(fifo_fd, "w");
- else
- warn("Couldn't open status fifo.\n");
- }
-
+ signal(SIGPIPE, SIG_IGN);
load_settings();
run_config();
running = true;
rsp[0] = MESSAGE_FAILURE;
rsp_len = 1;
}
- send(ret_fd, rsp, rsp_len, 0);
- close(ret_fd);
+ if (rsp_len == 1 && rsp[0] == MESSAGE_SUBSCRIBE) {
+ add_subscriber(ret_fd);
+ } else {
+ send(ret_fd, rsp, rsp_len, 0);
+ close(ret_fd);
+ }
rsp[0] = '\0';
}
}
cleanup();
close(sock_fd);
- if (status_fifo != NULL)
- fclose(status_fifo);
xcb_ewmh_connection_wipe(ewmh);
xcb_destroy_window(dpy, motion_recorder);
free(ewmh);
mon = mon_head = mon_tail = pri_mon = NULL;
history_head = history_tail = history_needle = NULL;
stack_head = stack_tail = NULL;
- status_fifo = NULL;
+ subscribe_head = subscribe_tail = NULL;
last_motion_time = last_motion_x = last_motion_y = 0;
visible = auto_raise = sticky_still = record_history = true;
randr_base = 0;
remove_monitor(mon_head);
while (stack_head != NULL)
remove_stack(stack_head);
+ while (subscribe_head != NULL)
+ remove_subscriber(subscribe_head);
empty_history();
free(frozen_pointer);
}
void put_status(void)
{
- if (status_fifo == NULL)
- return;
- if (status_prefix != NULL)
- fprintf(status_fifo, "%s", status_prefix);
- bool urgent = false;
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- fprintf(status_fifo, "%c%s:", (mon == m ? 'M' : 'm'), m->name);
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next, urgent = false) {
- for (node_t *n = first_extrema(d->root); n != NULL && !urgent; n = next_leaf(n, d->root))
- urgent |= n->client->urgent;
- char c = (urgent ? 'u' : (d->root == NULL ? 'f' : 'o'));
- if (m->desk == d)
- c = toupper(c);
- fprintf(status_fifo, "%c%s:", c, d->name);
- }
+ subscriber_list_t *sb = subscribe_head;
+ while (sb != NULL) {
+ subscriber_list_t *next = sb->next;
+ feed_subscriber(sb);
+ sb = next;
}
- if (mon != NULL && mon->desk != NULL)
- fprintf(status_fifo, "L%s", (mon->desk->layout == LAYOUT_TILED ? "tiled" : "monocle"));
- fprintf(status_fifo, "\n");
- fflush(status_fifo);
}
xcb_screen_t *screen;
xcb_window_t root;
uint8_t root_depth;
-FILE *status_fifo;
-char *status_prefix;
char config_path[MAXLEN];
monitor_t *mon;
history_t *history_needle;
stacking_list_t *stack_head;
stacking_list_t *stack_tail;
+subscriber_list_t *subscribe_head;
+subscriber_list_t *subscribe_tail;
pointer_state_t *frozen_pointer;
xcb_window_t motion_recorder;
_bspc() {
local commands='window desktop monitor query pointer restore control config quit'
- local settings='rule_command focused_border_color active_border_color normal_border_color presel_border_color focused_locked_border_color active_locked_border_color normal_locked_border_color focused_sticky_border_color normal_sticky_border_color focused_private_border_color active_private_border_color normal_private_border_color urgent_border_color focused_frame_opacity active_frame_opacity normal_frame_opacity border_width window_gap top_padding right_padding bottom_padding left_padding split_ratio growth_factor borderless_monocle gapless_monocle focus_follows_pointer pointer_follows_monitor apply_floating_atom auto_alternate auto_cancel history_aware_focus ignore_ewmh_focus'
+ local settings='rule_command status_prefix focused_border_color active_border_color normal_border_color presel_border_color focused_locked_border_color active_locked_border_color normal_locked_border_color focused_sticky_border_color normal_sticky_border_color focused_private_border_color active_private_border_color normal_private_border_color urgent_border_color focused_frame_opacity active_frame_opacity normal_frame_opacity border_width window_gap top_padding right_padding bottom_padding left_padding split_ratio growth_factor borderless_monocle gapless_monocle focus_follows_pointer pointer_follows_monitor apply_floating_atom auto_alternate auto_cancel history_aware_focus ignore_ewmh_focus'
COMPREPLY=()
fi
export BSPWM_SOCKET=${state_path}/bspwm-socket
-export PANEL_FIFO=${state_path}/panel-fifo
-
-mkfifo "${PANEL_FIFO}"
# Trap: make sure everything started in ~/.config/bspwm/autostart is
# signalled when this script exits or dies. Also clean up $state_path.
[ -r "${sxhkdrc_path}" ] && sxhkd -c "${sxhkdrc_path}" &
# Launch bspwm:
-bspwm -s "${PANEL_FIFO}" -p W
+bspwm
_bspc() {
local -a commands settings
commands=('window' 'desktop' 'monitor' 'query' 'pointer' 'restore' 'control' 'config' 'quit')
- settings=('rule_command' 'focused_border_color' 'active_border_color' 'normal_border_color' 'presel_border_color' 'focused_locked_border_color' 'active_locked_border_color' 'normal_locked_border_color' 'focused_sticky_border_color' 'normal_sticky_border_color' 'focused_private_border_color' 'active_private_border_color' 'normal_private_border_color' 'urgent_border_color' 'focused_frame_opacity' 'active_frame_opacity' 'normal_frame_opacity' 'border_width' 'window_gap' 'top_padding' 'right_padding' 'bottom_padding' 'left_padding' 'split_ratio' 'growth_factor' 'borderless_monocle' 'gapless_monocle' 'focus_follows_pointer' 'pointer_follows_monitor' 'apply_floating_atom' 'auto_alternate' 'auto_cancel' 'history_aware_focus' 'ignore_ewmh_focus')
+ settings=('rule_command' 'status_prefix' 'focused_border_color' 'active_border_color' 'normal_border_color' 'presel_border_color' 'focused_locked_border_color' 'active_locked_border_color' 'normal_locked_border_color' 'focused_sticky_border_color' 'normal_sticky_border_color' 'focused_private_border_color' 'active_private_border_color' 'normal_private_border_color' 'urgent_border_color' 'focused_frame_opacity' 'active_frame_opacity' 'normal_frame_opacity' 'border_width' 'window_gap' 'top_padding' 'right_padding' 'bottom_padding' 'left_padding' 'split_ratio' 'growth_factor' 'borderless_monocle' 'gapless_monocle' 'focus_follows_pointer' 'pointer_follows_monitor' 'apply_floating_atom' 'auto_alternate' 'auto_cancel' 'history_aware_focus' 'ignore_ewmh_focus')
if (( CURRENT == 2 )) ; then
_values 'command' "$commands[@]"
elif (( CURRENT == 3 )) ; then
.\" Title: bspwm
.\" Author: [see the "Author" section]
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
-.\" Date: 11/05/2013
+.\" Date: 11/07/2013
.\" Manual: Bspwm Manual
.\" Source: Bspwm 0.8.6
.\" Language: English
.\"
-.TH "BSPWM" "1" "11/05/2013" "Bspwm 0\&.8\&.6" "Bspwm Manual"
+.TH "BSPWM" "1" "11/07/2013" "Bspwm 0\&.8\&.6" "Bspwm Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
bspwm \- Binary space partitioning window manager
.SH "SYNOPSIS"
.sp
-\fBbspwm\fR [\fB\-h\fR|\fB\-v\fR|\fB\-c\fR \fICONFIG_PATH\fR|\fB\-s\fR \fIPANEL_FIFO\fR|\fB\-p\fR \fIPANEL_PREFIX\fR]
+\fBbspwm\fR [\fB\-h\fR|\fB\-v\fR|\fB\-c\fR \fICONFIG_PATH\fR]
.sp
\fBbspc\fR \fICOMMAND\fR [\fIARGUMENTS\fR]
.SH "DESCRIPTION"
.RS 4
Use the given configuration file\&.
.RE
-.PP
-\fB\-s\fR \fIPANEL_FIFO\fR
-.RS 4
-Write the internal state to the given FIFO\&.
-.RE
-.PP
-\fB\-p\fR \fIPANEL_PREFIX\fR
-.RS 4
-Start every line written to the
-\fIPANEL_FIFO\fR
-with the given prefix\&.
-.RE
.SH "CONFIGURATION"
.sp
\fBbspwm\fR has only two sources of informations: the X events it receives and the messages it reads on a dedicated socket\&.
Manage all the unmanaged windows remaining from a previous session\&.
.RE
.PP
-\fB\-\-put\-status\fR
-.RS 4
-Write the current internal state to the panel FIFO\&.
-.RE
-.PP
\fB\-\-toggle\-visibility\fR
.RS 4
Toggle the visibility of all the windows\&.
.RS 4
Enable or disable the recording of window focus history\&.
.RE
+.PP
+\fB\-\-subscribe\fR
+.RS 4
+Continuously print status informations on standard output\&.
+.RE
.RE
.SS "Pointer"
.sp
\fImonitor\fR\&.
.RE
.PP
+\fIstatus_prefix\fR
+.RS 4
+Prefix prepended to each of the status lines\&.
+.RE
+.PP
\fIfocused_border_color\fR
.RS 4
Color of the border of a focused window of a focused monitor\&.
.RS 4
Window border width\&.
.RE
-.SH "INTERNAL STATE FORMAT"
-.sp
-If a \fIPANEL_FIFO\fR is specified, \fBbspwm\fR will write informations regarding its current state to it\&.
+.SH "STATUS FORMAT"
.sp
-Those informations are composed of items separated by colons\&.
+Status informations are composed of items separated by colons\&.
.sp
Each item as the form \fI<type><value>\fR where \fI<type>\fR is the first character of the item\&.
.PP
Synopsis
--------
-*bspwm* [*-h*|*-v*|*-c* 'CONFIG_PATH'|*-s* 'PANEL_FIFO'|*-p* 'PANEL_PREFIX']
+*bspwm* [*-h*|*-v*|*-c* 'CONFIG_PATH']
*bspc* 'COMMAND' ['ARGUMENTS']
*-c* 'CONFIG_PATH'::
Use the given configuration file.
-*-s* 'PANEL_FIFO'::
- Write the internal state to the given FIFO.
-
-*-p* 'PANEL_PREFIX'::
- Start every line written to the 'PANEL_FIFO' with the given prefix.
-
-
Configuration
-------------
*--adopt-orphans*::
Manage all the unmanaged windows remaining from a previous session.
-*--put-status*::
- Write the current internal state to the panel FIFO.
-
*--toggle-visibility*::
Toggle the visibility of all the windows.
*--record-history* on|off::
Enable or disable the recording of window focus history.
+*--subscribe*::
+ Continuously print status informations on standard output.
+
Pointer
~~~~~~~
'rule_command'::
External command used to retrieve rule consequences. It must contain at least one integer format directive which will be replaced by the ID of the window being processed. The output of that command must have the following format: *key1=value1 key2=value2 ...*, where *keyN* is one of 'floating', 'fullscreen', 'locked', 'sticky', 'private', 'frame', 'center', 'lower', 'follow', 'manage', 'focus', 'desktop' or 'monitor'.
+'status_prefix'::
+ Prefix prepended to each of the status lines.
+
'focused_border_color'::
Color of the border of a focused window of a focused monitor.
Window border width.
-Internal State Format
----------------------
-
-If a 'PANEL_FIFO' is specified, *bspwm* will write informations regarding its current state to it.
+Status Format
+-------------
-Those informations are composed of items separated by colons.
+Status informations are composed of items separated by colons.
Each item as the form '<type><value>' where '<type>' is the first character of the item.
trap 'trap - TERM; kill 0' INT TERM QUIT EXIT
+sleep 0.1
+
flavor=${1:-bar}
+[ -e "$PANEL_FIFO" ] && rm "$PANEL_FIFO"
+mkfifo "$PANEL_FIFO"
+
bspc config top_padding $PANEL_HEIGHT
-bspc control --put-status
+
+bspc control --subscribe > "$PANEL_FIFO" &
xtitle -sf 'T%s' > "$PANEL_FIFO" &
clock -sf 'S%a %H:%M' > "$PANEL_FIFO" &
+++ /dev/null
-[ -e "$PANEL_FIFO" ] && rm "$PANEL_FIFO"
-mkfifo "$PANEL_FIFO"
-exec bspwm -s "$PANEL_FIFO" -p W
} else if (streq("restore", *args)) {
return cmd_restore(++args, --num);
} else if (streq("control", *args)) {
- return cmd_control(++args, --num);
+ return cmd_control(++args, --num, rsp);
} else if (streq("pointer", *args)) {
return cmd_pointer(++args, --num);
} else if (streq("config", *args)) {
return true;
}
-bool cmd_control(char **args, int num)
+bool cmd_control(char **args, int num, char *rsp)
{
if (num < 1)
return false;
put_status();
} else if (streq("--toggle-visibility", *args)) {
toggle_visibility();
+ } else if (streq("--subscribe", *args)) {
+ snprintf(rsp, BUFSIZ, "%c", MESSAGE_SUBSCRIBE);
} else if (streq("--record-history", *args)) {
num--, args++;
if (num < 1)
MONSET(bottom_padding)
MONSET(left_padding)
#undef MONSET
- } else if (streq("rule_command", name)) {
- return snprintf(rule_command, sizeof(rule_command), "%s", value) >= 0;
+#define SETSTR(s) \
+ } else if (streq(#s, name)) { \
+ return snprintf(s, sizeof(s), "%s", value) >= 0;
+ SETSTR(rule_command)
+ SETSTR(status_prefix)
+#undef SETSTR
} else if (streq("split_ratio", name)) {
double r;
if (sscanf(value, "%lf", &r) == 1 && r > 0 && r < 1)
snprintf(rsp, BUFSIZ, "%u", loc.desktop->border_width);
else if (streq("rule_command", name))
snprintf(rsp, BUFSIZ, "%s", rule_command);
+ else if (streq("status_prefix", name))
+ snprintf(rsp, BUFSIZ, "%s", status_prefix);
#define MONGET(k) \
else if (streq(#k, name)) \
if (loc.monitor == NULL) \
#define CAT_CHR '.'
#define EQL_TOK "="
+#define MESSAGE_SUBSCRIBE '\x01'
+
bool handle_message(char *msg, int msg_len, char *rsp);
bool process_message(char **args, int num, char *rsp);
bool cmd_window(char **args, int num);
bool cmd_query(char **args, int num, char *rsp);
bool cmd_pointer(char **args, int num);
bool cmd_restore(char **args, int num);
-bool cmd_control(char **args, int num);
+bool cmd_control(char **args, int num, char *rsp);
bool cmd_config(char **args, int num, char *rsp);
bool cmd_quit(char **args, int num);
bool set_setting(coordinates_t loc, char *name, char *value);
void load_settings(void)
{
snprintf(rule_command, sizeof(rule_command), "%s", RULE_COMMAND);
+ snprintf(status_prefix, sizeof(status_prefix), "%s", STATUS_PREFIX);
snprintf(normal_border_color, sizeof(normal_border_color), "%s", NORMAL_BORDER_COLOR);
snprintf(focused_border_color, sizeof(focused_border_color), "%s", FOCUSED_BORDER_COLOR);
#define CONFIG_NAME WM_NAME "rc"
#define CONFIG_HOME_ENV "XDG_CONFIG_HOME"
#define RULE_COMMAND "true"
+#define STATUS_PREFIX "W"
#define FOCUSED_BORDER_COLOR "#7E7F89"
#define ACTIVE_BORDER_COLOR "#545350"
#define IGNORE_EWMH_FOCUS false
char rule_command[MAXLEN];
+char status_prefix[MAXLEN];
char focused_border_color[MAXLEN];
char active_border_color[MAXLEN];
--- /dev/null
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include "bspwm.h"
+#include "tree.h"
+#include "settings.h"
+#include "subscribe.h"
+
+subscriber_list_t *make_subscriber_list(int fd)
+{
+ subscriber_list_t *sb = malloc(sizeof(subscriber_list_t));
+ sb->prev = sb->next = NULL;
+ sb->fd = fd;
+ sb->stream = fdopen(fd, "w");
+ if (sb->stream == NULL) {
+ warn("Can't open subscriber %i\n", fd);
+ close(fd);
+ free(sb);
+ return NULL;
+ }
+ return sb;
+}
+
+void remove_subscriber(subscriber_list_t *sb)
+{
+ if (sb == NULL)
+ return;
+ subscriber_list_t *a = sb->prev;
+ subscriber_list_t *b = sb->next;
+ if (a != NULL)
+ a->next = b;
+ if (b != NULL)
+ b->prev = a;
+ if (sb == subscribe_head)
+ subscribe_head = b;
+ if (sb == subscribe_tail)
+ subscribe_tail = a;
+ fclose(sb->stream);
+ free(sb);
+}
+
+void add_subscriber(int fd)
+{
+ subscriber_list_t *sb = make_subscriber_list(fd);
+ if (sb == NULL)
+ return;
+ if (subscribe_head == NULL) {
+ subscribe_head = subscribe_tail = sb;
+ } else {
+ subscribe_tail->next = sb;
+ sb->prev = subscribe_tail;
+ subscribe_tail = sb;
+ }
+ feed_subscriber(sb);
+}
+
+void feed_subscriber(subscriber_list_t *sb)
+{
+ fprintf(sb->stream, "%s", status_prefix);
+ bool urgent = false;
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ fprintf(sb->stream, "%c%s:", (mon == m ? 'M' : 'm'), m->name);
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next, urgent = false) {
+ for (node_t *n = first_extrema(d->root); n != NULL && !urgent; n = next_leaf(n, d->root))
+ urgent |= n->client->urgent;
+ char c = (urgent ? 'u' : (d->root == NULL ? 'f' : 'o'));
+ if (m->desk == d)
+ c = toupper(c);
+ fprintf(sb->stream, "%c%s:", c, d->name);
+ }
+ }
+ if (mon != NULL && mon->desk != NULL)
+ fprintf(sb->stream, "L%s", (mon->desk->layout == LAYOUT_TILED ? "tiled" : "monocle"));
+ int ret = fflush(sb->stream);
+ if (ret != 0)
+ remove_subscriber(sb);
+}
--- /dev/null
+#ifndef BSPWM_SUBSCRIBE_H
+#define BSPWM_SUBSCRIBE_H
+
+subscriber_list_t *make_subscriber_list(int fd);
+void remove_subscriber(subscriber_list_t *sb);
+void add_subscriber(int fd);
+void feed_subscriber(subscriber_list_t *sb);
+
+#endif
stacking_list_t *next;
};
+typedef struct subscriber_list_t subscriber_list_t;
+struct subscriber_list_t {
+ int fd;
+ FILE *stream;
+ subscriber_list_t *prev;
+ subscriber_list_t *next;
+};
+
typedef struct {
char desktop_desc[MAXLEN];
char monitor_desc[MAXLEN];