Merge branch 'next'
This commit is contained in:
commit
94a8273b09
|
@ -1,3 +1,4 @@
|
||||||
i3bar
|
i3bar
|
||||||
*.o
|
*.o
|
||||||
core
|
core
|
||||||
|
doc/i3bar.1
|
||||||
|
|
|
@ -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
|
|
@ -6,10 +6,17 @@ FILES:=$(wildcard src/*.c)
|
||||||
FILES:=$(FILES:.c=.o)
|
FILES:=$(FILES:.c=.o)
|
||||||
HEADERS:=$(wildcard include/*.h)
|
HEADERS:=$(wildcard include/*.h)
|
||||||
|
|
||||||
all: ${FILES}
|
all: i3bar doc
|
||||||
|
|
||||||
|
i3bar: ${FILES}
|
||||||
echo "LINK"
|
echo "LINK"
|
||||||
$(CC) -o i3bar ${FILES} ${LDFLAGS}
|
$(CC) -o i3bar ${FILES} ${LDFLAGS}
|
||||||
|
|
||||||
|
doc:
|
||||||
|
echo ""
|
||||||
|
echo "SUBDIR doc"
|
||||||
|
$(MAKE) -C doc
|
||||||
|
|
||||||
src/%.o: src/%.c ${HEADERS}
|
src/%.o: src/%.c ${HEADERS}
|
||||||
echo "CC $<"
|
echo "CC $<"
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
@ -20,6 +27,11 @@ install: all
|
||||||
$(INSTALL) -m 0755 i3bar $(DESTDIR)$(PREFIX)/bin
|
$(INSTALL) -m 0755 i3bar $(DESTDIR)$(PREFIX)/bin
|
||||||
|
|
||||||
clean:
|
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
|
||||||
|
|
|
@ -14,7 +14,7 @@ CFLAGS += -DI3BAR_VERSION=\"${GIT_VERSION}\"
|
||||||
LDFLAGS += -lev
|
LDFLAGS += -lev
|
||||||
LDFLAGS += -lyajl
|
LDFLAGS += -lyajl
|
||||||
LDFLAGS += -lxcb
|
LDFLAGS += -lxcb
|
||||||
LDFLAGS += -lxcb-atom
|
LDFLAGS += -lX11
|
||||||
LDFLAGS += -L/usr/local/lib
|
LDFLAGS += -L/usr/local/lib
|
||||||
|
|
||||||
ifeq ($(DEBUG),1)
|
ifeq ($(DEBUG),1)
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -26,4 +26,16 @@ void start_child(char *command);
|
||||||
*/
|
*/
|
||||||
void kill_child();
|
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
|
#endif
|
||||||
|
|
|
@ -13,7 +13,6 @@ typedef struct rect_t rect;
|
||||||
typedef int bool;
|
typedef int bool;
|
||||||
|
|
||||||
struct ev_loop* main_loop;
|
struct ev_loop* main_loop;
|
||||||
pid_t child_pid;
|
|
||||||
char *statusline;
|
char *statusline;
|
||||||
|
|
||||||
struct rect_t {
|
struct rect_t {
|
||||||
|
@ -31,5 +30,6 @@ struct rect_t {
|
||||||
#include "workspaces.h"
|
#include "workspaces.h"
|
||||||
#include "xcb.h"
|
#include "xcb.h"
|
||||||
#include "ucs2_to_utf8.h"
|
#include "ucs2_to_utf8.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -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
|
|
@ -11,6 +11,10 @@
|
||||||
|
|
||||||
#include "queue.h"
|
#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 */
|
/* Securely free p */
|
||||||
#define FREE(p) do { \
|
#define FREE(p) do { \
|
||||||
if (p != NULL) { \
|
if (p != NULL) { \
|
||||||
|
@ -40,3 +44,14 @@
|
||||||
walk = TAILQ_FIRST(l); \
|
walk = TAILQ_FIRST(l); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} 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)
|
||||||
|
|
|
@ -9,7 +9,21 @@
|
||||||
#ifndef XCB_H_
|
#ifndef XCB_H_
|
||||||
#define 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
|
* Initialize xcb and use the specified fontname for text-rendering
|
||||||
|
@ -17,6 +31,12 @@ int font_height;
|
||||||
*/
|
*/
|
||||||
void init_xcb();
|
void init_xcb();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize the colors
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void init_colors(const struct xcb_color_strings_t *colors);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Cleanup the xcb-stuff.
|
* Cleanup the xcb-stuff.
|
||||||
* Called once, before the program terminates.
|
* Called once, before the program terminates.
|
||||||
|
@ -49,11 +69,17 @@ void reconfig_windows();
|
||||||
void draw_bars();
|
void draw_bars();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calculate the rendered width of a string with the configured font.
|
* Redraw the bars, i.e. simply copy the buffer to the barwindow
|
||||||
* The string has to be encoded in ucs2 and glyph_len has to be the length
|
|
||||||
* of the string (in width)
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
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
|
#endif
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -20,6 +21,9 @@
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
/* Global variables for child_*() */
|
||||||
|
pid_t child_pid;
|
||||||
|
|
||||||
/* stdin- and sigchild-watchers */
|
/* stdin- and sigchild-watchers */
|
||||||
ev_io *stdin_io;
|
ev_io *stdin_io;
|
||||||
ev_child *child_sig;
|
ev_child *child_sig;
|
||||||
|
@ -37,38 +41,8 @@ void cleanup() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since we don't use colors and stuff, we strip the dzen-formatstrings
|
* Callbalk for stdin. We read a line from stdin and store the result
|
||||||
*
|
* in statusline
|
||||||
*/
|
|
||||||
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
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
|
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);
|
n = read(fd, buffer + rec, buffer_len - rec);
|
||||||
if (n == -1) {
|
if (n == -1) {
|
||||||
if (errno == EAGAIN) {
|
if (errno == EAGAIN) {
|
||||||
|
/* remove trailing newline and finish up */
|
||||||
|
buffer[rec-1] = '\0';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
printf("ERROR: read() failed!");
|
ELOG("read() failed!\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
if (rec == buffer_len) {
|
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;
|
buffer_len += STDIN_CHUNK_SIZE;
|
||||||
FREE(tmp);
|
buffer = realloc(buffer, buffer_len);
|
||||||
} else {
|
} else {
|
||||||
|
/* remove trailing newline and finish up */
|
||||||
|
buffer[rec-1] = '\0';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,10 +79,9 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
|
||||||
FREE(buffer);
|
FREE(buffer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
strip_dzen_formats(buffer);
|
|
||||||
FREE(statusline);
|
FREE(statusline);
|
||||||
statusline = buffer;
|
statusline = buffer;
|
||||||
printf("%s\n", buffer);
|
DLOG("%s\n", buffer);
|
||||||
draw_bars();
|
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) {
|
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,
|
child_pid,
|
||||||
watcher->rstatus);
|
watcher->rstatus);
|
||||||
cleanup();
|
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.
|
* 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
|
* 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) {
|
void start_child(char *command) {
|
||||||
child_pid = 0;
|
child_pid = 0;
|
||||||
if (command != NULL) {
|
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);
|
pipe(fd);
|
||||||
child_pid = fork();
|
/* We also need this temporary pipe to get back the pid of i3status */
|
||||||
switch (child_pid) {
|
pipe(tmp);
|
||||||
|
switch (fork()) {
|
||||||
case -1:
|
case -1:
|
||||||
printf("ERROR: Couldn't fork()");
|
ELOG("Couldn't fork()\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
case 0:
|
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)
|
if ((shell = getenv("SHELL")) == NULL)
|
||||||
shell = "/bin/sh";
|
shell = "/bin/sh";
|
||||||
|
|
||||||
execl(shell, shell, "-c", command, (char*) NULL);
|
execl(shell, shell, "-c", command, (char*) NULL);
|
||||||
return;
|
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:
|
default:
|
||||||
|
/* Parent-process. Rerout stdin */
|
||||||
close(fd[1]);
|
close(fd[1]);
|
||||||
|
|
||||||
dup2(fd[0], STDIN_FILENO);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
wait(0);
|
||||||
|
|
||||||
|
/* We set O_NONBLOCK because blocking is evil in event-driven software */
|
||||||
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
|
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
|
||||||
|
|
||||||
stdin_io = malloc(sizeof(ev_io));
|
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
|
* free()s the stdin- and sigchild-watchers
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void kill_child() {
|
void kill_child() {
|
||||||
if (child_pid != 0) {
|
if (child_pid != 0) {
|
||||||
kill(child_pid, SIGQUIT);
|
kill(child_pid, SIGTERM);
|
||||||
}
|
}
|
||||||
cleanup();
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ typedef void(*handler_t)(char*);
|
||||||
int get_ipc_fd(const char *socket_path) {
|
int get_ipc_fd(const char *socket_path) {
|
||||||
int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
|
int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
|
||||||
if (sockfd == -1) {
|
if (sockfd == -1) {
|
||||||
printf("ERROR: Could not create Socket!\n");
|
ELOG("Could not create Socket!\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ int get_ipc_fd(const char *socket_path) {
|
||||||
addr.sun_family = AF_LOCAL;
|
addr.sun_family = AF_LOCAL;
|
||||||
strcpy(addr.sun_path, socket_path);
|
strcpy(addr.sun_path, socket_path);
|
||||||
if (connect(sockfd, (const struct sockaddr*) &addr, sizeof(struct sockaddr_un)) < 0) {
|
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);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
return sockfd;
|
return sockfd;
|
||||||
|
@ -51,7 +51,7 @@ int get_ipc_fd(const char *socket_path) {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void got_command_reply(char *reply) {
|
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) {
|
void got_workspace_reply(char *reply) {
|
||||||
printf("Got Workspace-Data!\n");
|
DLOG("Got Workspace-Data!\n");
|
||||||
parse_workspaces_json(reply);
|
parse_workspaces_json(reply);
|
||||||
draw_bars();
|
draw_bars();
|
||||||
}
|
}
|
||||||
|
@ -70,8 +70,8 @@ void got_workspace_reply(char *reply) {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void got_subscribe_reply(char *reply) {
|
void got_subscribe_reply(char *reply) {
|
||||||
printf("Got Subscribe Reply: %s\n", reply);
|
DLOG("Got Subscribe Reply: %s\n", reply);
|
||||||
/* FIXME: Error handling for subscribe-commands */
|
/* TODO: Error handling for subscribe-commands */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -79,9 +79,9 @@ void got_subscribe_reply(char *reply) {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void got_output_reply(char *reply) {
|
void got_output_reply(char *reply) {
|
||||||
printf("Parsing Outputs-JSON...\n");
|
DLOG("Parsing Outputs-JSON...\n");
|
||||||
parse_outputs_json(reply);
|
parse_outputs_json(reply);
|
||||||
printf("Reconfiguring Windows...\n");
|
DLOG("Reconfiguring Windows...\n");
|
||||||
reconfig_windows();
|
reconfig_windows();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ handler_t reply_handlers[] = {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void got_workspace_event(char *event) {
|
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);
|
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) {
|
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_OUTPUTS, NULL);
|
||||||
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, 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) {
|
void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
|
||||||
printf("Got data!\n");
|
DLOG("Got data!\n");
|
||||||
int fd = watcher->fd;
|
int fd = watcher->fd;
|
||||||
|
|
||||||
/* First we only read the header, because we know it's length */
|
/* First we only read the header, because we know it's length */
|
||||||
uint32_t header_len = strlen(I3_IPC_MAGIC) + sizeof(uint32_t)*2;
|
uint32_t header_len = strlen(I3_IPC_MAGIC) + sizeof(uint32_t)*2;
|
||||||
char *header = malloc(header_len);
|
char *header = malloc(header_len);
|
||||||
if (header == NULL) {
|
if (header == NULL) {
|
||||||
printf("ERROR: Could not allocate memory!\n");
|
ELOG("Could not allocate memory!\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* We first parse the fixed-length IPC-header, to know, how much data
|
||||||
|
* we have to expect */
|
||||||
uint32_t rec = 0;
|
uint32_t rec = 0;
|
||||||
while (rec < header_len) {
|
while (rec < header_len) {
|
||||||
int n = read(fd, header + rec, header_len - rec);
|
int n = read(fd, header + rec, header_len - rec);
|
||||||
if (n == -1) {
|
if (n == -1) {
|
||||||
printf("ERROR: read() failed!\n");
|
ELOG("read() failed!\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
printf("ERROR: Nothing to read!\n");
|
ELOG("Nothing to read!\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
rec += n;
|
rec += n;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strncmp(header, I3_IPC_MAGIC, strlen(I3_IPC_MAGIC))) {
|
if (strncmp(header, I3_IPC_MAGIC, strlen(I3_IPC_MAGIC))) {
|
||||||
printf("ERROR: Wrong magic code: %.*s\n Expected: %s\n",
|
ELOG("Wrong magic code: %.*s\n Expected: %s\n",
|
||||||
(int) strlen(I3_IPC_MAGIC),
|
(int) strlen(I3_IPC_MAGIC),
|
||||||
header,
|
header,
|
||||||
I3_IPC_MAGIC);
|
I3_IPC_MAGIC);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Know we read the rest of the message */
|
|
||||||
char *walk = header + strlen(I3_IPC_MAGIC);
|
char *walk = header + strlen(I3_IPC_MAGIC);
|
||||||
uint32_t size = *((uint32_t*) walk);
|
uint32_t size = *((uint32_t*) walk);
|
||||||
walk += sizeof(uint32_t);
|
walk += sizeof(uint32_t);
|
||||||
uint32_t type = *((uint32_t*) walk);
|
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);
|
char *buffer = malloc(size + 1);
|
||||||
if (buffer == NULL) {
|
if (buffer == NULL) {
|
||||||
printf("ERROR: Could not allocate memory!\n");
|
ELOG("Could not allocate memory!\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
rec = 0;
|
rec = 0;
|
||||||
|
@ -171,11 +175,11 @@ void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
|
||||||
while (rec < size) {
|
while (rec < size) {
|
||||||
int n = read(fd, buffer + rec, size - rec);
|
int n = read(fd, buffer + rec, size - rec);
|
||||||
if (n == -1) {
|
if (n == -1) {
|
||||||
printf("ERROR: read() failed!\n");
|
ELOG("read() failed!\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
printf("ERROR: Nothing to read!\n");
|
ELOG("Nothing to read!\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
rec += n;
|
rec += n;
|
||||||
|
@ -205,10 +209,14 @@ int i3_send_msg(uint32_t type, const char *payload) {
|
||||||
len = strlen(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;
|
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);
|
char *buffer = malloc(to_write);
|
||||||
if (buffer == NULL) {
|
if (buffer == NULL) {
|
||||||
printf("ERROR: Could not allocate memory\n");
|
ELOG("Could not allocate memory\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,7 +236,7 @@ int i3_send_msg(uint32_t type, const char *payload) {
|
||||||
while (to_write > 0) {
|
while (to_write > 0) {
|
||||||
int n = write(i3_connection->fd, buffer + written, to_write);
|
int n = write(i3_connection->fd, buffer + written, to_write);
|
||||||
if (n == -1) {
|
if (n == -1) {
|
||||||
printf("ERROR: write() failed!\n");
|
ELOG("write() failed!\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
179
i3bar/src/main.c
179
i3bar/src/main.c
|
@ -18,40 +18,130 @@
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
char *i3_default_sock_path = "~/.i3/ipc.sock";
|
/*
|
||||||
|
* Glob path, i.e. expand ~
|
||||||
|
*
|
||||||
|
*/
|
||||||
char *expand_path(char *path) {
|
char *expand_path(char *path) {
|
||||||
static glob_t globbuf;
|
static glob_t globbuf;
|
||||||
if (glob(path, GLOB_NOCHECK | GLOB_TILDE, NULL, &globbuf) < 0) {
|
if (glob(path, GLOB_NOCHECK | GLOB_TILDE, NULL, &globbuf) < 0) {
|
||||||
printf("glob() failed");
|
ELOG("glob() failed\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
char *result = strdup(globbuf.gl_pathc > 0 ? globbuf.gl_pathv[0] : path);
|
char *result = strdup(globbuf.gl_pathc > 0 ? globbuf.gl_pathv[0] : path);
|
||||||
if (result == NULL) {
|
if (result == NULL) {
|
||||||
printf("malloc() failed");
|
ELOG("malloc() failed\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
globfree(&globbuf);
|
globfree(&globbuf);
|
||||||
return result;
|
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 main(int argc, char **argv) {
|
||||||
int opt;
|
int opt;
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
char *socket_path = NULL;
|
char *socket_path = NULL;
|
||||||
char *command = NULL;
|
char *command = NULL;
|
||||||
char *fontname = 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[] = {
|
static struct option long_opt[] = {
|
||||||
{ "socket", required_argument, 0, 's' },
|
{ "socket", required_argument, 0, 's' },
|
||||||
{ "command", required_argument, 0, 'c' },
|
{ "command", required_argument, 0, 'c' },
|
||||||
{ "font", required_argument, 0, 'f' },
|
{ "hide", no_argument, 0, 'm' },
|
||||||
{ "help", no_argument, 0, 'h' },
|
{ "font", required_argument, 0, 'f' },
|
||||||
{ "version", no_argument, 0, 'v' },
|
{ "help", no_argument, 0, 'h' },
|
||||||
{ NULL, 0, 0, 0}
|
{ "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) {
|
switch (opt) {
|
||||||
case 's':
|
case 's':
|
||||||
socket_path = expand_path(optarg);
|
socket_path = expand_path(optarg);
|
||||||
|
@ -59,47 +149,102 @@ int main(int argc, char **argv) {
|
||||||
case 'c':
|
case 'c':
|
||||||
command = strdup(optarg);
|
command = strdup(optarg);
|
||||||
break;
|
break;
|
||||||
|
case 'm':
|
||||||
|
config.hide_on_modifier = 1;
|
||||||
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
fontname = strdup(optarg);
|
fontname = strdup(optarg);
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
printf("i3bar version " I3BAR_VERSION " © 2010 Axel Wagner and contributors\n");
|
printf("i3bar version " I3BAR_VERSION " © 2010 Axel Wagner and contributors\n");
|
||||||
exit(EXIT_SUCCESS);
|
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:
|
default:
|
||||||
printf("Usage: %s [-s socket_path] [-c command] [-f font] [-h]\n", argv[0]);
|
print_usage(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");
|
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fontname == NULL) {
|
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";
|
fontname = "-misc-fixed-medium-r-semicondensed--12-110-75-75-c-60-iso10646-1";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (socket_path == NULL) {
|
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);
|
socket_path = expand_path(i3_default_sock_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
main_loop = ev_default_loop(0);
|
main_loop = ev_default_loop(0);
|
||||||
|
|
||||||
|
init_colors(&colors);
|
||||||
init_xcb(fontname);
|
init_xcb(fontname);
|
||||||
|
|
||||||
|
free_colors(&colors);
|
||||||
|
|
||||||
init_outputs();
|
init_outputs();
|
||||||
init_connection(socket_path);
|
init_connection(socket_path);
|
||||||
|
|
||||||
FREE(socket_path);
|
FREE(socket_path);
|
||||||
|
|
||||||
|
/* We subscribe to the i3-events we need */
|
||||||
subscribe_events();
|
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_OUTPUTS, NULL);
|
||||||
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, 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);
|
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);
|
ev_loop(main_loop, 0);
|
||||||
|
|
||||||
kill_child();
|
kill_child();
|
||||||
|
|
|
@ -230,7 +230,7 @@ void parse_outputs_json(char *json) {
|
||||||
case yajl_status_client_canceled:
|
case yajl_status_client_canceled:
|
||||||
case yajl_status_insufficient_data:
|
case yajl_status_insufficient_data:
|
||||||
case yajl_status_error:
|
case yajl_status_error:
|
||||||
printf("ERROR: Could not parse outputs-reply!\n");
|
ELOG("Could not parse outputs-reply!\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
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->ucs2_name = ucs2_name;
|
||||||
params->workspaces_walk->name_glyphs = ucs2_len;
|
params->workspaces_walk->name_glyphs = ucs2_len;
|
||||||
params->workspaces_walk->name_width = get_string_width(params->workspaces_walk->ucs2_name,
|
params->workspaces_walk->name_width =
|
||||||
params->workspaces_walk->name_glyphs);
|
predict_text_extents(params->workspaces_walk->ucs2_name,
|
||||||
|
params->workspaces_walk->name_glyphs);
|
||||||
|
|
||||||
printf("Got Workspace %s, name_width: %d, glyphs: %d\n",
|
DLOG("Got Workspace %s, name_width: %d, glyphs: %d\n",
|
||||||
params->workspaces_walk->name,
|
params->workspaces_walk->name,
|
||||||
params->workspaces_walk->name_width,
|
params->workspaces_walk->name_width,
|
||||||
params->workspaces_walk->name_glyphs);
|
params->workspaces_walk->name_glyphs);
|
||||||
FREE(params->cur_key);
|
FREE(params->cur_key);
|
||||||
|
|
||||||
return 1;
|
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));
|
params->cur_key = malloc(sizeof(unsigned char) * (keyLen + 1));
|
||||||
if (params->cur_key == NULL) {
|
if (params->cur_key == NULL) {
|
||||||
printf("ERROR: Could not allocate memory!\n");
|
ELOG("Could not allocate memory!\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
strncpy(params->cur_key, (const char*) keyVal, keyLen);
|
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_client_canceled:
|
||||||
case yajl_status_insufficient_data:
|
case yajl_status_insufficient_data:
|
||||||
case yajl_status_error:
|
case yajl_status_error:
|
||||||
printf("ERROR: Could not parse workspaces-reply!\n");
|
ELOG("Could not parse workspaces-reply!\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
585
i3bar/src/xcb.c
585
i3bar/src/xcb.c
|
@ -10,13 +10,18 @@
|
||||||
*/
|
*/
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
#include <xcb/xproto.h>
|
#include <xcb/xproto.h>
|
||||||
#include <xcb/xcb_event.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <i3/ipc.h>
|
#include <i3/ipc.h>
|
||||||
#include <ev.h>
|
#include <ev.h>
|
||||||
|
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/XKBlib.h>
|
||||||
|
#include <X11/extensions/XKB.h>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
/* We save the Atoms in an easy to access array, indexed by an enum */
|
/* 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 */
|
/* Variables, that are the same for all functions at all times */
|
||||||
xcb_connection_t *xcb_connection;
|
xcb_connection_t *xcb_connection;
|
||||||
xcb_screen_t *xcb_screens;
|
xcb_screen_t *xcb_screen;
|
||||||
xcb_window_t xcb_root;
|
xcb_window_t xcb_root;
|
||||||
xcb_font_t xcb_font;
|
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 */
|
/* Event-Watchers, to interact with the user */
|
||||||
ev_prepare *xcb_prep;
|
ev_prepare *xcb_prep;
|
||||||
ev_check *xcb_chk;
|
ev_check *xcb_chk;
|
||||||
ev_io *xcb_io;
|
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.
|
* 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);
|
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).
|
* 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-
|
* 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) {
|
if (walk == NULL) {
|
||||||
printf("Unknown Bar klicked!\n");
|
DLOG("Unknown Bar klicked!\n");
|
||||||
return;
|
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) {
|
TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
|
||||||
if (cur_ws->visible) {
|
if (cur_ws->visible) {
|
||||||
break;
|
break;
|
||||||
|
@ -87,20 +311,20 @@ void handle_button(xcb_button_press_event_t *event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cur_ws == NULL) {
|
if (cur_ws == NULL) {
|
||||||
printf("No Workspace active?\n");
|
DLOG("No Workspace active?\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t x = event->event_x;
|
int32_t x = event->event_x;
|
||||||
|
|
||||||
printf("Got Button %d\n", event->detail);
|
DLOG("Got Button %d\n", event->detail);
|
||||||
|
|
||||||
switch (event->detail) {
|
switch (event->detail) {
|
||||||
case 1:
|
case 1:
|
||||||
/* Left Mousbutton. We determine, which button was clicked
|
/* Left Mousbutton. We determine, which button was clicked
|
||||||
* and set cur_ws accordingly */
|
* and set cur_ws accordingly */
|
||||||
TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
|
TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
|
||||||
printf("x = %d\n", x);
|
DLOG("x = %d\n", x);
|
||||||
if (x < cur_ws->name_width + 10) {
|
if (x < cur_ws->name_width + 10) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -157,7 +381,7 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) {
|
||||||
switch (event->response_type & ~0x80) {
|
switch (event->response_type & ~0x80) {
|
||||||
case XCB_EXPOSE:
|
case XCB_EXPOSE:
|
||||||
/* Expose-events happen, when the window needs to be redrawn */
|
/* Expose-events happen, when the window needs to be redrawn */
|
||||||
draw_bars();
|
redraw_bars();
|
||||||
break;
|
break;
|
||||||
case XCB_BUTTON_PRESS:
|
case XCB_BUTTON_PRESS:
|
||||||
/* Button-press-events are mouse-buttons clicked on one of our bars */
|
/* 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.
|
* We need to bind to the modifier per XKB. Sadly, XCB does not implement this
|
||||||
* The string has to be encoded in ucs2 and glyph_len has to be the length
|
|
||||||
* of the string (in width)
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
int get_string_width(xcb_char2b_t *string, int glyph_len) {
|
void xkb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
|
||||||
xcb_query_text_extents_cookie_t cookie;
|
XkbEvent ev;
|
||||||
xcb_query_text_extents_reply_t *reply;
|
int modstate;
|
||||||
xcb_generic_error_t *error;
|
|
||||||
int width;
|
|
||||||
|
|
||||||
cookie = xcb_query_text_extents(xcb_connection, xcb_font, glyph_len, string);
|
DLOG("Got XKB-Event!\n");
|
||||||
reply = xcb_query_text_extents_reply(xcb_connection, cookie, &error);
|
|
||||||
if (reply == NULL) {
|
while (XPending(xkb_dpy)) {
|
||||||
printf("ERROR: Could not get text extents!");
|
XNextEvent(xkb_dpy, (XEvent*)&ev);
|
||||||
return 7;
|
|
||||||
|
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;
|
if (modstate != mod_pressed) {
|
||||||
free(reply);
|
if (modstate == 0) {
|
||||||
return width;
|
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 */
|
/* FIXME: xcb_connect leaks Memory */
|
||||||
xcb_connection = xcb_connect(NULL, NULL);
|
xcb_connection = xcb_connect(NULL, NULL);
|
||||||
if (xcb_connection_has_error(xcb_connection)) {
|
if (xcb_connection_has_error(xcb_connection)) {
|
||||||
printf("Cannot open display\n");
|
ELOG("Cannot open display\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
printf("Connected to xcb\n");
|
DLOG("Connected to xcb\n");
|
||||||
|
|
||||||
/* We have to request the atoms we need */
|
/* We have to request the atoms we need */
|
||||||
#define ATOM_DO(name) atom_cookies[name] = xcb_intern_atom(xcb_connection, 0, strlen(#name), #name);
|
#define ATOM_DO(name) atom_cookies[name] = xcb_intern_atom(xcb_connection, 0, strlen(#name), #name);
|
||||||
#include "xcb_atoms.def"
|
#include "xcb_atoms.def"
|
||||||
|
|
||||||
xcb_screens = xcb_setup_roots_iterator(xcb_get_setup(xcb_connection)).data;
|
xcb_screen = xcb_setup_roots_iterator(xcb_get_setup(xcb_connection)).data;
|
||||||
xcb_root = xcb_screens->root;
|
xcb_root = xcb_screen->root;
|
||||||
|
|
||||||
/* We load and allocate the font */
|
/* We load and allocate the font */
|
||||||
xcb_font = xcb_generate_id(xcb_connection);
|
xcb_font = xcb_generate_id(xcb_connection);
|
||||||
|
@ -227,12 +466,71 @@ void init_xcb(char *fontname) {
|
||||||
strlen(fontname),
|
strlen(fontname),
|
||||||
fontname);
|
fontname);
|
||||||
|
|
||||||
/* We also need the fontheight to configure our bars accordingly */
|
/* We need to save info about the font, because we need the fonts height and
|
||||||
xcb_list_fonts_with_info_cookie_t cookie;
|
* information about the width of characters */
|
||||||
cookie = xcb_list_fonts_with_info(xcb_connection,
|
xcb_query_font_cookie_t query_font_cookie;
|
||||||
1,
|
query_font_cookie = xcb_query_font(xcb_connection,
|
||||||
strlen(fontname),
|
xcb_font);
|
||||||
fontname);
|
|
||||||
|
/* 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 */
|
/* The varios Watchers to communicate with xcb */
|
||||||
xcb_io = malloc(sizeof(ev_io));
|
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 */
|
/* Now we get the atoms and save them in a nice data-structure */
|
||||||
get_atoms();
|
get_atoms();
|
||||||
|
|
||||||
xcb_generic_error_t *err = xcb_request_check(xcb_connection,
|
/* Now we save the font-infos */
|
||||||
open_font_cookie);
|
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) {
|
if (xcb_request_failed(open_font_cookie, "Could not open font")) {
|
||||||
printf("ERROR: Could not open font! XCB-Error-Code: %d\n", err->error_code);
|
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now we calculate the font-height */
|
if (xcb_query_font_char_infos_length(font_info) == 0) {
|
||||||
xcb_list_fonts_with_info_reply_t *reply;
|
font_table = NULL;
|
||||||
reply = xcb_list_fonts_with_info_reply(xcb_connection,
|
} else {
|
||||||
cookie,
|
font_table = xcb_query_font_char_infos(font_info);
|
||||||
NULL);
|
}
|
||||||
font_height = reply->font_ascent + reply->font_descent;
|
|
||||||
FREE(reply);
|
|
||||||
|
|
||||||
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_chk);
|
||||||
FREE(xcb_prep);
|
FREE(xcb_prep);
|
||||||
FREE(xcb_io);
|
FREE(xcb_io);
|
||||||
|
FREE(font_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -299,11 +602,15 @@ void clean_xcb() {
|
||||||
void get_atoms() {
|
void get_atoms() {
|
||||||
xcb_intern_atom_reply_t *reply;
|
xcb_intern_atom_reply_t *reply;
|
||||||
#define ATOM_DO(name) reply = xcb_intern_atom_reply(xcb_connection, atom_cookies[name], NULL); \
|
#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; \
|
atoms[name] = reply->atom; \
|
||||||
free(reply);
|
free(reply);
|
||||||
|
|
||||||
#include "xcb_atoms.def"
|
#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() {
|
void reconfig_windows() {
|
||||||
uint32_t mask;
|
uint32_t mask;
|
||||||
uint32_t values[4];
|
uint32_t values[5];
|
||||||
|
|
||||||
xcb_generic_error_t *err;
|
xcb_generic_error_t *err;
|
||||||
|
|
||||||
|
@ -336,42 +643,45 @@ void reconfig_windows() {
|
||||||
if (!walk->active) {
|
if (!walk->active) {
|
||||||
/* If an output is not active, we destroy it's bar */
|
/* If an output is not active, we destroy it's bar */
|
||||||
/* FIXME: Maybe we rather want to unmap? */
|
/* 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);
|
destroy_window(walk);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (walk->bar == XCB_NONE) {
|
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->bar = xcb_generate_id(xcb_connection);
|
||||||
walk->buffer = 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 */
|
/* 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 */
|
/* The events we want to receive */
|
||||||
values[1] = XCB_EVENT_MASK_EXPOSURE |
|
values[2] = XCB_EVENT_MASK_EXPOSURE |
|
||||||
XCB_EVENT_MASK_BUTTON_PRESS;
|
XCB_EVENT_MASK_BUTTON_PRESS;
|
||||||
|
|
||||||
xcb_void_cookie_t win_cookie = xcb_create_window_checked(xcb_connection,
|
xcb_void_cookie_t win_cookie = xcb_create_window_checked(xcb_connection,
|
||||||
xcb_screens->root_depth,
|
xcb_screen->root_depth,
|
||||||
walk->bar,
|
walk->bar,
|
||||||
xcb_root,
|
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,
|
walk->rect.w, font_height + 6,
|
||||||
1,
|
1,
|
||||||
XCB_WINDOW_CLASS_INPUT_OUTPUT,
|
XCB_WINDOW_CLASS_INPUT_OUTPUT,
|
||||||
xcb_screens->root_visual,
|
xcb_screen->root_visual,
|
||||||
mask,
|
mask,
|
||||||
values);
|
values);
|
||||||
|
|
||||||
|
/* The double-buffer we use to render stuff off-screen */
|
||||||
xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection,
|
xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection,
|
||||||
xcb_screens->root_depth,
|
xcb_screen->root_depth,
|
||||||
walk->buffer,
|
walk->buffer,
|
||||||
walk->bar,
|
walk->bar,
|
||||||
walk->rect.w,
|
walk->rect.w,
|
||||||
walk->rect.h);
|
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_void_cookie_t prop_cookie = xcb_change_property(xcb_connection,
|
||||||
XCB_PROP_MODE_REPLACE,
|
XCB_PROP_MODE_REPLACE,
|
||||||
walk->bar,
|
walk->bar,
|
||||||
|
@ -381,42 +691,28 @@ void reconfig_windows() {
|
||||||
1,
|
1,
|
||||||
(unsigned char*) &atoms[_NET_WM_WINDOW_TYPE_DOCK]);
|
(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);
|
walk->bargc = xcb_generate_id(xcb_connection);
|
||||||
mask = XCB_GC_FONT;
|
mask = XCB_GC_FONT;
|
||||||
values[0] = xcb_font;
|
values[0] = xcb_font;
|
||||||
|
|
||||||
xcb_void_cookie_t gc_cookie = xcb_create_gc_checked(xcb_connection,
|
xcb_void_cookie_t gc_cookie = xcb_create_gc_checked(xcb_connection,
|
||||||
walk->bargc,
|
walk->bargc,
|
||||||
walk->bar,
|
walk->bar,
|
||||||
mask,
|
mask,
|
||||||
values);
|
values);
|
||||||
|
|
||||||
/* We finally map the bar (display it on screen) */
|
/* We finally map the bar (display it on screen), unless the modifier-switch is on */
|
||||||
xcb_void_cookie_t map_cookie = xcb_map_window_checked(xcb_connection, walk->bar);
|
xcb_void_cookie_t map_cookie;
|
||||||
|
if (!config.hide_on_modifier) {
|
||||||
if ((err = xcb_request_check(xcb_connection, win_cookie)) != NULL) {
|
map_cookie = xcb_map_window_checked(xcb_connection, walk->bar);
|
||||||
printf("ERROR: Could not create Window. XCB-errorcode: %d\n", err->error_code);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = xcb_request_check(xcb_connection, pm_cookie)) != NULL) {
|
if (xcb_request_failed(win_cookie, "Could not create window") ||
|
||||||
printf("ERROR: Could not create Pixmap. XCB-errorcode: %d\n", err->error_code);
|
xcb_request_failed(pm_cookie, "Could not create pixmap") ||
|
||||||
exit(EXIT_FAILURE);
|
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"))) {
|
||||||
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);
|
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -424,38 +720,22 @@ void reconfig_windows() {
|
||||||
mask = XCB_CONFIG_WINDOW_X |
|
mask = XCB_CONFIG_WINDOW_X |
|
||||||
XCB_CONFIG_WINDOW_Y |
|
XCB_CONFIG_WINDOW_Y |
|
||||||
XCB_CONFIG_WINDOW_WIDTH |
|
XCB_CONFIG_WINDOW_WIDTH |
|
||||||
XCB_CONFIG_WINDOW_HEIGHT;
|
XCB_CONFIG_WINDOW_HEIGHT |
|
||||||
|
XCB_CONFIG_WINDOW_STACK_MODE;
|
||||||
values[0] = walk->rect.x;
|
values[0] = walk->rect.x;
|
||||||
values[1] = walk->rect.y + walk->rect.h - font_height - 6;
|
values[1] = walk->rect.y + walk->rect.h - font_height - 6;
|
||||||
values[2] = walk->rect.w;
|
values[2] = walk->rect.w;
|
||||||
values[3] = font_height + 6;
|
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,
|
xcb_void_cookie_t cfg_cookie = xcb_configure_window_checked(xcb_connection,
|
||||||
walk->bar,
|
walk->bar,
|
||||||
mask,
|
mask,
|
||||||
values);
|
values);
|
||||||
|
if (xcb_request_failed(cfg_cookie, "Could not reconfigure window")) {
|
||||||
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);
|
|
||||||
exit(EXIT_FAILURE);
|
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() {
|
void draw_bars() {
|
||||||
printf("Drawing Bars...\n");
|
DLOG("Drawing Bars...\n");
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
|
refresh_statusline();
|
||||||
|
|
||||||
i3_output *outputs_walk;
|
i3_output *outputs_walk;
|
||||||
SLIST_FOREACH(outputs_walk, outputs, slist) {
|
SLIST_FOREACH(outputs_walk, outputs, slist) {
|
||||||
if (!outputs_walk->active) {
|
if (!outputs_walk->active) {
|
||||||
printf("Output %s inactive, skipping...\n", outputs_walk->name);
|
DLOG("Output %s inactive, skipping...\n", outputs_walk->name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (outputs_walk->bar == XCB_NONE) {
|
if (outputs_walk->bar == XCB_NONE) {
|
||||||
|
/* Oh shit, an active output without an own bar. Create it now! */
|
||||||
reconfig_windows();
|
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,
|
xcb_change_gc(xcb_connection,
|
||||||
outputs_walk->bargc,
|
outputs_walk->bargc,
|
||||||
XCB_GC_FOREGROUND,
|
XCB_GC_FOREGROUND,
|
||||||
|
@ -486,75 +771,78 @@ void draw_bars() {
|
||||||
outputs_walk->bargc,
|
outputs_walk->bargc,
|
||||||
1,
|
1,
|
||||||
&rect);
|
&rect);
|
||||||
|
|
||||||
if (statusline != NULL) {
|
if (statusline != NULL) {
|
||||||
printf("Printing statusline!\n");
|
DLOG("Printing statusline!\n");
|
||||||
xcb_change_gc(xcb_connection,
|
|
||||||
|
/* 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,
|
outputs_walk->bargc,
|
||||||
XCB_GC_BACKGROUND,
|
MAX(0, (int16_t)(statusline_width - outputs_walk->rect.w + 4)), 0,
|
||||||
&color);
|
MAX(0, (int16_t)(outputs_walk->rect.w - statusline_width - 4)), 3,
|
||||||
color = get_colorpixel("FFFFFF");
|
MIN(outputs_walk->rect.w - 4, statusline_width), font_height);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
i3_ws *ws_walk;
|
i3_ws *ws_walk;
|
||||||
TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
|
TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
|
||||||
printf("Drawing Button for WS %s at x = %d\n", ws_walk->name, i);
|
DLOG("Drawing Button for WS %s at x = %d\n", ws_walk->name, i);
|
||||||
uint32_t color = get_colorpixel("240000");
|
uint32_t fg_color = colors.inactive_ws_fg;
|
||||||
|
uint32_t bg_color = colors.inactive_ws_bg;
|
||||||
if (ws_walk->visible) {
|
if (ws_walk->visible) {
|
||||||
color = get_colorpixel("480000");
|
fg_color = colors.active_ws_fg;
|
||||||
|
bg_color = colors.active_ws_bg;
|
||||||
}
|
}
|
||||||
if (ws_walk->urgent) {
|
if (ws_walk->urgent) {
|
||||||
printf("WS %s is urgent!\n", ws_walk->name);
|
DLOG("WS %s is urgent!\n", ws_walk->name);
|
||||||
color = get_colorpixel("002400");
|
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,
|
xcb_change_gc(xcb_connection,
|
||||||
outputs_walk->bargc,
|
outputs_walk->bargc,
|
||||||
XCB_GC_FOREGROUND,
|
XCB_GC_FOREGROUND,
|
||||||
&color);
|
&bg_color);
|
||||||
xcb_change_gc(xcb_connection,
|
xcb_change_gc(xcb_connection,
|
||||||
outputs_walk->bargc,
|
outputs_walk->bargc,
|
||||||
XCB_GC_BACKGROUND,
|
XCB_GC_BACKGROUND,
|
||||||
&color);
|
&bg_color);
|
||||||
xcb_rectangle_t rect = { i + 1, 1, ws_walk->name_width + 8, font_height + 4 };
|
xcb_rectangle_t rect = { i + 1, 1, ws_walk->name_width + 8, font_height + 4 };
|
||||||
xcb_poly_fill_rectangle(xcb_connection,
|
xcb_poly_fill_rectangle(xcb_connection,
|
||||||
outputs_walk->buffer,
|
outputs_walk->buffer,
|
||||||
outputs_walk->bargc,
|
outputs_walk->bargc,
|
||||||
1,
|
1,
|
||||||
&rect);
|
&rect);
|
||||||
color = get_colorpixel("FFFFFF");
|
|
||||||
xcb_change_gc(xcb_connection,
|
xcb_change_gc(xcb_connection,
|
||||||
outputs_walk->bargc,
|
outputs_walk->bargc,
|
||||||
XCB_GC_FOREGROUND,
|
XCB_GC_FOREGROUND,
|
||||||
&color);
|
&fg_color);
|
||||||
xcb_image_text_16(xcb_connection,
|
xcb_image_text_16(xcb_connection,
|
||||||
ws_walk->name_glyphs,
|
ws_walk->name_glyphs,
|
||||||
outputs_walk->buffer,
|
outputs_walk->buffer,
|
||||||
outputs_walk->bargc,
|
outputs_walk->bargc,
|
||||||
i + 5, font_height + 1,
|
i + 5, font_info->font_ascent + 2,
|
||||||
ws_walk->ucs2_name);
|
ws_walk->ucs2_name);
|
||||||
i += 10 + ws_walk->name_width;
|
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,
|
xcb_copy_area(xcb_connection,
|
||||||
outputs_walk->buffer,
|
outputs_walk->buffer,
|
||||||
outputs_walk->bar,
|
outputs_walk->bar,
|
||||||
|
@ -563,7 +851,6 @@ void draw_bars() {
|
||||||
0, 0,
|
0, 0,
|
||||||
outputs_walk->rect.w,
|
outputs_walk->rect.w,
|
||||||
outputs_walk->rect.h);
|
outputs_walk->rect.h);
|
||||||
|
xcb_flush(xcb_connection);
|
||||||
i = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue