Merge branch 'next'

This commit is contained in:
Axel Wagner 2010-12-23 16:34:19 +01:00
commit 94a8273b09
17 changed files with 898 additions and 264 deletions

1
i3bar/.gitignore vendored
View File

@ -1,3 +1,4 @@
i3bar
*.o
core
doc/i3bar.1

9
i3bar/CHANGELOG Normal file
View File

@ -0,0 +1,9 @@
- Bugfix: Correctly render long text
- Bugfix: Don't segfault on SIGCHILD
- Implement hide-on-modifier
- Use double-buffering
v0.5
=====
- Initial release

View File

@ -6,10 +6,17 @@ FILES:=$(wildcard src/*.c)
FILES:=$(FILES:.c=.o)
HEADERS:=$(wildcard include/*.h)
all: ${FILES}
all: i3bar doc
i3bar: ${FILES}
echo "LINK"
$(CC) -o i3bar ${FILES} ${LDFLAGS}
doc:
echo ""
echo "SUBDIR doc"
$(MAKE) -C doc
src/%.o: src/%.c ${HEADERS}
echo "CC $<"
$(CC) $(CFLAGS) -c -o $@ $<
@ -20,6 +27,11 @@ install: all
$(INSTALL) -m 0755 i3bar $(DESTDIR)$(PREFIX)/bin
clean:
rm src/*.o
rm -f src/*.o
make -C doc clean
.PHONY: install clean
distclean: clean
rm -f i3bar
make -C doc distclean
.PHONY: install clean distclean doc

View File

@ -14,7 +14,7 @@ CFLAGS += -DI3BAR_VERSION=\"${GIT_VERSION}\"
LDFLAGS += -lev
LDFLAGS += -lyajl
LDFLAGS += -lxcb
LDFLAGS += -lxcb-atom
LDFLAGS += -lX11
LDFLAGS += -L/usr/local/lib
ifeq ($(DEBUG),1)

9
i3bar/doc/Makefile Normal file
View File

@ -0,0 +1,9 @@
all: i3bar.1
i3bar.1: i3bar.man
echo "A2X i3bar"
a2x -f manpage i3bar.man
clean:
rm -f i3bar.xml i3bar.1 i3bar.html
distclean: clean

73
i3bar/doc/i3bar.man Normal file
View File

@ -0,0 +1,73 @@
i3bar(1)
========
Axel Wagner <mail+i3bar@merovius.de>
v0.5, September 2010
== NAME
i3bar - xcb-based status- and ws-bar
== SYNOPSIS
*i3bar* [*-s* 'sock_path'] [*-c* 'command'] [*-m*] [*-f* 'font'] [*-V*] [*-h*]
== OPTIONS
*-s, --socket* 'sock_path'::
Specifies the 'socketpath', via which *i3bar* connects to *i3*(1). If *i3bar* can not connect to *i3*, it will exit. Defaults to '~/.i3/ipc.sock'
*-c, --command* 'command'::
Execute '<command>' to get 'stdin'. You can also simply pipe into 'stdin', but starting the coomand for itself, *i3bar* is able to send 'SIGCONT' and 'SIGSTOP', when combined with *-m*
*-m, --hide*::
Hide the bar, when 'mod4' is not pressed. With this, dockmode will not be set, and the bar is out of the way most of the time so you have more room.
If *-c* is specified, the childprocess is sent a 'SIGSTOP' on hiding and a 'SIGCONT' on unhiding of the bars
*-f, --font* 'font'::
Specifies a 'X-core-font' to use. You can choose one with *xfontsel*(1). Defaults to '-misc-fixed-medium-r-semicondensed--12-110-75-75-c-60-iso10646-1'.
*-V, --verbose*::
Be (very) verbose with the debug-output. If not set, only errors are reported to 'stderr'
*-h, --help*::
Display a short help-message and exit
== DESCRIPTION
*i3bar* is an xcb- and libev-based status- and ws-bar. It is best thought of as an replacement for the *i3-wsbar*(1) + *dzen2*(1)-combination. It creates a workspace-bar for every active output ("screen") and displays a piped in statusline rightaligned on every bar.
It does not sample any status-information itself, so you still need a program like *i3status*(1) or *conky*(1) for that.
i3bar does not support any color or other markups, so stdin should be plain utf8, one line at a time. If you use *i3status*(1), you therefore should specify 'output_format = none' in the general-section of it's configfile.
Also, you should disable the internal workspace bar of *i3*(1), when using *i3bar* by specifying 'workspace_bar no' in your *i3*-configfile.
== COLORS
*i3bar* does not yet support formatting in the displayed statusline. However it does support setting colors for the bar, the workspace-buttons and the statusline.
For now this happens with the following command-line-options:
*--color-bar-fg, --color-bar-bg, --color-active-ws-fg, --color-active-ws-bg, --color-inactive-ws-fg, --color-inactive-ws-bg, color-urgent-ws-bg, color-urgent-ws-fg*
For each specified option you need to give a HEX-colorcode.
Be advised that this command-line-options are only temporary and are very likely to be removed, when we finally have a config-file.
== EXAMPLES
To get a docked bar with some statusinformation, you use
*i3status | i3bar*
If you want it to hide when not needed, you should instead use
*i3bar -c i3status -m*
== SEE ALSO
+i3(1)+, +i3-wsbar(1)+, +dzen2(1)+, +i3status(1)+
== AUTHORS
Axel Wagner and contributors

View File

@ -26,4 +26,16 @@ void start_child(char *command);
*/
void kill_child();
/*
* Sends a SIGSTOP to the child-process (if existent)
*
*/
void stop_child();
/*
* Sends a SIGCONT to the child-process (if existent)
*
*/
void cont_child();
#endif

View File

@ -13,7 +13,6 @@ typedef struct rect_t rect;
typedef int bool;
struct ev_loop* main_loop;
pid_t child_pid;
char *statusline;
struct rect_t {
@ -31,5 +30,6 @@ struct rect_t {
#include "workspaces.h"
#include "xcb.h"
#include "ucs2_to_utf8.h"
#include "config.h"
#endif

14
i3bar/include/config.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef CONFIG_H_
#define CONFIG_H_
#include "common.h"
typedef struct config_t {
int hide_on_modifier;
int verbose;
xcb_colors_t *colors;
} config_t;
config_t config;
#endif

View File

@ -11,6 +11,10 @@
#include "queue.h"
/* Get the maximum/minimum of x and y */
#define MAX(x,y) ((x) > (y) ? (x) : (y))
#define MIN(x,y) ((x) < (y) ? (x) : (y))
/* Securely free p */
#define FREE(p) do { \
if (p != NULL) { \
@ -40,3 +44,14 @@
walk = TAILQ_FIRST(l); \
} \
} while (0)
/* Use cool logging-macros */
#define DLOG(fmt, ...) do { \
if (config.verbose) { \
printf("[%s:%d] " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
} \
} while(0)
#define ELOG(fmt, ...) do { \
fprintf(stderr, "[%s:%d] ERROR: " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
} while(0)

View File

@ -9,7 +9,21 @@
#ifndef XCB_H_
#define XCB_H_
int font_height;
#include <stdint.h>
//#include "outputs.h"
struct xcb_color_strings_t {
char *bar_fg;
char *bar_bg;
char *active_ws_fg;
char *active_ws_bg;
char *inactive_ws_fg;
char *inactive_ws_bg;
char *urgent_ws_bg;
char *urgent_ws_fg;
};
typedef struct xcb_colors_t xcb_colors_t;
/*
* Initialize xcb and use the specified fontname for text-rendering
@ -17,6 +31,12 @@ int font_height;
*/
void init_xcb();
/*
* Initialize the colors
*
*/
void init_colors(const struct xcb_color_strings_t *colors);
/*
* Cleanup the xcb-stuff.
* Called once, before the program terminates.
@ -49,11 +69,17 @@ void reconfig_windows();
void draw_bars();
/*
* Calculate the rendered width of a string with the configured font.
* The string has to be encoded in ucs2 and glyph_len has to be the length
* of the string (in width)
* Redraw the bars, i.e. simply copy the buffer to the barwindow
*
*/
int get_string_width(xcb_char2b_t *string, int glyph_len);
void redraw_bars();
/*
* Predicts the length of text based on cached data.
* The string has to be encoded in ucs2 and glyph_len has to be the length
* of the string (in glyphs).
*
*/
uint32_t predict_text_extents(xcb_char2b_t *text, uint32_t length);
#endif

View File

@ -12,6 +12,7 @@
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/wait.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
@ -20,6 +21,9 @@
#include "common.h"
/* Global variables for child_*() */
pid_t child_pid;
/* stdin- and sigchild-watchers */
ev_io *stdin_io;
ev_child *child_sig;
@ -37,38 +41,8 @@ void cleanup() {
}
/*
* Since we don't use colors and stuff, we strip the dzen-formatstrings
*
*/
void strip_dzen_formats(char *buffer) {
char *src = buffer;
char *dest = buffer;
while (*src != '\0') {
/* ^ starts a format-string, ) ends it */
if (*src == '^') {
/* We replace the seperators from i3status by pipe-symbols */
if (!strncmp(src, "^ro", strlen("^ro"))) {
*(dest++) = ' ';
*(dest++) = '|';
*(dest++) = ' ';
}
while (*src != ')') {
src++;
}
src++;
} else {
*dest = *src;
src++;
dest++;
}
}
/* The last character is \n, which xcb cannot display */
*(--dest) = '\0';
}
/*
* Callbalk for stdin. We read a line from stdin, strip dzen-formats and store
* the result in statusline
* Callbalk for stdin. We read a line from stdin and store the result
* in statusline
*
*/
void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
@ -82,20 +56,20 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
n = read(fd, buffer + rec, buffer_len - rec);
if (n == -1) {
if (errno == EAGAIN) {
/* remove trailing newline and finish up */
buffer[rec-1] = '\0';
break;
}
printf("ERROR: read() failed!");
ELOG("read() failed!\n");
exit(EXIT_FAILURE);
}
if (n == 0) {
if (rec == buffer_len) {
char *tmp = buffer;
buffer = malloc(buffer_len + STDIN_CHUNK_SIZE);
memset(buffer, '\0', buffer_len);
strncpy(buffer, tmp, buffer_len);
buffer_len += STDIN_CHUNK_SIZE;
FREE(tmp);
buffer = realloc(buffer, buffer_len);
} else {
/* remove trailing newline and finish up */
buffer[rec-1] = '\0';
break;
}
}
@ -105,10 +79,9 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
FREE(buffer);
return;
}
strip_dzen_formats(buffer);
FREE(statusline);
statusline = buffer;
printf("%s\n", buffer);
DLOG("%s\n", buffer);
draw_bars();
}
@ -119,7 +92,7 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
*
*/
void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) {
printf("Child (pid: %d) unexpectedly exited with status %d\n",
DLOG("Child (pid: %d) unexpectedly exited with status %d\n",
child_pid,
watcher->rstatus);
cleanup();
@ -128,40 +101,69 @@ void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) {
/*
* Start a child-process with the specified command and reroute stdin.
* We actually start a $SHELL to execute the command so we don't have to care
* about arguments and such
* about arguments and such.
* We also double-fork() to avoid zombies and pass the pid of the child through a
* temporary pipe back to i3bar
*
*/
void start_child(char *command) {
child_pid = 0;
if (command != NULL) {
int fd[2];
int fd[2], tmp[2];
/* This pipe will be used to communicate between e.g. i3status and i3bar */
pipe(fd);
child_pid = fork();
switch (child_pid) {
/* We also need this temporary pipe to get back the pid of i3status */
pipe(tmp);
switch (fork()) {
case -1:
printf("ERROR: Couldn't fork()");
ELOG("Couldn't fork()\n");
exit(EXIT_FAILURE);
case 0:
close(fd[0]);
/* Double-fork(), so the child gets reparented to init */
switch(child_pid = fork()) {
case -1:
ELOG("Couldn't fork() twice\n");
exit(EXIT_FAILURE);
case 0:
/* Child-process. Reroute stdout and start shell */
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
dup2(fd[1], STDOUT_FILENO);
static const char *shell = NULL;
static const char *shell = NULL;
if ((shell = getenv("SHELL")) == NULL)
shell = "/bin/sh";
if ((shell = getenv("SHELL")) == NULL)
shell = "/bin/sh";
execl(shell, shell, "-c", command, (char*) NULL);
return;
execl(shell, shell, "-c", command, (char*) NULL);
return;
default:
/* Temporary parent. We tell i3bar about the pid of i3status and exit */
write(tmp[1], &child_pid, sizeof(int));
close(tmp[0]);
close(tmp[1]);
exit(EXIT_SUCCESS);
}
default:
/* Parent-process. Rerout stdin */
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
/* We also need to get the pid of i3status from the temporary pipe */
size_t rec = 0;
while (rec < sizeof(int)) {
rec += read(tmp[0], &child_pid, sizeof(int) - rec);
}
/* The temporary pipe is no longer needed */
close(tmp[0]);
close(tmp[1]);
break;
}
}
wait(0);
/* We set O_NONBLOCK because blocking is evil in event-driven software */
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
stdin_io = malloc(sizeof(ev_io));
@ -176,13 +178,33 @@ void start_child(char *command) {
}
/*
* kill()s the child-prozess (if existend) and closes and
* kill()s the child-process (if existent) and closes and
* free()s the stdin- and sigchild-watchers
*
*/
void kill_child() {
if (child_pid != 0) {
kill(child_pid, SIGQUIT);
kill(child_pid, SIGTERM);
}
cleanup();
}
/*
* Sends a SIGSTOP to the child-process (if existent)
*
*/
void stop_child() {
if (child_pid != 0) {
kill(child_pid, SIGSTOP);
}
}
/*
* Sends a SIGCONT to the child-process (if existent)
*
*/
void cont_child() {
if (child_pid != 0) {
kill(child_pid, SIGCONT);
}
}

View File

@ -30,7 +30,7 @@ typedef void(*handler_t)(char*);
int get_ipc_fd(const char *socket_path) {
int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
if (sockfd == -1) {
printf("ERROR: Could not create Socket!\n");
ELOG("Could not create Socket!\n");
exit(EXIT_FAILURE);
}
@ -39,7 +39,7 @@ int get_ipc_fd(const char *socket_path) {
addr.sun_family = AF_LOCAL;
strcpy(addr.sun_path, socket_path);
if (connect(sockfd, (const struct sockaddr*) &addr, sizeof(struct sockaddr_un)) < 0) {
printf("ERROR: Could not connct to i3\n");
ELOG("Could not connct to i3!\n");
exit(EXIT_FAILURE);
}
return sockfd;
@ -51,7 +51,7 @@ int get_ipc_fd(const char *socket_path) {
*
*/
void got_command_reply(char *reply) {
/* FIXME: Error handling for command-replies */
/* TODO: Error handling for command-replies */
}
/*
@ -59,7 +59,7 @@ void got_command_reply(char *reply) {
*
*/
void got_workspace_reply(char *reply) {
printf("Got Workspace-Data!\n");
DLOG("Got Workspace-Data!\n");
parse_workspaces_json(reply);
draw_bars();
}
@ -70,8 +70,8 @@ void got_workspace_reply(char *reply) {
*
*/
void got_subscribe_reply(char *reply) {
printf("Got Subscribe Reply: %s\n", reply);
/* FIXME: Error handling for subscribe-commands */
DLOG("Got Subscribe Reply: %s\n", reply);
/* TODO: Error handling for subscribe-commands */
}
/*
@ -79,9 +79,9 @@ void got_subscribe_reply(char *reply) {
*
*/
void got_output_reply(char *reply) {
printf("Parsing Outputs-JSON...\n");
DLOG("Parsing Outputs-JSON...\n");
parse_outputs_json(reply);
printf("Reconfiguring Windows...\n");
DLOG("Reconfiguring Windows...\n");
reconfig_windows();
}
@ -98,7 +98,7 @@ handler_t reply_handlers[] = {
*
*/
void got_workspace_event(char *event) {
printf("Got Workspace Event!\n");
DLOG("Got Workspace Event!\n");
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
}
@ -107,7 +107,7 @@ void got_workspace_event(char *event) {
*
*/
void got_output_event(char *event) {
printf("Got Output Event!\n");
DLOG("Got Output Event!\n");
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
}
@ -123,47 +123,51 @@ handler_t event_handlers[] = {
*
*/
void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
printf("Got data!\n");
DLOG("Got data!\n");
int fd = watcher->fd;
/* First we only read the header, because we know it's length */
uint32_t header_len = strlen(I3_IPC_MAGIC) + sizeof(uint32_t)*2;
char *header = malloc(header_len);
if (header == NULL) {
printf("ERROR: Could not allocate memory!\n");
ELOG("Could not allocate memory!\n");
exit(EXIT_FAILURE);
}
/* We first parse the fixed-length IPC-header, to know, how much data
* we have to expect */
uint32_t rec = 0;
while (rec < header_len) {
int n = read(fd, header + rec, header_len - rec);
if (n == -1) {
printf("ERROR: read() failed!\n");
ELOG("read() failed!\n");
exit(EXIT_FAILURE);
}
if (n == 0) {
printf("ERROR: Nothing to read!\n");
ELOG("Nothing to read!\n");
exit(EXIT_FAILURE);
}
rec += n;
}
if (strncmp(header, I3_IPC_MAGIC, strlen(I3_IPC_MAGIC))) {
printf("ERROR: Wrong magic code: %.*s\n Expected: %s\n",
(int) strlen(I3_IPC_MAGIC),
header,
I3_IPC_MAGIC);
ELOG("Wrong magic code: %.*s\n Expected: %s\n",
(int) strlen(I3_IPC_MAGIC),
header,
I3_IPC_MAGIC);
exit(EXIT_FAILURE);
}
/* Know we read the rest of the message */
char *walk = header + strlen(I3_IPC_MAGIC);
uint32_t size = *((uint32_t*) walk);
walk += sizeof(uint32_t);
uint32_t type = *((uint32_t*) walk);
/* Now that we know, what to expect, we can start read()ing the rest
* of the message */
char *buffer = malloc(size + 1);
if (buffer == NULL) {
printf("ERROR: Could not allocate memory!\n");
ELOG("Could not allocate memory!\n");
exit(EXIT_FAILURE);
}
rec = 0;
@ -171,11 +175,11 @@ void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
while (rec < size) {
int n = read(fd, buffer + rec, size - rec);
if (n == -1) {
printf("ERROR: read() failed!\n");
ELOG("read() failed!\n");
exit(EXIT_FAILURE);
}
if (n == 0) {
printf("ERROR: Nothing to read!\n");
ELOG("Nothing to read!\n");
exit(EXIT_FAILURE);
}
rec += n;
@ -205,10 +209,14 @@ int i3_send_msg(uint32_t type, const char *payload) {
len = strlen(payload);
}
/* We are a wellbehaved client and send a proper header first */
uint32_t to_write = strlen (I3_IPC_MAGIC) + sizeof(uint32_t)*2 + len;
/* TODO: I'm not entirely sure if this buffer really has to contain more
* than the pure header (why not just write() the payload from *payload?),
* but we leave it for now */
char *buffer = malloc(to_write);
if (buffer == NULL) {
printf("ERROR: Could not allocate memory\n");
ELOG("Could not allocate memory\n");
exit(EXIT_FAILURE);
}
@ -228,7 +236,7 @@ int i3_send_msg(uint32_t type, const char *payload) {
while (to_write > 0) {
int n = write(i3_connection->fd, buffer + written, to_write);
if (n == -1) {
printf("ERROR: write() failed!\n");
ELOG("write() failed!\n");
exit(EXIT_FAILURE);
}

View File

@ -18,40 +18,130 @@
#include "common.h"
char *i3_default_sock_path = "~/.i3/ipc.sock";
/*
* Glob path, i.e. expand ~
*
*/
char *expand_path(char *path) {
static glob_t globbuf;
if (glob(path, GLOB_NOCHECK | GLOB_TILDE, NULL, &globbuf) < 0) {
printf("glob() failed");
ELOG("glob() failed\n");
exit(EXIT_FAILURE);
}
char *result = strdup(globbuf.gl_pathc > 0 ? globbuf.gl_pathv[0] : path);
if (result == NULL) {
printf("malloc() failed");
ELOG("malloc() failed\n");
exit(EXIT_FAILURE);
}
globfree(&globbuf);
return result;
}
static void read_color(char **color) {
int len = strlen(optarg);
if (len == 6 || (len == 7 && optarg[0] == '#')) {
int offset = len - 6;
int good = 1, i;
for (i = offset; good && i < 6 + offset; ++i) {
char c = optarg[i];
if (!(c >= 'a' && c <= 'f')
&& !(c >= 'A' && c <= 'F')
&& !(c >= '0' && c <= '9')) {
good = 0;
break;
}
}
if (good) {
*color = strdup(optarg + offset);
return;
}
}
fprintf(stderr, "Bad color value \"%s\"\n", optarg);
exit(EXIT_FAILURE);
}
static void free_colors(struct xcb_color_strings_t *colors) {
#define FREE_COLOR(x) \
do { \
if (colors->x) \
free(colors->x); \
} while (0)
FREE_COLOR(bar_fg);
FREE_COLOR(bar_bg);
FREE_COLOR(active_ws_fg);
FREE_COLOR(active_ws_bg);
FREE_COLOR(inactive_ws_fg);
FREE_COLOR(inactive_ws_bg);
FREE_COLOR(urgent_ws_fg);
FREE_COLOR(urgent_ws_bg);
#undef FREE_COLOR
}
void print_usage(char *elf_name) {
printf("Usage: %s [-s sock_path] [-c command] [-m] [-f font] [-V] [-h]\n", elf_name);
printf("-s <sock_path>\tConnect to i3 via <sock_path>\n");
printf("-c <command>\tExecute <command> to get stdin\n");
printf("-m\t\tHide the bars, when mod4 is not pressed.\n");
printf("\t\tIf -c is specified, the childprocess is sent a SIGSTOP on hiding,\n");
printf("\t\tand a SIGCONT on unhiding of the bars\n");
printf("-f <font>\tUse X-Core-Font <font> for display\n");
printf("-V\t\tBe (very) verbose with the debug-output\n");
printf("-h\t\tDisplay this help-message and exit\n");
}
/*
* We watch various signals, that are there to make our application stop.
* If we get one of those, we ev_unloop() and invoke the cleanup-routines
* in main() with that
*
*/
void sig_cb(struct ev_loop *loop, ev_signal *watcher, int revents) {
switch (watcher->signum) {
case SIGTERM:
DLOG("Got a SIGTERM, stopping\n");
break;
case SIGINT:
DLOG("Got a SIGINT, stopping\n");
break;
case SIGHUP:
DLOG("Got a SIGHUP, stopping\n");
}
ev_unloop(main_loop, EVUNLOOP_ALL);
}
int main(int argc, char **argv) {
int opt;
int option_index = 0;
char *socket_path = NULL;
char *command = NULL;
char *fontname = NULL;
char *i3_default_sock_path = "~/.i3/ipc.sock";
struct xcb_color_strings_t colors = { NULL, };
/* Definition of the standard-config */
config.hide_on_modifier = 0;
static struct option long_opt[] = {
{ "socket", required_argument, 0, 's' },
{ "command", required_argument, 0, 'c' },
{ "font", required_argument, 0, 'f' },
{ "help", no_argument, 0, 'h' },
{ "version", no_argument, 0, 'v' },
{ NULL, 0, 0, 0}
{ "socket", required_argument, 0, 's' },
{ "command", required_argument, 0, 'c' },
{ "hide", no_argument, 0, 'm' },
{ "font", required_argument, 0, 'f' },
{ "help", no_argument, 0, 'h' },
{ "version", no_argument, 0, 'v' },
{ "verbose", no_argument, 0, 'V' },
{ "color-bar-fg", required_argument, 0, 'A' },
{ "color-bar-bg", required_argument, 0, 'B' },
{ "color-active-ws-fg", required_argument, 0, 'C' },
{ "color-active-ws-bg", required_argument, 0, 'D' },
{ "color-inactive-ws-fg", required_argument, 0, 'E' },
{ "color-inactive-ws-bg", required_argument, 0, 'F' },
{ "color-urgent-ws-bg", required_argument, 0, 'G' },
{ "color-urgent-ws-fg", required_argument, 0, 'H' },
{ NULL, 0, 0, 0}
};
while ((opt = getopt_long(argc, argv, "s:c:f:hv", long_opt, &option_index)) != -1) {
while ((opt = getopt_long(argc, argv, "s:c:mf:hvVA:B:C:D:E:F:G:H:", long_opt, &option_index)) != -1) {
switch (opt) {
case 's':
socket_path = expand_path(optarg);
@ -59,47 +149,102 @@ int main(int argc, char **argv) {
case 'c':
command = strdup(optarg);
break;
case 'm':
config.hide_on_modifier = 1;
break;
case 'f':
fontname = strdup(optarg);
break;
case 'v':
printf("i3bar version " I3BAR_VERSION " © 2010 Axel Wagner and contributors\n");
exit(EXIT_SUCCESS);
break;
case 'V':
config.verbose = 1;
break;
case 'A':
read_color(&colors.bar_fg);
break;
case 'B':
read_color(&colors.bar_bg);
break;
case 'C':
read_color(&colors.active_ws_fg);
break;
case 'D':
read_color(&colors.active_ws_bg);
break;
case 'E':
read_color(&colors.inactive_ws_fg);
break;
case 'F':
read_color(&colors.inactive_ws_bg);
break;
case 'G':
read_color(&colors.urgent_ws_bg);
break;
case 'H':
read_color(&colors.urgent_ws_fg);
break;
default:
printf("Usage: %s [-s socket_path] [-c command] [-f font] [-h]\n", argv[0]);
printf("-s <socket_path>: Connect to i3 via <socket_path>\n");
printf("-c <command>: Execute <command> to get sdtin\n");
printf("-f <font>: Use X-Core-Font <font> for display\n");
printf("-h: Display this help-message and exit\n");
print_usage(argv[0]);
exit(EXIT_SUCCESS);
break;
}
}
if (fontname == NULL) {
/* This is a very restrictive default. More sensefull would be something like
* "-misc-*-*-*-*--*-*-*-*-*-*-*-*". But since that produces very ugly results
* on my machine, let's stick with this until we have a configfile */
fontname = "-misc-fixed-medium-r-semicondensed--12-110-75-75-c-60-iso10646-1";
}
if (socket_path == NULL) {
printf("No Socket Path Specified, default to %s\n", i3_default_sock_path);
ELOG("No Socket Path Specified, default to %s\n", i3_default_sock_path);
socket_path = expand_path(i3_default_sock_path);
}
main_loop = ev_default_loop(0);
init_colors(&colors);
init_xcb(fontname);
free_colors(&colors);
init_outputs();
init_connection(socket_path);
FREE(socket_path);
/* We subscribe to the i3-events we need */
subscribe_events();
/* We initiate the main-function by requesting infos about the outputs and
* workspaces. Everything else (creating the bars, showing the right workspace-
* buttons and more) is taken care of by the event-driveniness of the code */
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
/* The name of this function is actually misleading. Even if no -c is specified,
* this function initiates the watchers to listen on stdin and react accordingly */
start_child(command);
/* We listen to SIGTERM/QUIT/INT and try to exit cleanly, by stopping the main-loop.
* We only need those watchers on the stack, so putting them on the stack saves us
* some calls to free() */
ev_signal sig_term, sig_int, sig_hup;
ev_signal_init(&sig_term, &sig_cb, SIGTERM);
ev_signal_init(&sig_int, &sig_cb, SIGINT);
ev_signal_init(&sig_hup, &sig_cb, SIGHUP);
ev_signal_start(main_loop, &sig_term);
ev_signal_start(main_loop, &sig_int);
ev_signal_start(main_loop, &sig_hup);
/* From here on everything should run smooth for itself, just start listening for
* events. We stop simply stop the event-loop, when we are finished */
ev_loop(main_loop, 0);
kill_child();

View File

@ -230,7 +230,7 @@ void parse_outputs_json(char *json) {
case yajl_status_client_canceled:
case yajl_status_insufficient_data:
case yajl_status_error:
printf("ERROR: Could not parse outputs-reply!\n");
ELOG("Could not parse outputs-reply!\n");
exit(EXIT_FAILURE);
break;
}

View File

@ -115,13 +115,14 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, unsigne
xcb_char2b_t *ucs2_name = (xcb_char2b_t*) convert_utf8_to_ucs2(params->workspaces_walk->name, &ucs2_len);
params->workspaces_walk->ucs2_name = ucs2_name;
params->workspaces_walk->name_glyphs = ucs2_len;
params->workspaces_walk->name_width = get_string_width(params->workspaces_walk->ucs2_name,
params->workspaces_walk->name_glyphs);
params->workspaces_walk->name_width =
predict_text_extents(params->workspaces_walk->ucs2_name,
params->workspaces_walk->name_glyphs);
printf("Got Workspace %s, name_width: %d, glyphs: %d\n",
params->workspaces_walk->name,
params->workspaces_walk->name_width,
params->workspaces_walk->name_glyphs);
DLOG("Got Workspace %s, name_width: %d, glyphs: %d\n",
params->workspaces_walk->name,
params->workspaces_walk->name_width,
params->workspaces_walk->name_glyphs);
FREE(params->cur_key);
return 1;
@ -183,7 +184,7 @@ static int workspaces_map_key_cb(void *params_, const unsigned char *keyVal, uns
params->cur_key = malloc(sizeof(unsigned char) * (keyLen + 1));
if (params->cur_key == NULL) {
printf("ERROR: Could not allocate memory!\n");
ELOG("Could not allocate memory!\n");
exit(EXIT_FAILURE);
}
strncpy(params->cur_key, (const char*) keyVal, keyLen);
@ -237,7 +238,7 @@ void parse_workspaces_json(char *json) {
case yajl_status_client_canceled:
case yajl_status_insufficient_data:
case yajl_status_error:
printf("ERROR: Could not parse workspaces-reply!\n");
ELOG("Could not parse workspaces-reply!\n");
exit(EXIT_FAILURE);
break;
}

View File

@ -10,13 +10,18 @@
*/
#include <xcb/xcb.h>
#include <xcb/xproto.h>
#include <xcb/xcb_event.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <i3/ipc.h>
#include <ev.h>
#include <X11/Xlib.h>
#include <X11/XKBlib.h>
#include <X11/extensions/XKB.h>
#include "common.h"
/* We save the Atoms in an easy to access array, indexed by an enum */
@ -32,14 +37,124 @@ xcb_atom_t atoms[NUM_ATOMS];
/* Variables, that are the same for all functions at all times */
xcb_connection_t *xcb_connection;
xcb_screen_t *xcb_screens;
xcb_screen_t *xcb_screen;
xcb_window_t xcb_root;
xcb_font_t xcb_font;
/* We need to cache some data to speed up text-width-prediction */
xcb_query_font_reply_t *font_info;
int font_height;
xcb_charinfo_t *font_table;
/* These are only relevant for XKB, which we only need for grabbing modifiers */
Display *xkb_dpy;
int xkb_event_base;
int mod_pressed;
/* Because the statusline is the same on all outputs, we have
* global buffer to render it on */
xcb_gcontext_t statusline_ctx;
xcb_pixmap_t statusline_pm;
uint32_t statusline_width;
/* Event-Watchers, to interact with the user */
ev_prepare *xcb_prep;
ev_check *xcb_chk;
ev_io *xcb_io;
ev_io *xkb_io;
/* The parsed colors */
struct xcb_colors_t {
uint32_t bar_fg;
uint32_t bar_bg;
uint32_t active_ws_fg;
uint32_t active_ws_bg;
uint32_t inactive_ws_fg;
uint32_t inactive_ws_bg;
uint32_t urgent_ws_bg;
uint32_t urgent_ws_fg;
};
struct xcb_colors_t colors;
/* We define xcb_request_failed as a macro to include the relevant line-number */
#define xcb_request_failed(cookie, err_msg) _xcb_request_failed(cookie, err_msg, __LINE__)
int _xcb_request_failed(xcb_void_cookie_t cookie, char *err_msg, int line) {
xcb_generic_error_t *err;
if ((err = xcb_request_check(xcb_connection, cookie)) != NULL) {
ELOG("%s. X Error Code: %d\n", err_msg, err->error_code);
return err->error_code;
}
return 0;
}
/*
* Predicts the length of text based on cached data.
* The string has to be encoded in ucs2 and glyph_len has to be the length
* of the string (in glyphs).
*
*/
uint32_t predict_text_extents(xcb_char2b_t *text, uint32_t length) {
/* If we don't have per-character data, return the maximum width */
if (font_table == NULL) {
return (font_info->max_bounds.character_width * length);
}
uint32_t width = 0;
uint32_t i;
for (i = 0; i < length; i++) {
xcb_charinfo_t *info;
int row = text[i].byte1;
int col = text[i].byte2;
if (row < font_info->min_byte1 || row > font_info->max_byte1 ||
col < font_info->min_char_or_byte2 || col > font_info->max_char_or_byte2) {
continue;
}
/* Don't you ask me, how this one works… */
info = &font_table[((row - font_info->min_byte1) *
(font_info->max_char_or_byte2 - font_info->min_char_or_byte2 + 1)) +
(col - font_info->min_char_or_byte2)];
if (info->character_width != 0 ||
(info->right_side_bearing |
info->left_side_bearing |
info->ascent |
info->descent) != 0) {
width += info->character_width;
}
}
return width;
}
/*
* Draws text given in UCS-2-encoding to a given drawable and position
*
*/
void draw_text(xcb_drawable_t drawable, xcb_gcontext_t ctx, int16_t x, int16_t y,
xcb_char2b_t *text, uint32_t glyph_count) {
int offset = 0;
int16_t pos_x = x;
int16_t font_ascent = font_info->font_ascent;
while (glyph_count > 0) {
uint8_t chunk_size = MIN(255, glyph_count);
uint32_t chunk_width = predict_text_extents(text + offset, chunk_size);
xcb_image_text_16(xcb_connection,
chunk_size,
drawable,
ctx,
pos_x, y + font_ascent,
text + offset);
offset += chunk_size;
pos_x += chunk_width;
glyph_count -= chunk_size;
}
}
/*
* Converts a colorstring to a colorpixel as expected from xcb_change_gc.
@ -56,6 +171,115 @@ uint32_t get_colorpixel(const char *s) {
return (r << 16 | g << 8 | b);
}
/*
* Redraws the statusline to the buffer
*
*/
void refresh_statusline() {
int glyph_count;
if (statusline == NULL) {
return;
}
xcb_char2b_t *text = (xcb_char2b_t*) convert_utf8_to_ucs2(statusline, &glyph_count);
statusline_width = predict_text_extents(text, glyph_count);
xcb_free_pixmap(xcb_connection, statusline_pm);
statusline_pm = xcb_generate_id(xcb_connection);
xcb_void_cookie_t sl_pm_cookie = xcb_create_pixmap_checked(xcb_connection,
xcb_screen->root_depth,
statusline_pm,
xcb_root,
statusline_width,
font_height);
draw_text(statusline_pm, statusline_ctx, 0, 0, text, glyph_count);
if (xcb_request_failed(sl_pm_cookie, "Could not allocate statusline-buffer")) {
exit(EXIT_FAILURE);
}
}
/*
* Hides all bars (unmaps them)
*
*/
void hide_bars() {
if (!config.hide_on_modifier) {
return;
}
i3_output *walk;
SLIST_FOREACH(walk, outputs, slist) {
xcb_unmap_window(xcb_connection, walk->bar);
}
stop_child();
}
/*
* Unhides all bars (maps them)
*
*/
void unhide_bars() {
if (!config.hide_on_modifier) {
return;
}
i3_output *walk;
xcb_void_cookie_t cookie;
uint32_t mask;
uint32_t values[5];
cont_child();
SLIST_FOREACH(walk, outputs, slist) {
if (walk->bar == XCB_NONE) {
continue;
}
mask = XCB_CONFIG_WINDOW_X |
XCB_CONFIG_WINDOW_Y |
XCB_CONFIG_WINDOW_WIDTH |
XCB_CONFIG_WINDOW_HEIGHT |
XCB_CONFIG_WINDOW_STACK_MODE;
values[0] = walk->rect.x;
values[1] = walk->rect.y + walk->rect.h - font_height - 6;
values[2] = walk->rect.w;
values[3] = font_height + 6;
values[4] = XCB_STACK_MODE_ABOVE;
DLOG("Reconfiguring Window for output %s to %d,%d\n", walk->name, values[0], values[1]);
cookie = xcb_configure_window_checked(xcb_connection,
walk->bar,
mask,
values);
if (xcb_request_failed(cookie, "Could not reconfigure window")) {
exit(EXIT_FAILURE);
}
xcb_map_window(xcb_connection, walk->bar);
}
}
/*
* Parse the colors into a format that we can use
*
*/
void init_colors(const struct xcb_color_strings_t *new_colors) {
#define PARSE_COLOR(name, def) \
do { \
colors.name = get_colorpixel(new_colors->name ? new_colors->name : def); \
} while (0)
PARSE_COLOR(bar_fg, "FFFFFF");
PARSE_COLOR(bar_bg, "000000");
PARSE_COLOR(active_ws_fg, "FFFFFF");
PARSE_COLOR(active_ws_bg, "480000");
PARSE_COLOR(inactive_ws_fg, "FFFFFF");
PARSE_COLOR(inactive_ws_bg, "240000");
PARSE_COLOR(urgent_ws_fg, "FFFFFF");
PARSE_COLOR(urgent_ws_bg, "002400");
#undef PARSE_COLOR
}
/*
* Handle a button-press-event (i.c. a mouse click on one of our bars).
* We determine, wether the click occured on a ws-button or if the scroll-
@ -75,11 +299,11 @@ void handle_button(xcb_button_press_event_t *event) {
}
if (walk == NULL) {
printf("Unknown Bar klicked!\n");
DLOG("Unknown Bar klicked!\n");
return;
}
/* TODO: Move this to exern get_ws_for_output() */
/* TODO: Move this to extern get_ws_for_output() */
TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
if (cur_ws->visible) {
break;
@ -87,20 +311,20 @@ void handle_button(xcb_button_press_event_t *event) {
}
if (cur_ws == NULL) {
printf("No Workspace active?\n");
DLOG("No Workspace active?\n");
return;
}
int32_t x = event->event_x;
printf("Got Button %d\n", event->detail);
DLOG("Got Button %d\n", event->detail);
switch (event->detail) {
case 1:
/* Left Mousbutton. We determine, which button was clicked
* and set cur_ws accordingly */
TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
printf("x = %d\n", x);
DLOG("x = %d\n", x);
if (x < cur_ws->name_width + 10) {
break;
}
@ -157,7 +381,7 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) {
switch (event->response_type & ~0x80) {
case XCB_EXPOSE:
/* Expose-events happen, when the window needs to be redrawn */
draw_bars();
redraw_bars();
break;
case XCB_BUTTON_PRESS:
/* Button-press-events are mouse-buttons clicked on one of our bars */
@ -176,27 +400,42 @@ void xcb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
}
/*
* Calculate the rendered width of a string with the configured font.
* The string has to be encoded in ucs2 and glyph_len has to be the length
* of the string (in width)
* We need to bind to the modifier per XKB. Sadly, XCB does not implement this
*
*/
int get_string_width(xcb_char2b_t *string, int glyph_len) {
xcb_query_text_extents_cookie_t cookie;
xcb_query_text_extents_reply_t *reply;
xcb_generic_error_t *error;
int width;
void xkb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
XkbEvent ev;
int modstate;
cookie = xcb_query_text_extents(xcb_connection, xcb_font, glyph_len, string);
reply = xcb_query_text_extents_reply(xcb_connection, cookie, &error);
if (reply == NULL) {
printf("ERROR: Could not get text extents!");
return 7;
DLOG("Got XKB-Event!\n");
while (XPending(xkb_dpy)) {
XNextEvent(xkb_dpy, (XEvent*)&ev);
if (ev.type != xkb_event_base) {
ELOG("No Xkb-Event!\n");
continue;
}
if (ev.any.xkb_type != XkbStateNotify) {
ELOG("No State Notify!\n");
continue;
}
unsigned int mods = ev.state.mods;
modstate = mods & Mod4Mask;
}
width = reply->overall_width;
free(reply);
return width;
if (modstate != mod_pressed) {
if (modstate == 0) {
DLOG("Mod4 got released!\n");
hide_bars();
} else {
DLOG("Mod4 got pressed!\n");
unhide_bars();
}
mod_pressed = modstate;
}
}
/*
@ -207,17 +446,17 @@ void init_xcb(char *fontname) {
/* FIXME: xcb_connect leaks Memory */
xcb_connection = xcb_connect(NULL, NULL);
if (xcb_connection_has_error(xcb_connection)) {
printf("Cannot open display\n");
ELOG("Cannot open display\n");
exit(EXIT_FAILURE);
}
printf("Connected to xcb\n");
DLOG("Connected to xcb\n");
/* We have to request the atoms we need */
#define ATOM_DO(name) atom_cookies[name] = xcb_intern_atom(xcb_connection, 0, strlen(#name), #name);
#include "xcb_atoms.def"
xcb_screens = xcb_setup_roots_iterator(xcb_get_setup(xcb_connection)).data;
xcb_root = xcb_screens->root;
xcb_screen = xcb_setup_roots_iterator(xcb_get_setup(xcb_connection)).data;
xcb_root = xcb_screen->root;
/* We load and allocate the font */
xcb_font = xcb_generate_id(xcb_connection);
@ -227,12 +466,71 @@ void init_xcb(char *fontname) {
strlen(fontname),
fontname);
/* We also need the fontheight to configure our bars accordingly */
xcb_list_fonts_with_info_cookie_t cookie;
cookie = xcb_list_fonts_with_info(xcb_connection,
1,
strlen(fontname),
fontname);
/* We need to save info about the font, because we need the fonts height and
* information about the width of characters */
xcb_query_font_cookie_t query_font_cookie;
query_font_cookie = xcb_query_font(xcb_connection,
xcb_font);
/* To grab modifiers without blocking other applications from receiving key-events
* involving that modifier, we sadly have to use xkb which is not yet fully supported
* in xcb */
if (config.hide_on_modifier) {
int xkb_major, xkb_minor, xkb_errbase, xkb_err;
xkb_major = XkbMajorVersion;
xkb_minor = XkbMinorVersion;
xkb_dpy = XkbOpenDisplay(":0",
&xkb_event_base,
&xkb_errbase,
&xkb_major,
&xkb_minor,
&xkb_err);
if (xkb_dpy == NULL) {
ELOG("No XKB!\n");
exit(EXIT_FAILURE);
}
if (fcntl(ConnectionNumber(xkb_dpy), F_SETFD, FD_CLOEXEC) == -1) {
ELOG("Could not set FD_CLOEXEC on xkbdpy\n");
exit(EXIT_FAILURE);
}
int i1;
if (!XkbQueryExtension(xkb_dpy, &i1, &xkb_event_base, &xkb_errbase, &xkb_major, &xkb_minor)) {
ELOG("XKB not supported by X-server!\n");
exit(EXIT_FAILURE);
}
if (!XkbSelectEvents(xkb_dpy, XkbUseCoreKbd, XkbStateNotifyMask, XkbStateNotifyMask)) {
ELOG("Could not grab Key!\n");
exit(EXIT_FAILURE);
}
xkb_io = malloc(sizeof(ev_io));
ev_io_init(xkb_io, &xkb_io_cb, ConnectionNumber(xkb_dpy), EV_READ);
ev_io_start(main_loop, xkb_io);
XFlush(xkb_dpy);
}
/* We draw the statusline to a seperate pixmap, because it looks the same on all bars and
* this way, we can choose to crop it */
statusline_ctx = xcb_generate_id(xcb_connection);
uint32_t mask = XCB_GC_FOREGROUND |
XCB_GC_BACKGROUND |
XCB_GC_FONT;
uint32_t vals[3] = { colors.bar_fg, colors.bar_bg, xcb_font };
xcb_void_cookie_t sl_ctx_cookie = xcb_create_gc_checked(xcb_connection,
statusline_ctx,
xcb_root,
mask,
vals);
/* We only generate an id for the pixmap, because the width of it is dependent on the
* input we get */
statusline_pm = xcb_generate_id(xcb_connection);
/* The varios Watchers to communicate with xcb */
xcb_io = malloc(sizeof(ev_io));
@ -250,23 +548,27 @@ void init_xcb(char *fontname) {
/* Now we get the atoms and save them in a nice data-structure */
get_atoms();
xcb_generic_error_t *err = xcb_request_check(xcb_connection,
open_font_cookie);
/* Now we save the font-infos */
font_info = xcb_query_font_reply(xcb_connection,
query_font_cookie,
NULL);
font_height = font_info->font_ascent + font_info->font_descent;
if (err != NULL) {
printf("ERROR: Could not open font! XCB-Error-Code: %d\n", err->error_code);
if (xcb_request_failed(open_font_cookie, "Could not open font")) {
exit(EXIT_FAILURE);
}
/* Now we calculate the font-height */
xcb_list_fonts_with_info_reply_t *reply;
reply = xcb_list_fonts_with_info_reply(xcb_connection,
cookie,
NULL);
font_height = reply->font_ascent + reply->font_descent;
FREE(reply);
if (xcb_query_font_char_infos_length(font_info) == 0) {
font_table = NULL;
} else {
font_table = xcb_query_font_char_infos(font_info);
}
printf("Calculated Font-height: %d\n", font_height);
DLOG("Calculated Font-height: %d\n", font_height);
if (xcb_request_failed(sl_ctx_cookie, "Could not create context for statusline")) {
exit(EXIT_FAILURE);
}
}
/*
@ -290,6 +592,7 @@ void clean_xcb() {
FREE(xcb_chk);
FREE(xcb_prep);
FREE(xcb_io);
FREE(font_info);
}
/*
@ -299,11 +602,15 @@ void clean_xcb() {
void get_atoms() {
xcb_intern_atom_reply_t *reply;
#define ATOM_DO(name) reply = xcb_intern_atom_reply(xcb_connection, atom_cookies[name], NULL); \
if (reply == NULL) { \
ELOG("Could not get atom %s\n", #name); \
exit(EXIT_FAILURE); \
} \
atoms[name] = reply->atom; \
free(reply);
#include "xcb_atoms.def"
printf("Got Atoms\n");
DLOG("Got Atoms\n");
}
/*
@ -327,7 +634,7 @@ void destroy_window(i3_output *output) {
*/
void reconfig_windows() {
uint32_t mask;
uint32_t values[4];
uint32_t values[5];
xcb_generic_error_t *err;
@ -336,42 +643,45 @@ void reconfig_windows() {
if (!walk->active) {
/* If an output is not active, we destroy it's bar */
/* FIXME: Maybe we rather want to unmap? */
printf("Destroying window for output %s\n", walk->name);
DLOG("Destroying window for output %s\n", walk->name);
destroy_window(walk);
continue;
}
if (walk->bar == XCB_NONE) {
printf("Creating Window for output %s\n", walk->name);
DLOG("Creating Window for output %s\n", walk->name);
walk->bar = xcb_generate_id(xcb_connection);
walk->buffer = xcb_generate_id(xcb_connection);
mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
/* Black background */
values[0] = xcb_screens->black_pixel;
values[0] = colors.bar_bg;
/* If hide_on_modifier is set, i3 is not supposed to manage our bar-windows */
values[1] = config.hide_on_modifier;
/* The events we want to receive */
values[1] = XCB_EVENT_MASK_EXPOSURE |
values[2] = XCB_EVENT_MASK_EXPOSURE |
XCB_EVENT_MASK_BUTTON_PRESS;
xcb_void_cookie_t win_cookie = xcb_create_window_checked(xcb_connection,
xcb_screens->root_depth,
xcb_screen->root_depth,
walk->bar,
xcb_root,
walk->rect.x, walk->rect.y,
walk->rect.x, walk->rect.y + walk->rect.h - font_height - 6,
walk->rect.w, font_height + 6,
1,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
xcb_screens->root_visual,
xcb_screen->root_visual,
mask,
values);
/* The double-buffer we use to render stuff off-screen */
xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection,
xcb_screens->root_depth,
xcb_screen->root_depth,
walk->buffer,
walk->bar,
walk->rect.w,
walk->rect.h);
/* We want dock-windows (for now) */
/* We want dock-windows (for now). When override_redirect is set, i3 is ignoring
* this one */
xcb_void_cookie_t prop_cookie = xcb_change_property(xcb_connection,
XCB_PROP_MODE_REPLACE,
walk->bar,
@ -381,42 +691,28 @@ void reconfig_windows() {
1,
(unsigned char*) &atoms[_NET_WM_WINDOW_TYPE_DOCK]);
/* We also want a graphics-context (the "canvas" on which we draw) */
/* We also want a graphics-context for the bars (it defines the properties
* with which we draw to them) */
walk->bargc = xcb_generate_id(xcb_connection);
mask = XCB_GC_FONT;
values[0] = xcb_font;
xcb_void_cookie_t gc_cookie = xcb_create_gc_checked(xcb_connection,
walk->bargc,
walk->bar,
mask,
values);
/* We finally map the bar (display it on screen) */
xcb_void_cookie_t map_cookie = xcb_map_window_checked(xcb_connection, walk->bar);
if ((err = xcb_request_check(xcb_connection, win_cookie)) != NULL) {
printf("ERROR: Could not create Window. XCB-errorcode: %d\n", err->error_code);
exit(EXIT_FAILURE);
/* We finally map the bar (display it on screen), unless the modifier-switch is on */
xcb_void_cookie_t map_cookie;
if (!config.hide_on_modifier) {
map_cookie = xcb_map_window_checked(xcb_connection, walk->bar);
}
if ((err = xcb_request_check(xcb_connection, pm_cookie)) != NULL) {
printf("ERROR: Could not create Pixmap. XCB-errorcode: %d\n", err->error_code);
exit(EXIT_FAILURE);
}
if ((err = xcb_request_check(xcb_connection, prop_cookie)) != NULL) {
printf("ERROR: Could not set dock mode. XCB-errorcode: %d\n", err->error_code);
exit(EXIT_FAILURE);
}
if ((err = xcb_request_check(xcb_connection, gc_cookie)) != NULL) {
printf("ERROR: Could not create graphical context. XCB-errorcode: %d\n", err->error_code);
exit(EXIT_FAILURE);
}
if ((err = xcb_request_check(xcb_connection, map_cookie)) != NULL) {
printf("ERROR: Could not map window. XCB-errorcode: %d\n", err->error_code);
if (xcb_request_failed(win_cookie, "Could not create window") ||
xcb_request_failed(pm_cookie, "Could not create pixmap") ||
xcb_request_failed(prop_cookie, "Could not set dock mode") ||
xcb_request_failed(gc_cookie, "Could not create graphical context") ||
(!config.hide_on_modifier && xcb_request_failed(map_cookie, "Could not map window"))) {
exit(EXIT_FAILURE);
}
} else {
@ -424,38 +720,22 @@ void reconfig_windows() {
mask = XCB_CONFIG_WINDOW_X |
XCB_CONFIG_WINDOW_Y |
XCB_CONFIG_WINDOW_WIDTH |
XCB_CONFIG_WINDOW_HEIGHT;
XCB_CONFIG_WINDOW_HEIGHT |
XCB_CONFIG_WINDOW_STACK_MODE;
values[0] = walk->rect.x;
values[1] = walk->rect.y + walk->rect.h - font_height - 6;
values[2] = walk->rect.w;
values[3] = font_height + 6;
printf("Reconfiguring Window for output %s to %d,%d\n", walk->name, values[0], values[1]);
values[4] = XCB_STACK_MODE_ABOVE;
DLOG("Reconfiguring Window for output %s to %d,%d\n", walk->name, values[0], values[1]);
xcb_void_cookie_t cfg_cookie = xcb_configure_window_checked(xcb_connection,
walk->bar,
mask,
values);
xcb_free_pixmap(xcb_connection, walk->buffer);
walk->buffer = xcb_generate_id(xcb_connection);
xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection,
xcb_screens->root_depth,
walk->buffer,
walk->bar,
walk->rect.w,
walk->rect.h);
if ((err = xcb_request_check(xcb_connection, cfg_cookie)) != NULL) {
printf("ERROR: Could not reconfigure window. XCB-errorcode: %d\n", err->error_code);
if (xcb_request_failed(cfg_cookie, "Could not reconfigure window")) {
exit(EXIT_FAILURE);
}
if ((err = xcb_request_check(xcb_connection, pm_cookie)) != NULL) {
printf("ERROR: Could not create Pixmap. XCB-errorcode: %d\n", err->error_code);
exit(EXIT_FAILURE);
}
}
}
}
}
@ -464,18 +744,23 @@ void reconfig_windows() {
*
*/
void draw_bars() {
printf("Drawing Bars...\n");
DLOG("Drawing Bars...\n");
int i = 0;
refresh_statusline();
i3_output *outputs_walk;
SLIST_FOREACH(outputs_walk, outputs, slist) {
if (!outputs_walk->active) {
printf("Output %s inactive, skipping...\n", outputs_walk->name);
DLOG("Output %s inactive, skipping...\n", outputs_walk->name);
continue;
}
if (outputs_walk->bar == XCB_NONE) {
/* Oh shit, an active output without an own bar. Create it now! */
reconfig_windows();
}
uint32_t color = get_colorpixel("000000");
/* First things first: clear the backbuffer */
uint32_t color = colors.bar_bg;
xcb_change_gc(xcb_connection,
outputs_walk->bargc,
XCB_GC_FOREGROUND,
@ -486,75 +771,78 @@ void draw_bars() {
outputs_walk->bargc,
1,
&rect);
if (statusline != NULL) {
printf("Printing statusline!\n");
xcb_change_gc(xcb_connection,
DLOG("Printing statusline!\n");
/* Luckily we already prepared a seperate pixmap containing the rendered
* statusline, we just have to copy the relevant parts to the relevant
* position */
xcb_copy_area(xcb_connection,
statusline_pm,
outputs_walk->buffer,
outputs_walk->bargc,
XCB_GC_BACKGROUND,
&color);
color = get_colorpixel("FFFFFF");
xcb_change_gc(xcb_connection,
outputs_walk->bargc,
XCB_GC_FOREGROUND,
&color);
int glyph_count;
xcb_char2b_t *text = (xcb_char2b_t*) convert_utf8_to_ucs2(statusline, &glyph_count);
xcb_void_cookie_t cookie;
cookie = xcb_image_text_16(xcb_connection,
glyph_count,
outputs_walk->buffer,
outputs_walk->bargc,
outputs_walk->rect.w - get_string_width(text, glyph_count) - 4,
font_height + 1,
(xcb_char2b_t*) text);
xcb_generic_error_t *err = xcb_request_check(xcb_connection, cookie);
if (err != NULL) {
printf("XCB-Error: %d\n", err->error_code);
}
MAX(0, (int16_t)(statusline_width - outputs_walk->rect.w + 4)), 0,
MAX(0, (int16_t)(outputs_walk->rect.w - statusline_width - 4)), 3,
MIN(outputs_walk->rect.w - 4, statusline_width), font_height);
}
i3_ws *ws_walk;
TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
printf("Drawing Button for WS %s at x = %d\n", ws_walk->name, i);
uint32_t color = get_colorpixel("240000");
DLOG("Drawing Button for WS %s at x = %d\n", ws_walk->name, i);
uint32_t fg_color = colors.inactive_ws_fg;
uint32_t bg_color = colors.inactive_ws_bg;
if (ws_walk->visible) {
color = get_colorpixel("480000");
fg_color = colors.active_ws_fg;
bg_color = colors.active_ws_bg;
}
if (ws_walk->urgent) {
printf("WS %s is urgent!\n", ws_walk->name);
color = get_colorpixel("002400");
DLOG("WS %s is urgent!\n", ws_walk->name);
fg_color = colors.urgent_ws_fg;
bg_color = colors.urgent_ws_bg;
/* The urgent-hint should get noticed, so we unhide the bars shortly */
unhide_bars();
}
xcb_change_gc(xcb_connection,
outputs_walk->bargc,
XCB_GC_FOREGROUND,
&color);
&bg_color);
xcb_change_gc(xcb_connection,
outputs_walk->bargc,
XCB_GC_BACKGROUND,
&color);
&bg_color);
xcb_rectangle_t rect = { i + 1, 1, ws_walk->name_width + 8, font_height + 4 };
xcb_poly_fill_rectangle(xcb_connection,
outputs_walk->buffer,
outputs_walk->bargc,
1,
&rect);
color = get_colorpixel("FFFFFF");
xcb_change_gc(xcb_connection,
outputs_walk->bargc,
XCB_GC_FOREGROUND,
&color);
&fg_color);
xcb_image_text_16(xcb_connection,
ws_walk->name_glyphs,
outputs_walk->buffer,
outputs_walk->bargc,
i + 5, font_height + 1,
i + 5, font_info->font_ascent + 2,
ws_walk->ucs2_name);
i += 10 + ws_walk->name_width;
}
redraw_bars();
i = 0;
}
}
/*
* Redraw the bars, i.e. simply copy the buffer to the barwindow
*
*/
void redraw_bars() {
i3_output *outputs_walk;
SLIST_FOREACH(outputs_walk, outputs, slist) {
xcb_copy_area(xcb_connection,
outputs_walk->buffer,
outputs_walk->bar,
@ -563,7 +851,6 @@ void draw_bars() {
0, 0,
outputs_walk->rect.w,
outputs_walk->rect.h);
i = 0;
xcb_flush(xcb_connection);
}
}