add i3-nagbar. tells you about config file errors (for example)
This commit is contained in:
parent
b63a559c28
commit
c55abca115
|
@ -8,6 +8,7 @@ SYSCONFDIR=/etc
|
||||||
else
|
else
|
||||||
SYSCONFDIR=$(PREFIX)/etc
|
SYSCONFDIR=$(PREFIX)/etc
|
||||||
endif
|
endif
|
||||||
|
TERM_EMU=xterm
|
||||||
# The escaping is absurd, but we need to escape for shell, sed, make, define
|
# The escaping is absurd, but we need to escape for shell, sed, make, define
|
||||||
GIT_VERSION:="$(shell git describe --tags --always) ($(shell git log --pretty=format:%cd --date=short -n1), branch $(shell [ -f .git/HEAD ] && sed 's/ref: refs\/heads\/\(.*\)/\\\\\\"\1\\\\\\"/g' .git/HEAD || echo 'unknown'))"
|
GIT_VERSION:="$(shell git describe --tags --always) ($(shell git log --pretty=format:%cd --date=short -n1), branch $(shell [ -f .git/HEAD ] && sed 's/ref: refs\/heads\/\(.*\)/\\\\\\"\1\\\\\\"/g' .git/HEAD || echo 'unknown'))"
|
||||||
VERSION:=$(shell git describe --tags --abbrev=0)
|
VERSION:=$(shell git describe --tags --abbrev=0)
|
||||||
|
@ -46,6 +47,7 @@ CFLAGS += $(call cflags_for_lib, yajl)
|
||||||
CFLAGS += $(call cflags_for_lib, libev)
|
CFLAGS += $(call cflags_for_lib, libev)
|
||||||
CFLAGS += -DI3_VERSION=\"${GIT_VERSION}\"
|
CFLAGS += -DI3_VERSION=\"${GIT_VERSION}\"
|
||||||
CFLAGS += -DSYSCONFDIR=\"${SYSCONFDIR}\"
|
CFLAGS += -DSYSCONFDIR=\"${SYSCONFDIR}\"
|
||||||
|
CFLAGS += -DTERM_EMU=\"$(TERM_EMU)\"
|
||||||
|
|
||||||
LDFLAGS += -lm
|
LDFLAGS += -lm
|
||||||
LDFLAGS += $(call ldflags_for_lib, xcb-event, xcb-event)
|
LDFLAGS += $(call ldflags_for_lib, xcb-event, xcb-event)
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Default value so one can compile i3-nagbar standalone
|
||||||
|
TOPDIR=..
|
||||||
|
|
||||||
|
include $(TOPDIR)/common.mk
|
||||||
|
|
||||||
|
# Depend on the object files of all source-files in src/*.c and on all header files
|
||||||
|
FILES=$(patsubst %.c,%.o,$(wildcard *.c))
|
||||||
|
HEADERS=$(wildcard *.h)
|
||||||
|
|
||||||
|
# Depend on the specific file (.c for each .o) and on all headers
|
||||||
|
%.o: %.c ${HEADERS}
|
||||||
|
echo "CC $<"
|
||||||
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
all: ${FILES}
|
||||||
|
echo "LINK i3-nagbar"
|
||||||
|
$(CC) -o i3-nagbar ${FILES} $(LDFLAGS)
|
||||||
|
|
||||||
|
install: all
|
||||||
|
echo "INSTALL"
|
||||||
|
$(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
|
||||||
|
$(INSTALL) -m 0755 i3-nagbar $(DESTDIR)$(PREFIX)/bin/
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o
|
||||||
|
|
||||||
|
distclean: clean
|
||||||
|
rm -f i3-nagbar
|
|
@ -0,0 +1,6 @@
|
||||||
|
xmacro(_NET_WM_WINDOW_TYPE)
|
||||||
|
xmacro(_NET_WM_WINDOW_TYPE_DOCK)
|
||||||
|
xmacro(_NET_WM_STRUT_PARTIAL)
|
||||||
|
xmacro(I3_SOCKET_PATH)
|
||||||
|
xmacro(ATOM)
|
||||||
|
xmacro(CARDINAL)
|
|
@ -0,0 +1,26 @@
|
||||||
|
#ifndef _I3_NAGBAR
|
||||||
|
#define _I3_NAGBAR
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
|
||||||
|
#define die(...) errx(EXIT_FAILURE, __VA_ARGS__);
|
||||||
|
#define FREE(pointer) do { \
|
||||||
|
if (pointer != NULL) { \
|
||||||
|
free(pointer); \
|
||||||
|
pointer = NULL; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
while (0)
|
||||||
|
|
||||||
|
#define xmacro(atom) xcb_atom_t A_ ## atom;
|
||||||
|
#include "atoms.xmacro"
|
||||||
|
#undef xmacro
|
||||||
|
|
||||||
|
extern xcb_window_t root;
|
||||||
|
|
||||||
|
uint32_t get_colorpixel(xcb_connection_t *conn, char *hex);
|
||||||
|
xcb_window_t open_input_window(xcb_connection_t *conn, uint32_t width, uint32_t height);
|
||||||
|
int get_font_id(xcb_connection_t *conn, char *pattern, int *font_height);
|
||||||
|
void xcb_change_gc_single(xcb_connection_t *conn, xcb_gcontext_t gc, uint32_t mask, uint32_t value);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,392 @@
|
||||||
|
/*
|
||||||
|
* vim:ts=4:sw=4:expandtab
|
||||||
|
*
|
||||||
|
* i3 - an improved dynamic tiling window manager
|
||||||
|
*
|
||||||
|
* © 2009-2011 Michael Stapelberg and contributors
|
||||||
|
*
|
||||||
|
* See file LICENSE for license information.
|
||||||
|
*
|
||||||
|
* i3-nagbar is a utility which displays a nag message.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <ev.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include <xcb/xcb.h>
|
||||||
|
#include <xcb/xcb_aux.h>
|
||||||
|
#include <xcb/xcb_event.h>
|
||||||
|
|
||||||
|
#include "i3-nagbar.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *label;
|
||||||
|
char *action;
|
||||||
|
int16_t x;
|
||||||
|
uint16_t width;
|
||||||
|
} button_t;
|
||||||
|
|
||||||
|
static xcb_window_t win;
|
||||||
|
static xcb_pixmap_t pixmap;
|
||||||
|
static xcb_gcontext_t pixmap_gc;
|
||||||
|
static xcb_rectangle_t rect = { 0, 0, 600, 20 };
|
||||||
|
static char *glyphs_ucs[512];
|
||||||
|
static int input_position;
|
||||||
|
static int font_height;
|
||||||
|
static char *prompt = "You have an error in your i3 config file!";
|
||||||
|
static button_t *buttons;
|
||||||
|
static int buttoncnt;
|
||||||
|
xcb_window_t root;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Starts the given application by passing it through a shell. We use double fork
|
||||||
|
* to avoid zombie processes. As the started application’s parent exits (immediately),
|
||||||
|
* the application is reparented to init (process-id 1), which correctly handles
|
||||||
|
* childs, so we don’t have to do it :-).
|
||||||
|
*
|
||||||
|
* The shell is determined by looking for the SHELL environment variable. If it
|
||||||
|
* does not exist, /bin/sh is used.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static void start_application(const char *command) {
|
||||||
|
printf("executing: %s\n", command);
|
||||||
|
if (fork() == 0) {
|
||||||
|
/* Child process */
|
||||||
|
setsid();
|
||||||
|
if (fork() == 0) {
|
||||||
|
/* Stores the path of the shell */
|
||||||
|
static const char *shell = NULL;
|
||||||
|
|
||||||
|
if (shell == NULL)
|
||||||
|
if ((shell = getenv("SHELL")) == NULL)
|
||||||
|
shell = "/bin/sh";
|
||||||
|
|
||||||
|
/* This is the child */
|
||||||
|
execl(shell, shell, "-c", command, (void*)NULL);
|
||||||
|
/* not reached */
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
wait(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static button_t *get_button_at(int16_t x, int16_t y) {
|
||||||
|
for (int c = 0; c < buttoncnt; c++)
|
||||||
|
if (x >= (buttons[c].x) && x <= (buttons[c].x + buttons[c].width))
|
||||||
|
return &buttons[c];
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_button_press(xcb_connection_t *conn, xcb_button_press_event_t *event) {
|
||||||
|
printf("button pressed on x = %d, y = %d\n",
|
||||||
|
event->event_x, event->event_y);
|
||||||
|
/* TODO: set a flag for the button, re-render */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called when the user releases the mouse button. Checks whether the
|
||||||
|
* coordinates are over a button and executes the appropriate action.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static void handle_button_release(xcb_connection_t *conn, xcb_button_release_event_t *event) {
|
||||||
|
printf("button released on x = %d, y = %d\n",
|
||||||
|
event->event_x, event->event_y);
|
||||||
|
/* If the user hits the close button, we exit(0) */
|
||||||
|
if (event->event_x >= (rect.width - 32))
|
||||||
|
exit(0);
|
||||||
|
button_t *button = get_button_at(event->event_x, event->event_y);
|
||||||
|
if (!button)
|
||||||
|
return;
|
||||||
|
start_application(button->action);
|
||||||
|
|
||||||
|
/* TODO: unset flag, re-render */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handles expose events (redraws of the window) and rendering in general. Will
|
||||||
|
* be called from the code with event == NULL or from X with event != NULL.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
|
||||||
|
printf("expose!\n");
|
||||||
|
|
||||||
|
/* re-draw the background */
|
||||||
|
xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#900000"));
|
||||||
|
xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &rect);
|
||||||
|
|
||||||
|
/* restore font color */
|
||||||
|
uint32_t values[3];
|
||||||
|
values[0] = get_colorpixel(conn, "#FFFFFF");
|
||||||
|
values[1] = get_colorpixel(conn, "#900000");
|
||||||
|
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND, values);
|
||||||
|
xcb_image_text_8(conn, strlen(prompt), pixmap, pixmap_gc, 4 + 4/* X */,
|
||||||
|
font_height + 2 + 4 /* Y = baseline of font */, prompt);
|
||||||
|
|
||||||
|
/* render close button */
|
||||||
|
int line_width = 4;
|
||||||
|
int w = 20;
|
||||||
|
int y = rect.width;
|
||||||
|
values[0] = get_colorpixel(conn, "#680a0a");
|
||||||
|
values[1] = line_width;
|
||||||
|
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_LINE_WIDTH, values);
|
||||||
|
|
||||||
|
xcb_rectangle_t close = { y - w - (2 * line_width), 0, w + (2 * line_width), rect.height };
|
||||||
|
xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &close);
|
||||||
|
|
||||||
|
xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#d92424"));
|
||||||
|
xcb_point_t points[] = {
|
||||||
|
{ y - w - (2 * line_width), line_width / 2 },
|
||||||
|
{ y - (line_width / 2), line_width / 2 },
|
||||||
|
{ y - (line_width / 2), (rect.height - (line_width / 2)) - 2 },
|
||||||
|
{ y - w - (2 * line_width), (rect.height - (line_width / 2)) - 2 },
|
||||||
|
{ y - w - (2 * line_width), line_width / 2 }
|
||||||
|
};
|
||||||
|
xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, pixmap, pixmap_gc, 5, points);
|
||||||
|
|
||||||
|
values[0] = get_colorpixel(conn, "#ffffff");
|
||||||
|
values[1] = get_colorpixel(conn, "#680a0a");
|
||||||
|
values[2] = 1;
|
||||||
|
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_LINE_WIDTH, values);
|
||||||
|
xcb_image_text_8(conn, strlen("x"), pixmap, pixmap_gc, y - w - line_width + (w / 2) - 4/* X */,
|
||||||
|
font_height + 2 + 4 - 1/* Y = baseline of font */, "X");
|
||||||
|
y -= w;
|
||||||
|
|
||||||
|
y -= 20;
|
||||||
|
|
||||||
|
/* render custom buttons */
|
||||||
|
line_width = 1;
|
||||||
|
for (int c = 0; c < buttoncnt; c++) {
|
||||||
|
/* TODO: make w = text extents of the label */
|
||||||
|
w = 90;
|
||||||
|
y -= 30;
|
||||||
|
xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#680a0a"));
|
||||||
|
close = (xcb_rectangle_t){ y - w - (2 * line_width), 2, w + (2 * line_width), rect.height - 6 };
|
||||||
|
xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &close);
|
||||||
|
|
||||||
|
xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#d92424"));
|
||||||
|
buttons[c].x = y - w - (2 * line_width);
|
||||||
|
buttons[c].width = w;
|
||||||
|
xcb_point_t points2[] = {
|
||||||
|
{ y - w - (2 * line_width), (line_width / 2) + 2 },
|
||||||
|
{ y - (line_width / 2), (line_width / 2) + 2 },
|
||||||
|
{ y - (line_width / 2), (rect.height - 4 - (line_width / 2)) },
|
||||||
|
{ y - w - (2 * line_width), (rect.height - 4 - (line_width / 2)) },
|
||||||
|
{ y - w - (2 * line_width), (line_width / 2) + 2 }
|
||||||
|
};
|
||||||
|
xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, pixmap, pixmap_gc, 5, points2);
|
||||||
|
|
||||||
|
values[0] = get_colorpixel(conn, "#ffffff");
|
||||||
|
values[1] = get_colorpixel(conn, "#680a0a");
|
||||||
|
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND, values);
|
||||||
|
xcb_image_text_8(conn, strlen(buttons[c].label), pixmap, pixmap_gc, y - w - line_width + 6/* X */,
|
||||||
|
font_height + 2 + 3/* Y = baseline of font */, buttons[c].label);
|
||||||
|
|
||||||
|
y -= w;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* border line at the bottom */
|
||||||
|
line_width = 2;
|
||||||
|
values[0] = get_colorpixel(conn, "#470909");
|
||||||
|
values[1] = line_width;
|
||||||
|
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_LINE_WIDTH, values);
|
||||||
|
xcb_point_t bottom[] = {
|
||||||
|
{ 0, rect.height - 0 },
|
||||||
|
{ rect.width, rect.height - 0 }
|
||||||
|
};
|
||||||
|
xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, pixmap, pixmap_gc, 2, bottom);
|
||||||
|
|
||||||
|
|
||||||
|
/* Copy the contents of the pixmap to the real window */
|
||||||
|
xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, rect.width, rect.height);
|
||||||
|
xcb_flush(conn);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
char *pattern = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1";
|
||||||
|
int o, option_index = 0;
|
||||||
|
|
||||||
|
static struct option long_options[] = {
|
||||||
|
{"version", no_argument, 0, 'v'},
|
||||||
|
{"font", required_argument, 0, 'f'},
|
||||||
|
{"button", required_argument, 0, 'b'},
|
||||||
|
{"help", no_argument, 0, 'h'},
|
||||||
|
{0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
char *options_string = "b:f:vh";
|
||||||
|
|
||||||
|
while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
|
||||||
|
switch (o) {
|
||||||
|
case 'v':
|
||||||
|
printf("i3-nagbar " I3_VERSION);
|
||||||
|
return 0;
|
||||||
|
case 'f':
|
||||||
|
FREE(pattern);
|
||||||
|
pattern = strdup(optarg);
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
printf("i3-nagbar " I3_VERSION);
|
||||||
|
printf("i3-nagbar [-s <socket>] [-p <prefix>] [-l <limit>] [-P <prompt>] [-f <font>] [-v]\n");
|
||||||
|
return 0;
|
||||||
|
case 'b':
|
||||||
|
buttons = realloc(buttons, sizeof(button_t) * (buttoncnt + 1));
|
||||||
|
buttons[buttoncnt].label = optarg;
|
||||||
|
buttons[buttoncnt].action = argv[optind];
|
||||||
|
printf("button with label *%s* and action *%s*\n",
|
||||||
|
buttons[buttoncnt].label,
|
||||||
|
buttons[buttoncnt].action);
|
||||||
|
buttoncnt++;
|
||||||
|
printf("now %d buttons\n", buttoncnt);
|
||||||
|
if (optind < argc)
|
||||||
|
optind++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int screens;
|
||||||
|
xcb_connection_t *conn = xcb_connect(NULL, &screens);
|
||||||
|
if (xcb_connection_has_error(conn))
|
||||||
|
die("Cannot open display\n");
|
||||||
|
|
||||||
|
/* Place requests for the atoms we need as soon as possible */
|
||||||
|
#define xmacro(atom) \
|
||||||
|
xcb_intern_atom_cookie_t atom ## _cookie = xcb_intern_atom(conn, 0, strlen(#atom), #atom);
|
||||||
|
#include "atoms.xmacro"
|
||||||
|
#undef xmacro
|
||||||
|
|
||||||
|
xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screens);
|
||||||
|
root = root_screen->root;
|
||||||
|
|
||||||
|
uint32_t font_id = get_font_id(conn, pattern, &font_height);
|
||||||
|
|
||||||
|
/* Open an input window */
|
||||||
|
win = open_input_window(conn, 500, font_height + 8 + 8 /* 8px padding */);
|
||||||
|
|
||||||
|
/* Setup NetWM atoms */
|
||||||
|
#define xmacro(name) \
|
||||||
|
do { \
|
||||||
|
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, name ## _cookie, NULL); \
|
||||||
|
if (!reply) \
|
||||||
|
die("Could not get atom " # name "\n"); \
|
||||||
|
\
|
||||||
|
A_ ## name = reply->atom; \
|
||||||
|
free(reply); \
|
||||||
|
} while (0);
|
||||||
|
#include "atoms.xmacro"
|
||||||
|
#undef xmacro
|
||||||
|
|
||||||
|
/* Set dock mode */
|
||||||
|
xcb_void_cookie_t dock_cookie = xcb_change_property(conn,
|
||||||
|
XCB_PROP_MODE_REPLACE,
|
||||||
|
win,
|
||||||
|
A__NET_WM_WINDOW_TYPE,
|
||||||
|
A_ATOM,
|
||||||
|
32,
|
||||||
|
1,
|
||||||
|
(unsigned char*) &A__NET_WM_WINDOW_TYPE_DOCK);
|
||||||
|
|
||||||
|
/* Reserve some space at the top of the screen */
|
||||||
|
struct {
|
||||||
|
uint32_t left;
|
||||||
|
uint32_t right;
|
||||||
|
uint32_t top;
|
||||||
|
uint32_t bottom;
|
||||||
|
uint32_t left_start_y;
|
||||||
|
uint32_t left_end_y;
|
||||||
|
uint32_t right_start_y;
|
||||||
|
uint32_t right_end_y;
|
||||||
|
uint32_t top_start_x;
|
||||||
|
uint32_t top_end_x;
|
||||||
|
uint32_t bottom_start_x;
|
||||||
|
uint32_t bottom_end_x;
|
||||||
|
} __attribute__((__packed__)) strut_partial = {0,};
|
||||||
|
|
||||||
|
strut_partial.top = font_height + 6;
|
||||||
|
strut_partial.top_start_x = 0;
|
||||||
|
strut_partial.top_end_x = 800;
|
||||||
|
|
||||||
|
xcb_void_cookie_t strut_cookie = xcb_change_property(conn,
|
||||||
|
XCB_PROP_MODE_REPLACE,
|
||||||
|
win,
|
||||||
|
A__NET_WM_STRUT_PARTIAL,
|
||||||
|
A_CARDINAL,
|
||||||
|
32,
|
||||||
|
12,
|
||||||
|
&strut_partial);
|
||||||
|
|
||||||
|
/* Create pixmap */
|
||||||
|
pixmap = xcb_generate_id(conn);
|
||||||
|
pixmap_gc = xcb_generate_id(conn);
|
||||||
|
xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, 500, font_height + 8);
|
||||||
|
xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0);
|
||||||
|
|
||||||
|
/* Create graphics context */
|
||||||
|
xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FONT, font_id);
|
||||||
|
|
||||||
|
/* Grab the keyboard to get all input */
|
||||||
|
xcb_flush(conn);
|
||||||
|
|
||||||
|
xcb_generic_event_t *event;
|
||||||
|
while ((event = xcb_wait_for_event(conn)) != NULL) {
|
||||||
|
if (event->response_type == 0) {
|
||||||
|
fprintf(stderr, "X11 Error received! sequence %x\n", event->sequence);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Strip off the highest bit (set if the event is generated) */
|
||||||
|
int type = (event->response_type & 0x7F);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case XCB_EXPOSE:
|
||||||
|
handle_expose(conn, (xcb_expose_event_t*)event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case XCB_BUTTON_PRESS:
|
||||||
|
handle_button_press(conn, (xcb_button_press_event_t*)event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case XCB_BUTTON_RELEASE:
|
||||||
|
handle_button_release(conn, (xcb_button_release_event_t*)event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case XCB_CONFIGURE_NOTIFY: {
|
||||||
|
xcb_configure_notify_event_t *configure_notify = (xcb_configure_notify_event_t*)event;
|
||||||
|
rect = (xcb_rectangle_t){
|
||||||
|
configure_notify->x,
|
||||||
|
configure_notify->y,
|
||||||
|
configure_notify->width,
|
||||||
|
configure_notify->height
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Recreate the pixmap / gc */
|
||||||
|
xcb_free_pixmap(conn, pixmap);
|
||||||
|
xcb_free_gc(conn, pixmap_gc);
|
||||||
|
|
||||||
|
xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, rect.width, rect.height);
|
||||||
|
xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0);
|
||||||
|
|
||||||
|
/* Create graphics context */
|
||||||
|
xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FONT, font_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
/*
|
||||||
|
* vim:ts=8:expandtab
|
||||||
|
*
|
||||||
|
* i3 - an improved dynamic tiling window manager
|
||||||
|
*
|
||||||
|
* © 2009 Michael Stapelberg and contributors
|
||||||
|
*
|
||||||
|
* See file LICENSE for license information.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <xcb/xcb.h>
|
||||||
|
#include <xcb/xcb_keysyms.h>
|
||||||
|
|
||||||
|
#include <X11/keysym.h>
|
||||||
|
|
||||||
|
#include "i3-nagbar.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convenience-wrapper around xcb_change_gc which saves us declaring a variable
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void xcb_change_gc_single(xcb_connection_t *conn, xcb_gcontext_t gc, uint32_t mask, uint32_t value) {
|
||||||
|
xcb_change_gc(conn, gc, mask, &value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the colorpixel to use for the given hex color (think of HTML).
|
||||||
|
*
|
||||||
|
* The hex_color has to start with #, for example #FF00FF.
|
||||||
|
*
|
||||||
|
* NOTE that get_colorpixel() does _NOT_ check the given color code for validity.
|
||||||
|
* This has to be done by the caller.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
uint32_t get_colorpixel(xcb_connection_t *conn, char *hex) {
|
||||||
|
char strgroups[3][3] = {{hex[1], hex[2], '\0'},
|
||||||
|
{hex[3], hex[4], '\0'},
|
||||||
|
{hex[5], hex[6], '\0'}};
|
||||||
|
uint32_t rgb16[3] = {(strtol(strgroups[0], NULL, 16)),
|
||||||
|
(strtol(strgroups[1], NULL, 16)),
|
||||||
|
(strtol(strgroups[2], NULL, 16))};
|
||||||
|
|
||||||
|
return (rgb16[0] << 16) + (rgb16[1] << 8) + rgb16[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Opens the window we use for input/output and maps it
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
xcb_window_t open_input_window(xcb_connection_t *conn, uint32_t width, uint32_t height) {
|
||||||
|
xcb_window_t win = xcb_generate_id(conn);
|
||||||
|
//xcb_cursor_t cursor_id = xcb_generate_id(conn);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* Use the default cursor (left pointer) */
|
||||||
|
if (cursor > -1) {
|
||||||
|
i3Font *cursor_font = load_font(conn, "cursor");
|
||||||
|
xcb_create_glyph_cursor(conn, cursor_id, cursor_font->id, cursor_font->id,
|
||||||
|
XCB_CURSOR_LEFT_PTR, XCB_CURSOR_LEFT_PTR + 1,
|
||||||
|
0, 0, 0, 65535, 65535, 65535);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint32_t mask = 0;
|
||||||
|
uint32_t values[3];
|
||||||
|
|
||||||
|
mask |= XCB_CW_BACK_PIXEL;
|
||||||
|
values[0] = 0;
|
||||||
|
|
||||||
|
mask |= XCB_CW_EVENT_MASK;
|
||||||
|
values[1] = XCB_EVENT_MASK_EXPOSURE |
|
||||||
|
XCB_EVENT_MASK_STRUCTURE_NOTIFY |
|
||||||
|
XCB_EVENT_MASK_BUTTON_PRESS |
|
||||||
|
XCB_EVENT_MASK_BUTTON_RELEASE;
|
||||||
|
|
||||||
|
xcb_create_window(conn,
|
||||||
|
XCB_COPY_FROM_PARENT,
|
||||||
|
win, /* the window id */
|
||||||
|
root, /* parent == root */
|
||||||
|
50, 50, width, height, /* dimensions */
|
||||||
|
0, /* border = 0, we draw our own */
|
||||||
|
XCB_WINDOW_CLASS_INPUT_OUTPUT,
|
||||||
|
XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
|
||||||
|
mask,
|
||||||
|
values);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (cursor > -1)
|
||||||
|
xcb_change_window_attributes(conn, result, XCB_CW_CURSOR, &cursor_id);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Map the window (= make it visible) */
|
||||||
|
xcb_map_window(conn, win);
|
||||||
|
|
||||||
|
return win;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the ID of the font matching the given pattern and stores the height
|
||||||
|
* of the font (in pixels) in *font_height. die()s if no font matches.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int get_font_id(xcb_connection_t *conn, char *pattern, int *font_height) {
|
||||||
|
xcb_void_cookie_t font_cookie;
|
||||||
|
xcb_list_fonts_with_info_cookie_t info_cookie;
|
||||||
|
|
||||||
|
/* Send all our requests first */
|
||||||
|
int result;
|
||||||
|
result = xcb_generate_id(conn);
|
||||||
|
font_cookie = xcb_open_font_checked(conn, result, strlen(pattern), pattern);
|
||||||
|
info_cookie = xcb_list_fonts_with_info(conn, 1, strlen(pattern), pattern);
|
||||||
|
|
||||||
|
xcb_generic_error_t *error = xcb_request_check(conn, font_cookie);
|
||||||
|
if (error != NULL) {
|
||||||
|
fprintf(stderr, "ERROR: Could not open font: %d\n", error->error_code);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get information (height/name) for this font */
|
||||||
|
xcb_list_fonts_with_info_reply_t *reply = xcb_list_fonts_with_info_reply(conn, info_cookie, NULL);
|
||||||
|
if (reply == NULL)
|
||||||
|
die("Could not load font \"%s\"\n", pattern);
|
||||||
|
|
||||||
|
*font_height = reply->font_ascent + reply->font_descent;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -32,6 +32,8 @@ extern SLIST_HEAD(modes_head, Mode) modes;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
struct context {
|
struct context {
|
||||||
|
bool has_errors;
|
||||||
|
|
||||||
int line_number;
|
int line_number;
|
||||||
char *line_copy;
|
char *line_copy;
|
||||||
const char *filename;
|
const char *filename;
|
||||||
|
@ -190,6 +192,17 @@ void switch_mode(const char *new_mode);
|
||||||
*/
|
*/
|
||||||
Binding *get_binding(uint16_t modifiers, xcb_keycode_t keycode);
|
Binding *get_binding(uint16_t modifiers, xcb_keycode_t keycode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kills the configerror i3-nagbar process, if any.
|
||||||
|
*
|
||||||
|
* Called when reloading/restarting.
|
||||||
|
*
|
||||||
|
* If wait_for_it is set (restarting), this function will waitpid(), otherwise,
|
||||||
|
* ev is assumed to handle it (reloading).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void kill_configerror_nagbar(bool wait_for_it);
|
||||||
|
|
||||||
/* prototype for src/cfgparse.y */
|
/* prototype for src/cfgparse.y */
|
||||||
void parse_file(const char *f);
|
void parse_file(const char *f);
|
||||||
|
|
||||||
|
|
|
@ -32,5 +32,6 @@ extern SLIST_HEAD(stack_wins_head, Stack_Window) stack_wins;
|
||||||
extern uint8_t root_depth;
|
extern uint8_t root_depth;
|
||||||
extern bool xcursor_supported, xkb_supported;
|
extern bool xcursor_supported, xkb_supported;
|
||||||
extern xcb_window_t root;
|
extern xcb_window_t root;
|
||||||
|
extern struct ev_loop *main_loop;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/*
|
/*
|
||||||
* vim:ts=8:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
* i3 - an improved dynamic tiling window manager
|
* i3 - an improved dynamic tiling window manager
|
||||||
*
|
*
|
||||||
* © 2009-2010 Michael Stapelberg and contributors
|
* © 2009-2011 Michael Stapelberg and contributors
|
||||||
*
|
*
|
||||||
* See file LICENSE for license information.
|
* See file LICENSE for license information.
|
||||||
*
|
*
|
||||||
|
@ -21,6 +21,14 @@
|
||||||
#define DLOG(fmt, ...) debuglog(LOGLEVEL, "%s:%s:%d - " fmt, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
#define DLOG(fmt, ...) debuglog(LOGLEVEL, "%s:%s:%d - " fmt, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||||
|
|
||||||
extern char *loglevels[];
|
extern char *loglevels[];
|
||||||
|
extern char *errorfilename;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes logging by creating an error logfile in /tmp (or
|
||||||
|
* XDG_RUNTIME_DIR, see get_process_filename()).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void init_logging();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables the given loglevel.
|
* Enables the given loglevel.
|
||||||
|
|
|
@ -102,6 +102,23 @@ char *sstrdup(const char *str);
|
||||||
*/
|
*/
|
||||||
void start_application(const char *command);
|
void start_application(const char *command);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* exec()s an i3 utility, for example the config file migration script or
|
||||||
|
* i3-nagbar. This function first searches $PATH for the given utility named,
|
||||||
|
* then falls back to the dirname() of the i3 executable path and then falls
|
||||||
|
* back to the dirname() of the target of /proc/self/exe (on linux).
|
||||||
|
*
|
||||||
|
* This function should be called after fork()ing.
|
||||||
|
*
|
||||||
|
* The first argument of the given argv vector will be overwritten with the
|
||||||
|
* executable name, so pass NULL.
|
||||||
|
*
|
||||||
|
* If the utility cannot be found in any of these locations, it exits with
|
||||||
|
* return code 2.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void exec_i3_utility(char *name, char *argv[]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks a generic cookie for errors and quits with the given message if
|
* Checks a generic cookie for errors and quits with the given message if
|
||||||
* there was an error.
|
* there was an error.
|
||||||
|
|
|
@ -2,8 +2,9 @@ all:
|
||||||
a2x -f manpage --asciidoc-opts="-f asciidoc.conf" i3.man
|
a2x -f manpage --asciidoc-opts="-f asciidoc.conf" i3.man
|
||||||
a2x -f manpage --asciidoc-opts="-f asciidoc.conf" i3-msg.man
|
a2x -f manpage --asciidoc-opts="-f asciidoc.conf" i3-msg.man
|
||||||
a2x -f manpage --asciidoc-opts="-f asciidoc.conf" i3-input.man
|
a2x -f manpage --asciidoc-opts="-f asciidoc.conf" i3-input.man
|
||||||
|
a2x -f manpage --asciidoc-opts="-f asciidoc.conf" i3-nagbar.man
|
||||||
clean:
|
clean:
|
||||||
for file in "i3 i3-msg i3-input"; \
|
for file in "i3 i3-msg i3-input i3-nagbar"; \
|
||||||
do \
|
do \
|
||||||
rm -f $${file}.1 $${file}.html $${file}.xml; \
|
rm -f $${file}.1 $${file}.html $${file}.xml; \
|
||||||
done
|
done
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
i3-nagbar(1)
|
||||||
|
============
|
||||||
|
Michael Stapelberg <michael+i3@stapelberg.de>
|
||||||
|
v4.0, July 2011
|
||||||
|
|
||||||
|
== NAME
|
||||||
|
|
||||||
|
i3-nagbar - displays an error bar on top of your screen
|
||||||
|
|
||||||
|
== SYNOPSIS
|
||||||
|
|
||||||
|
i3-nagbar -m 'message' -b 'label' 'action'
|
||||||
|
|
||||||
|
== DESCRIPTION
|
||||||
|
|
||||||
|
i3-nagbar is used by i3 to tell you about errors in your configuration file
|
||||||
|
(for example). While these errors are logged to the logfile (if any), the past
|
||||||
|
has proven that users are either not aware of their logfile or do not check it
|
||||||
|
after modifying the configuration file.
|
||||||
|
|
||||||
|
== EXAMPLE
|
||||||
|
|
||||||
|
------------------------------------------------
|
||||||
|
i3-nagbar -m 'You have an error in your i3 config file!' \
|
||||||
|
-b 'edit config' 'xterm $EDITOR ~/.i3/config'
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
== SEE ALSO
|
||||||
|
|
||||||
|
i3(1)
|
||||||
|
|
||||||
|
== AUTHOR
|
||||||
|
|
||||||
|
Michael Stapelberg and contributors
|
141
src/cfgparse.y
141
src/cfgparse.y
|
@ -9,10 +9,11 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <libgen.h>
|
|
||||||
|
|
||||||
#include "all.h"
|
#include "all.h"
|
||||||
|
|
||||||
|
static pid_t configerror_pid = -1;
|
||||||
|
|
||||||
static Match current_match;
|
static Match current_match;
|
||||||
|
|
||||||
typedef struct yy_buffer_state *YY_BUFFER_STATE;
|
typedef struct yy_buffer_state *YY_BUFFER_STATE;
|
||||||
|
@ -30,17 +31,18 @@ static struct context *context;
|
||||||
//int yydebug = 1;
|
//int yydebug = 1;
|
||||||
|
|
||||||
void yyerror(const char *error_message) {
|
void yyerror(const char *error_message) {
|
||||||
|
context->has_errors = true;
|
||||||
|
|
||||||
ELOG("\n");
|
ELOG("\n");
|
||||||
ELOG("CONFIG: %s\n", error_message);
|
ELOG("CONFIG: %s\n", error_message);
|
||||||
ELOG("CONFIG: in file \"%s\", line %d:\n",
|
ELOG("CONFIG: in file \"%s\", line %d:\n",
|
||||||
context->filename, context->line_number);
|
context->filename, context->line_number);
|
||||||
ELOG("CONFIG: %s\n", context->line_copy);
|
ELOG("CONFIG: %s\n", context->line_copy);
|
||||||
ELOG("CONFIG: ");
|
char buffer[context->last_column+1];
|
||||||
|
buffer[context->last_column] = '\0';
|
||||||
for (int c = 1; c <= context->last_column; c++)
|
for (int c = 1; c <= context->last_column; c++)
|
||||||
if (c >= context->first_column)
|
buffer[c-1] = (c >= context->first_column ? '^' : ' ');
|
||||||
printf("^");
|
ELOG("CONFIG: %s\n", buffer);
|
||||||
else printf(" ");
|
|
||||||
printf("\n");
|
|
||||||
ELOG("\n");
|
ELOG("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,32 +148,11 @@ static char *migrate_config(char *input, off_t size) {
|
||||||
close(readpipe[0]);
|
close(readpipe[0]);
|
||||||
dup2(readpipe[1], 1);
|
dup2(readpipe[1], 1);
|
||||||
|
|
||||||
/* start the migration script, search PATH first */
|
static char *argv[] = {
|
||||||
char *migratepath = "i3-migrate-config-to-v4.pl";
|
NULL, /* will be replaced by the executable path */
|
||||||
execlp(migratepath, migratepath, NULL);
|
NULL
|
||||||
|
};
|
||||||
/* if the script is not in path, maybe the user installed to a strange
|
exec_i3_utility("i3-migrate-config-to-v4.pl", argv);
|
||||||
* location and runs the i3 binary with an absolute path. We use
|
|
||||||
* argv[0]’s dirname */
|
|
||||||
char *pathbuf = strdup(start_argv[0]);
|
|
||||||
char *dir = dirname(pathbuf);
|
|
||||||
asprintf(&migratepath, "%s/%s", dir, "i3-migrate-config-to-v4.pl");
|
|
||||||
execlp(migratepath, migratepath, NULL);
|
|
||||||
|
|
||||||
#if defined(__linux__)
|
|
||||||
/* on linux, we have one more fall-back: dirname(/proc/self/exe) */
|
|
||||||
char buffer[BUFSIZ];
|
|
||||||
if (readlink("/proc/self/exe", buffer, BUFSIZ) == -1) {
|
|
||||||
warn("could not read /proc/self/exe");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
dir = dirname(buffer);
|
|
||||||
asprintf(&migratepath, "%s/%s", dir, "i3-migrate-config-to-v4.pl");
|
|
||||||
execlp(migratepath, migratepath, NULL);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
warn("Could not start i3-migrate-config-to-v4.pl");
|
|
||||||
exit(2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* parent */
|
/* parent */
|
||||||
|
@ -237,6 +218,98 @@ static char *migrate_config(char *input, off_t size) {
|
||||||
return converted;
|
return converted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handler which will be called when we get a SIGCHLD for the nagbar, meaning
|
||||||
|
* it exited (or could not be started, depending on the exit code).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static void nagbar_exited(EV_P_ ev_child *watcher, int revents) {
|
||||||
|
ev_child_stop(EV_A_ watcher);
|
||||||
|
if (!WIFEXITED(watcher->rstatus)) {
|
||||||
|
fprintf(stderr, "ERROR: i3-nagbar did not exit normally.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int exitcode = WEXITSTATUS(watcher->rstatus);
|
||||||
|
printf("i3-nagbar process exited with status %d\n", exitcode);
|
||||||
|
if (exitcode == 2) {
|
||||||
|
fprintf(stderr, "ERROR: i3-nagbar could not be found. Is it correctly installed on your system?\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
configerror_pid = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Starts an i3-nagbar process which alerts the user that his configuration
|
||||||
|
* file contains one or more errors. Also offers two buttons: One to launch an
|
||||||
|
* $EDITOR on the config file and another one to launch a $PAGER on the error
|
||||||
|
* logfile.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static void start_configerror_nagbar(const char *config_path) {
|
||||||
|
fprintf(stderr, "Would start i3-nagscreen now\n");
|
||||||
|
configerror_pid = fork();
|
||||||
|
if (configerror_pid == -1) {
|
||||||
|
warn("Could not fork()");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* child */
|
||||||
|
if (configerror_pid == 0) {
|
||||||
|
char *editaction,
|
||||||
|
*pageraction;
|
||||||
|
if (asprintf(&editaction, TERM_EMU " -e $EDITOR \"%s\"", config_path) == -1)
|
||||||
|
exit(1);
|
||||||
|
if (asprintf(&pageraction, TERM_EMU " -e $PAGER \"%s\"", errorfilename) == -1)
|
||||||
|
exit(1);
|
||||||
|
char *argv[] = {
|
||||||
|
NULL, /* will be replaced by the executable path */
|
||||||
|
"-m",
|
||||||
|
"You have an error in your i3 config file!",
|
||||||
|
"-b",
|
||||||
|
"edit config",
|
||||||
|
editaction,
|
||||||
|
(errorfilename ? "-b" : NULL),
|
||||||
|
"show errors",
|
||||||
|
pageraction,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
exec_i3_utility("i3-nagbar", argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parent */
|
||||||
|
/* install a child watcher */
|
||||||
|
ev_child *child = smalloc(sizeof(ev_child));
|
||||||
|
ev_child_init(child, &nagbar_exited, configerror_pid, 0);
|
||||||
|
ev_child_start(main_loop, child);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Kills the configerror i3-nagbar process, if any.
|
||||||
|
*
|
||||||
|
* Called when reloading/restarting.
|
||||||
|
*
|
||||||
|
* If wait_for_it is set (restarting), this function will waitpid(), otherwise,
|
||||||
|
* ev is assumed to handle it (reloading).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void kill_configerror_nagbar(bool wait_for_it) {
|
||||||
|
if (configerror_pid == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (kill(configerror_pid, SIGTERM) == -1)
|
||||||
|
warn("kill(configerror_nagbar) failed");
|
||||||
|
|
||||||
|
if (!wait_for_it)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* When restarting, we don’t enter the ev main loop anymore and after the
|
||||||
|
* exec(), our old pid is no longer watched. So, ev won’t handle SIGCHLD
|
||||||
|
* for us and we would end up with a <defunct> process. Therefore we
|
||||||
|
* waitpid() here. */
|
||||||
|
waitpid(configerror_pid, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
void parse_file(const char *f) {
|
void parse_file(const char *f) {
|
||||||
SLIST_HEAD(variables_head, Variable) variables = SLIST_HEAD_INITIALIZER(&variables);
|
SLIST_HEAD(variables_head, Variable) variables = SLIST_HEAD_INITIALIZER(&variables);
|
||||||
int fd, ret, read_bytes = 0;
|
int fd, ret, read_bytes = 0;
|
||||||
|
@ -390,6 +463,10 @@ void parse_file(const char *f) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (context->has_errors) {
|
||||||
|
start_configerror_nagbar(f);
|
||||||
|
}
|
||||||
|
|
||||||
FREE(context->line_copy);
|
FREE(context->line_copy);
|
||||||
free(context);
|
free(context);
|
||||||
free(new);
|
free(new);
|
||||||
|
|
|
@ -380,6 +380,7 @@ reload:
|
||||||
TOK_RELOAD
|
TOK_RELOAD
|
||||||
{
|
{
|
||||||
printf("reloading\n");
|
printf("reloading\n");
|
||||||
|
kill_configerror_nagbar(false);
|
||||||
load_configuration(conn, NULL, true);
|
load_configuration(conn, NULL, true);
|
||||||
x_set_i3_atoms();
|
x_set_i3_atoms();
|
||||||
/* Send an IPC event just in case the ws names have changed */
|
/* Send an IPC event just in case the ws names have changed */
|
||||||
|
|
24
src/log.c
24
src/log.c
|
@ -14,6 +14,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
@ -23,6 +24,23 @@
|
||||||
|
|
||||||
static uint64_t loglevel = 0;
|
static uint64_t loglevel = 0;
|
||||||
static bool verbose = true;
|
static bool verbose = true;
|
||||||
|
static FILE *errorfile;
|
||||||
|
char *errorfilename;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initializes logging by creating an error logfile in /tmp (or
|
||||||
|
* XDG_RUNTIME_DIR, see get_process_filename()).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void init_logging() {
|
||||||
|
errorfilename = get_process_filename("errorlog");
|
||||||
|
if (errorfilename == NULL) {
|
||||||
|
ELOG("Could not initialize errorlog\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
errorfile = fopen(errorfilename, "w");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set verbosity of i3. If verbose is set to true, informative messages will
|
* Set verbosity of i3. If verbose is set to true, informative messages will
|
||||||
|
@ -101,6 +119,12 @@ void errorlog(char *fmt, ...) {
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
vlog(fmt, args);
|
vlog(fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
|
/* also log to the error logfile, if opened */
|
||||||
|
va_start(args, fmt);
|
||||||
|
vfprintf(errorfile, fmt, args);
|
||||||
|
fflush(errorfile);
|
||||||
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
27
src/main.c
27
src/main.c
|
@ -19,6 +19,8 @@ xcb_connection_t *conn;
|
||||||
xcb_window_t root;
|
xcb_window_t root;
|
||||||
uint8_t root_depth;
|
uint8_t root_depth;
|
||||||
|
|
||||||
|
struct ev_loop *main_loop;
|
||||||
|
|
||||||
xcb_key_symbols_t *keysyms;
|
xcb_key_symbols_t *keysyms;
|
||||||
|
|
||||||
/* Those are our connections to X11 for use with libXcursor and XKB */
|
/* Those are our connections to X11 for use with libXcursor and XKB */
|
||||||
|
@ -178,6 +180,8 @@ int main(int argc, char *argv[]) {
|
||||||
if (!isatty(fileno(stdout)))
|
if (!isatty(fileno(stdout)))
|
||||||
setbuf(stdout, NULL);
|
setbuf(stdout, NULL);
|
||||||
|
|
||||||
|
init_logging();
|
||||||
|
|
||||||
start_argv = argv;
|
start_argv = argv;
|
||||||
|
|
||||||
while ((opt = getopt_long(argc, argv, "c:CvaL:hld:V", long_options, &option_index)) != -1) {
|
while ((opt = getopt_long(argc, argv, "c:CvaL:hld:V", long_options, &option_index)) != -1) {
|
||||||
|
@ -254,6 +258,13 @@ int main(int argc, char *argv[]) {
|
||||||
if (xcb_connection_has_error(conn))
|
if (xcb_connection_has_error(conn))
|
||||||
errx(EXIT_FAILURE, "Cannot open display\n");
|
errx(EXIT_FAILURE, "Cannot open display\n");
|
||||||
|
|
||||||
|
/* Initialize the libev event loop. This needs to be done before loading
|
||||||
|
* the config file because the parser will install an ev_child watcher
|
||||||
|
* for the nagbar when config errors are found. */
|
||||||
|
main_loop = EV_DEFAULT;
|
||||||
|
if (main_loop == NULL)
|
||||||
|
die("Could not initialize libev. Bad LIBEV_FLAGS?\n");
|
||||||
|
|
||||||
xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screens);
|
xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screens);
|
||||||
root = root_screen->root;
|
root = root_screen->root;
|
||||||
root_depth = root_screen->root_depth;
|
root_depth = root_screen->root_depth;
|
||||||
|
@ -395,10 +406,6 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
tree_render();
|
tree_render();
|
||||||
|
|
||||||
struct ev_loop *loop = ev_loop_new(0);
|
|
||||||
if (loop == NULL)
|
|
||||||
die("Could not initialize libev. Bad LIBEV_FLAGS?\n");
|
|
||||||
|
|
||||||
/* Create the UNIX domain socket for IPC */
|
/* Create the UNIX domain socket for IPC */
|
||||||
int ipc_socket = ipc_create_socket(config.ipc_socket_path);
|
int ipc_socket = ipc_create_socket(config.ipc_socket_path);
|
||||||
if (ipc_socket == -1) {
|
if (ipc_socket == -1) {
|
||||||
|
@ -407,7 +414,7 @@ int main(int argc, char *argv[]) {
|
||||||
free(config.ipc_socket_path);
|
free(config.ipc_socket_path);
|
||||||
struct ev_io *ipc_io = scalloc(sizeof(struct ev_io));
|
struct ev_io *ipc_io = scalloc(sizeof(struct ev_io));
|
||||||
ev_io_init(ipc_io, ipc_new_client, ipc_socket, EV_READ);
|
ev_io_init(ipc_io, ipc_new_client, ipc_socket, EV_READ);
|
||||||
ev_io_start(loop, ipc_io);
|
ev_io_start(main_loop, ipc_io);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set up i3 specific atoms like I3_SOCKET_PATH and I3_CONFIG_PATH */
|
/* Set up i3 specific atoms like I3_SOCKET_PATH and I3_CONFIG_PATH */
|
||||||
|
@ -419,22 +426,22 @@ int main(int argc, char *argv[]) {
|
||||||
struct ev_prepare *xcb_prepare = scalloc(sizeof(struct ev_prepare));
|
struct ev_prepare *xcb_prepare = scalloc(sizeof(struct ev_prepare));
|
||||||
|
|
||||||
ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ);
|
ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ);
|
||||||
ev_io_start(loop, xcb_watcher);
|
ev_io_start(main_loop, xcb_watcher);
|
||||||
|
|
||||||
|
|
||||||
if (xkb_supported) {
|
if (xkb_supported) {
|
||||||
ev_io_init(xkb, xkb_got_event, ConnectionNumber(xkbdpy), EV_READ);
|
ev_io_init(xkb, xkb_got_event, ConnectionNumber(xkbdpy), EV_READ);
|
||||||
ev_io_start(loop, xkb);
|
ev_io_start(main_loop, xkb);
|
||||||
|
|
||||||
/* Flush the buffer so that libev can properly get new events */
|
/* Flush the buffer so that libev can properly get new events */
|
||||||
XFlush(xkbdpy);
|
XFlush(xkbdpy);
|
||||||
}
|
}
|
||||||
|
|
||||||
ev_check_init(xcb_check, xcb_check_cb);
|
ev_check_init(xcb_check, xcb_check_cb);
|
||||||
ev_check_start(loop, xcb_check);
|
ev_check_start(main_loop, xcb_check);
|
||||||
|
|
||||||
ev_prepare_init(xcb_prepare, xcb_prepare_cb);
|
ev_prepare_init(xcb_prepare, xcb_prepare_cb);
|
||||||
ev_prepare_start(loop, xcb_prepare);
|
ev_prepare_start(main_loop, xcb_prepare);
|
||||||
|
|
||||||
xcb_flush(conn);
|
xcb_flush(conn);
|
||||||
|
|
||||||
|
@ -456,5 +463,5 @@ int main(int argc, char *argv[]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ev_loop(loop, 0);
|
ev_loop(main_loop, 0);
|
||||||
}
|
}
|
||||||
|
|
50
src/util.c
50
src/util.c
|
@ -19,6 +19,7 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <yajl/yajl_version.h>
|
#include <yajl/yajl_version.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
|
||||||
#include "all.h"
|
#include "all.h"
|
||||||
|
|
||||||
|
@ -118,6 +119,53 @@ void start_application(const char *command) {
|
||||||
wait(0);
|
wait(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* exec()s an i3 utility, for example the config file migration script or
|
||||||
|
* i3-nagbar. This function first searches $PATH for the given utility named,
|
||||||
|
* then falls back to the dirname() of the i3 executable path and then falls
|
||||||
|
* back to the dirname() of the target of /proc/self/exe (on linux).
|
||||||
|
*
|
||||||
|
* This function should be called after fork()ing.
|
||||||
|
*
|
||||||
|
* The first argument of the given argv vector will be overwritten with the
|
||||||
|
* executable name, so pass NULL.
|
||||||
|
*
|
||||||
|
* If the utility cannot be found in any of these locations, it exits with
|
||||||
|
* return code 2.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void exec_i3_utility(char *name, char *argv[]) {
|
||||||
|
/* start the migration script, search PATH first */
|
||||||
|
char *migratepath = name;
|
||||||
|
argv[0] = migratepath;
|
||||||
|
execvp(migratepath, argv);
|
||||||
|
|
||||||
|
/* if the script is not in path, maybe the user installed to a strange
|
||||||
|
* location and runs the i3 binary with an absolute path. We use
|
||||||
|
* argv[0]’s dirname */
|
||||||
|
char *pathbuf = strdup(start_argv[0]);
|
||||||
|
char *dir = dirname(pathbuf);
|
||||||
|
asprintf(&migratepath, "%s/%s", dir, name);
|
||||||
|
argv[0] = migratepath;
|
||||||
|
execvp(migratepath, argv);
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
/* on linux, we have one more fall-back: dirname(/proc/self/exe) */
|
||||||
|
char buffer[BUFSIZ];
|
||||||
|
if (readlink("/proc/self/exe", buffer, BUFSIZ) == -1) {
|
||||||
|
warn("could not read /proc/self/exe");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
dir = dirname(buffer);
|
||||||
|
asprintf(&migratepath, "%s/%s", dir, name);
|
||||||
|
argv[0] = migratepath;
|
||||||
|
execvp(migratepath, argv);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
warn("Could not start %s", name);
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Checks a generic cookie for errors and quits with the given message if there
|
* Checks a generic cookie for errors and quits with the given message if there
|
||||||
* was an error.
|
* was an error.
|
||||||
|
@ -358,6 +406,8 @@ char *store_restart_layout() {
|
||||||
void i3_restart(bool forget_layout) {
|
void i3_restart(bool forget_layout) {
|
||||||
char *restart_filename = forget_layout ? NULL : store_restart_layout();
|
char *restart_filename = forget_layout ? NULL : store_restart_layout();
|
||||||
|
|
||||||
|
kill_configerror_nagbar(true);
|
||||||
|
|
||||||
restore_geometry();
|
restore_geometry();
|
||||||
|
|
||||||
ipc_shutdown();
|
ipc_shutdown();
|
||||||
|
|
Loading…
Reference in New Issue