Merge branch 'bar-config' into next

This commit is contained in:
Michael Stapelberg 2011-10-22 17:02:49 +01:00
commit d75560981e
32 changed files with 1817 additions and 1126 deletions

View File

@ -1,7 +1,7 @@
IPC interface (interprocess communication) IPC interface (interprocess communication)
========================================== ==========================================
Michael Stapelberg <michael+i3@stapelberg.de> Michael Stapelberg <michael+i3@stapelberg.de>
March 2010 October 2011
This document describes how to interface with i3 from a separate process. This This document describes how to interface with i3 from a separate process. This
is useful for example to remote-control i3 (to write test cases for example) or is useful for example to remote-control i3 (to write test cases for example) or
@ -12,7 +12,7 @@ The method of choice for IPC in our case is a unix socket because it has very
little overhead on both sides and is usually available without headaches in little overhead on both sides and is usually available without headaches in
most languages. In the default configuration file, the ipc-socket gets created most languages. In the default configuration file, the ipc-socket gets created
in +/tmp/i3-%u/ipc-socket.%p+ where +%u+ is your UNIX username and +%p+ is the in +/tmp/i3-%u/ipc-socket.%p+ where +%u+ is your UNIX username and +%p+ is the
PID of i3. PID of i3. You can get the socketpath from i3 by calling +i3 --get-socketpath+.
All i3 utilities, like +i3-msg+ and +i3-input+ will read the +I3_SOCKET_PATH+ All i3 utilities, like +i3-msg+ and +i3-input+ will read the +I3_SOCKET_PATH+
X11 property, stored on the X11 root window. X11 property, stored on the X11 root window.
@ -24,7 +24,8 @@ snippet illustrates this in Perl:
------------------------------------------------------------- -------------------------------------------------------------
use IO::Socket::UNIX; use IO::Socket::UNIX;
my $sock = IO::Socket::UNIX->new(Peer => '/tmp/i3-ipc.sock'); chomp(my $path = qx(i3 --get-socketpath));
my $sock = IO::Socket::UNIX->new(Peer => $path);
------------------------------------------------------------- -------------------------------------------------------------
== Sending messages to i3 == Sending messages to i3
@ -63,6 +64,10 @@ GET_MARKS (5)::
Gets a list of marks (identifiers for containers to easily jump to them Gets a list of marks (identifiers for containers to easily jump to them
later). The reply will be a JSON-encoded list of window marks (see later). The reply will be a JSON-encoded list of window marks (see
reply section). reply section).
GET_BAR_CONFIG (6)::
Gets the configuration (as JSON map) of the workspace bar with the
given ID. If no ID is provided, an array with all configured bar IDs is
returned instead.
So, a typical message could look like this: So, a typical message could look like this:
-------------------------------------------------- --------------------------------------------------
@ -116,6 +121,8 @@ GET_TREE (4)::
Reply to the GET_TREE message. Reply to the GET_TREE message.
GET_MARKS (5):: GET_MARKS (5)::
Reply to the GET_MARKS message. Reply to the GET_MARKS message.
GET_BAR_CONFIG (6)::
Reply to the GET_BAR_CONFIG message.
=== COMMAND reply === COMMAND reply
@ -422,7 +429,7 @@ JSON dump:
} }
] ]
} }
------------------------
=== GET_MARKS reply === GET_MARKS reply
@ -432,8 +439,90 @@ same mark, it will be represented multiple times in the reply (the array
contents are not unique). contents are not unique).
If no window has a mark the response will be the empty array []. If no window has a mark the response will be the empty array [].
------------------------
=== GET_BAR_CONFIG reply
This can be used by third-party workspace bars (especially i3bar, but others
are free to implement compatible alternatives) to get the +bar+ block
configuration from i3.
Depending on the input, the reply is either:
empty input::
An array of configured bar IDs
Bar ID::
A JSON map containing the configuration for the specified bar.
Each bar configuration has the following properties:
id (string)::
The ID for this bar. Included in case you request multiple
configurations and want to differentiate the different replies.
mode (string)::
Either +dock+ (the bar sets the dock window type) or +hide+ (the bar
does not show unless a specific key is pressed).
position (string)::
Either +bottom+ or +top+ at the moment.
status_command (string)::
Command which will be run to generate a statusline. Each line on stdout
of this command will be displayed in the bar. At the moment, no
formatting is supported.
font (string)::
The font to use for text on the bar.
workspace_buttons (boolean)::
Display workspace buttons or not? Defaults to true.
verbose (boolean)::
Should the bar enable verbose output for debugging? Defaults to false.
colors (map)::
Contains key/value pairs of colors. Each value is a color code in hex,
formatted rrggbb (like used in HTML).
The following colors can be configured at the moment:
background::
Background color of the bar.
statusline::
Text color to be used for the statusline.
focused_workspace_text/focused_workspace_bg::
Text color/background color for a workspace button when the workspace
has focus.
active_workspace_text/active_workspace_bg::
Text color/background color for a workspace button when the workspace
is active (visible) on some output, but the focus is on another one.
You can only tell this apart from the focused workspace when you are
using multiple monitors.
inactive_workspace_text/inactive_workspace_bg::
Text color/background color for a workspace button when the workspace
does not have focus and is not active (visible) on any output. This
will be the case for most workspaces.
urgent_workspace_text/urgent_workspace_bar::
Text color/background color for workspaces which contain at least one
window with the urgency hint set.
*Example of configured bars:*
--------------
["bar-bxuqzf"]
--------------
*Example of bar configuration:*
--------------
{
"id": "bar-bxuqzf",
"mode": "dock",
"position": "bottom",
"status_command": "i3status",
"font": "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1",
"workspace_buttons": true,
"verbose": false,
"colors": {
"background": "c0c0c0",
"statusline": "00ff00",
"focused_workspace_text": "ffffff",
"focused_workspace_bg": "000000"
}
}
--------------
== Events == Events

View File

@ -547,7 +547,7 @@ exec_always command
*Examples*: *Examples*:
-------------------------------- --------------------------------
exec i3status | i3bar -d exec chromium
exec_always ~/my_script.sh exec_always ~/my_script.sh
-------------------------------- --------------------------------
@ -756,6 +756,226 @@ workspace_auto_back_and_forth <yes|no>
workspace_auto_back_and_forth yes workspace_auto_back_and_forth yes
--------------------------------- ---------------------------------
== Configuring i3bar
The bar at the bottom of your monitor is drawn by a separate process called
i3bar. Having this part of "the i3 user interface" in a separate process has
several advantages:
1. It is a modular approach. If you dont need a workspace bar at all, or if
you prefer a different one (dzen2, xmobar, maybe even gnome-panel?), you can
just remove the i3bar configuration and start your favorite bar instead.
2. It follows the UNIX philosophy of "Make each program do one thing well".
While i3 manages your windows well, i3bar is good at displaying a bar on
each monitor (unless you configure it otherwise).
3. It leads to two separate, clean codebases. If you want to understand i3, you
dont need to bother with the details of i3bar and vice versa.
That said, i3bar is configured in the same configuration file as i3. This is
because it is tightly coupled with i3 (in contrary to i3lock or i3status which
are useful for people using other window managers). Therefore, it makes no
sense to use a different configuration place when we already have a good
configuration infrastructure in place.
Configuring your workspace bar starts with opening a +bar+ block. You can have
multiple bar blocks to use different settings for different outputs (monitors):
*Example*:
---------------------------
bar {
status_command i3status
}
---------------------------
=== Statusline command
i3bar can run a program and display every line of its +stdout+ output on the
right hand side of the bar. This is useful to display system information like
your current IP address, battery status or date/time.
The specified command will be passed to +sh -c+, so you can use globbing and
have to have correct quoting etc.
*Syntax*:
----------------------
status_command command
----------------------
*Example*:
-------------------------------------------------
status_command i3status --config ~/.i3status.conf
-------------------------------------------------
=== Display mode
You can have i3bar either be visible permanently at one edge of the screen
(+dock+ mode) or make it show up when you press your modifier key (+hide+
mode).
The hide mode maximizes screen space that can be used for actual windows. Also,
i3bar sends the +SIGSTOP+ and +SIGCONT+ signals to the statusline process to
save battery power.
The default is dock mode.
*Syntax*:
----------------
mode <dock|hide>
----------------
*Example*:
----------------
mode hide
----------------
=== Position
This option determines in which edge of the screen i3bar should show up.
The default is bottom.
*Syntax*:
---------------------
position <top|bottom>
---------------------
*Example*:
---------------------
position top
---------------------
=== Output(s)
You can restrict i3bar to one or more outputs (monitors). The default is to
handle all outputs. Restricting the outputs is useful for using different
options for different outputs by using multiple 'bar' blocks.
*Syntax*:
---------------
output <output>
---------------
*Example*:
-------------------------------
# big monitor: everything
bar {
output HDMI2
status_command i3status
}
# laptop monitor: bright colors and i3status with less modules.
bar {
output LVDS1
status_command i3status --config ~/.i3status-small.conf
colors {
background #000000
statusline #ffffff
}
}
-------------------------------
=== Tray output
i3bar by default provides a system tray area where programs such as
NetworkManager, VLC, Pidgin, etc. can place little icons.
You can configure on which output (monitor) the icons should be displayed or
you can turn off the functionality entirely.
*Syntax*:
-------------------------
tray_output <none|output>
-------------------------
*Example*:
-------------------------
# disable system tray
tray_output none
# show tray icons on the big monitor
tray_output HDMI2
-------------------------
=== Font
Specifies the font (again, X core font, not Xft, just like in i3) to be used in
the bar.
*Syntax*:
---------------------
font <font>
---------------------
*Example*:
--------------------------------------------------------------
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
--------------------------------------------------------------
=== Workspace buttons
Specifies whether workspace buttons should be shown or not. This is useful if
you want to display a statusline-only bar containing additional information.
The default is to show workspace buttons.
*Syntax*:
--------------------------
workspace_buttons <yes|no>
--------------------------
*Example*:
--------------------
workspace_buttons no
--------------------
=== Colors
As with i3, colors are in HTML hex format (#rrggbb). The following colors can
be configured at the moment:
background::
Background color of the bar.
statusline::
Text color to be used for the statusline.
focused_workspace_text/focused_workspace_bg::
Text color/background color for a workspace button when the workspace
has focus.
active_workspace_text/active_workspace_bg::
Text color/background color for a workspace button when the workspace
is active (visible) on some output, but the focus is on another one.
You can only tell this apart from the focused workspace when you are
using multiple monitors.
inactive_workspace_text/inactive_workspace_bg::
Text color/background color for a workspace button when the workspace
does not have focus and is not active (visible) on any output. This
will be the case for most workspaces.
urgent_workspace_text/urgent_workspace_bar::
Text color/background color for workspaces which contain at least one
window with the urgency hint set.
*Syntax*:
----------------------------------------
colors {
background <color>
statusline <color>
colorclass <foreground> <background>
}
----------------------------------------
*Example*:
--------------------------------------
colors {
background #000000
statusline #ffffff
focused_workspace #ffffff #285577
active_workspace #888888 #222222
inactive_workspace #888888 #222222
urgent_workspace #ffffff #900000
}
--------------------------------------
== List of commands == List of commands
Commands are what you bind to specific keypresses. You can also issue commands Commands are what you bind to specific keypresses. You can also issue commands

View File

@ -358,6 +358,8 @@ sub convert_command {
# add an i3bar invocation automatically if no 'workspace_bar no' was found # add an i3bar invocation automatically if no 'workspace_bar no' was found
if ($workspace_bar) { if ($workspace_bar) {
print "\n"; print "\n";
print "# XXX: Automatically added a call to i3bar to provide a workspace bar\n"; print "# XXX: Automatically added a bar configuration\n";
print "exec i3status | i3bar -d\n"; print "bar {\n";
print " status_command i3status\n";
print "}\n";
} }

View File

@ -74,9 +74,11 @@ int main(int argc, char *argv[]) {
message_type = I3_IPC_MESSAGE_TYPE_GET_TREE; message_type = I3_IPC_MESSAGE_TYPE_GET_TREE;
else if (strcasecmp(optarg, "get_marks") == 0) else if (strcasecmp(optarg, "get_marks") == 0)
message_type = I3_IPC_MESSAGE_TYPE_GET_MARKS; message_type = I3_IPC_MESSAGE_TYPE_GET_MARKS;
else if (strcasecmp(optarg, "get_bar_config") == 0)
message_type = I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG;
else { else {
printf("Unknown message type\n"); printf("Unknown message type\n");
printf("Known types: command, get_workspaces, get_outputs, get_tree, get_marks\n"); printf("Known types: command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} else if (o == 'q') { } else if (o == 'q') {

View File

@ -45,6 +45,14 @@ static int font_height;
static char *prompt = "Please do not run this program."; static char *prompt = "Please do not run this program.";
static button_t *buttons; static button_t *buttons;
static int buttoncnt; static int buttoncnt;
/* Result of get_colorpixel() for the various colors. */
static uint32_t color_background; /* background of the bar */
static uint32_t color_button_background; /* background for buttons */
static uint32_t color_border; /* color of the button border */
static uint32_t color_border_bottom; /* color of the bottom border */
static uint32_t color_text; /* color of the text */
xcb_window_t root; xcb_window_t root;
/* /*
@ -118,16 +126,14 @@ static void handle_button_release(xcb_connection_t *conn, xcb_button_release_eve
* *
*/ */
static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) { static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
printf("expose!\n");
/* re-draw the background */ /* re-draw the background */
xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#900000")); xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FOREGROUND, color_background);
xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &rect); xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &rect);
/* restore font color */ /* restore font color */
uint32_t values[3]; uint32_t values[3];
values[0] = get_colorpixel(conn, "#FFFFFF"); values[0] = color_text;
values[1] = get_colorpixel(conn, "#900000"); values[1] = color_background;
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND, values); xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND, values);
xcb_image_text_8(conn, strlen(prompt), pixmap, pixmap_gc, 4 + 4/* X */, xcb_image_text_8(conn, strlen(prompt), pixmap, pixmap_gc, 4 + 4/* X */,
font_height + 2 + 4 /* Y = baseline of font */, prompt); font_height + 2 + 4 /* Y = baseline of font */, prompt);
@ -136,14 +142,14 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
int line_width = 4; int line_width = 4;
int w = 20; int w = 20;
int y = rect.width; int y = rect.width;
values[0] = get_colorpixel(conn, "#680a0a"); values[0] = color_button_background;
values[1] = line_width; values[1] = line_width;
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_LINE_WIDTH, values); xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_LINE_WIDTH, values);
xcb_rectangle_t close = { y - w - (2 * line_width), 0, w + (2 * line_width), rect.height }; xcb_rectangle_t close = { y - w - (2 * line_width), 0, w + (2 * line_width), rect.height };
xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &close); xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &close);
xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#d92424")); xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FOREGROUND, color_border);
xcb_point_t points[] = { xcb_point_t points[] = {
{ y - w - (2 * line_width), line_width / 2 }, { y - w - (2 * line_width), line_width / 2 },
{ y - (line_width / 2), line_width / 2 }, { y - (line_width / 2), line_width / 2 },
@ -153,8 +159,8 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
}; };
xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, pixmap, pixmap_gc, 5, points); xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, pixmap, pixmap_gc, 5, points);
values[0] = get_colorpixel(conn, "#ffffff"); values[0] = color_text;
values[1] = get_colorpixel(conn, "#680a0a"); values[1] = color_button_background;
values[2] = 1; values[2] = 1;
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_LINE_WIDTH, values); xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_LINE_WIDTH, values);
xcb_image_text_8(conn, strlen("x"), pixmap, pixmap_gc, y - w - line_width + (w / 2) - 4/* X */, xcb_image_text_8(conn, strlen("x"), pixmap, pixmap_gc, y - w - line_width + (w / 2) - 4/* X */,
@ -167,13 +173,13 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
line_width = 1; line_width = 1;
for (int c = 0; c < buttoncnt; c++) { for (int c = 0; c < buttoncnt; c++) {
/* TODO: make w = text extents of the label */ /* TODO: make w = text extents of the label */
w = 90; w = 100;
y -= 30; y -= 30;
xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#680a0a")); xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FOREGROUND, color_button_background);
close = (xcb_rectangle_t){ y - w - (2 * line_width), 2, w + (2 * line_width), rect.height - 6 }; close = (xcb_rectangle_t){ y - w - (2 * line_width), 2, w + (2 * line_width), rect.height - 6 };
xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &close); xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &close);
xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#d92424")); xcb_change_gc_single(conn, pixmap_gc, XCB_GC_FOREGROUND, color_border);
buttons[c].x = y - w - (2 * line_width); buttons[c].x = y - w - (2 * line_width);
buttons[c].width = w; buttons[c].width = w;
xcb_point_t points2[] = { xcb_point_t points2[] = {
@ -185,8 +191,8 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
}; };
xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, pixmap, pixmap_gc, 5, points2); xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, pixmap, pixmap_gc, 5, points2);
values[0] = get_colorpixel(conn, "#ffffff"); values[0] = color_text;
values[1] = get_colorpixel(conn, "#680a0a"); values[1] = color_button_background;
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND, values); xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND, values);
xcb_image_text_8(conn, strlen(buttons[c].label), pixmap, pixmap_gc, y - w - line_width + 6/* X */, xcb_image_text_8(conn, strlen(buttons[c].label), pixmap, pixmap_gc, y - w - line_width + 6/* X */,
font_height + 2 + 3/* Y = baseline of font */, buttons[c].label); font_height + 2 + 3/* Y = baseline of font */, buttons[c].label);
@ -196,7 +202,7 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
/* border line at the bottom */ /* border line at the bottom */
line_width = 2; line_width = 2;
values[0] = get_colorpixel(conn, "#470909"); values[0] = color_border_bottom;
values[1] = line_width; values[1] = line_width;
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_LINE_WIDTH, values); xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_LINE_WIDTH, values);
xcb_point_t bottom[] = { xcb_point_t bottom[] = {
@ -216,6 +222,7 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
char *pattern = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1"; char *pattern = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1";
int o, option_index = 0; int o, option_index = 0;
enum { TYPE_ERROR = 0, TYPE_WARNING = 1 } bar_type = TYPE_ERROR;
static struct option long_options[] = { static struct option long_options[] = {
{"version", no_argument, 0, 'v'}, {"version", no_argument, 0, 'v'},
@ -223,10 +230,11 @@ int main(int argc, char *argv[]) {
{"button", required_argument, 0, 'b'}, {"button", required_argument, 0, 'b'},
{"help", no_argument, 0, 'h'}, {"help", no_argument, 0, 'h'},
{"message", no_argument, 0, 'm'}, {"message", no_argument, 0, 'm'},
{"type", required_argument, 0, 't'},
{0, 0, 0, 0} {0, 0, 0, 0}
}; };
char *options_string = "b:f:m:vh"; char *options_string = "b:f:m:t:vh";
while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) { while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
switch (o) { switch (o) {
@ -240,6 +248,9 @@ int main(int argc, char *argv[]) {
case 'm': case 'm':
prompt = strdup(optarg); prompt = strdup(optarg);
break; break;
case 't':
bar_type = (strcasecmp(optarg, "warning") == 0 ? TYPE_WARNING : TYPE_ERROR);
break;
case 'h': case 'h':
printf("i3-nagbar " I3_VERSION "\n"); printf("i3-nagbar " I3_VERSION "\n");
printf("i3-nagbar [-m <message>] [-b <button> <action>] [-f <font>] [-v]\n"); printf("i3-nagbar [-m <message>] [-b <button> <action>] [-f <font>] [-v]\n");
@ -274,6 +285,22 @@ int main(int argc, char *argv[]) {
xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screens); xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screens);
root = root_screen->root; root = root_screen->root;
if (bar_type == TYPE_ERROR) {
/* Red theme for error messages */
color_button_background = get_colorpixel(conn, "#680a0a");
color_background = get_colorpixel(conn, "#900000");
color_text = get_colorpixel(conn, "#ffffff");
color_border = get_colorpixel(conn, "#d92424");
color_border_bottom = get_colorpixel(conn, "#470909");
} else {
/* Yellowish theme for warnings */
color_button_background = get_colorpixel(conn, "#ffc100");
color_background = get_colorpixel(conn, "#ffa8000");
color_text = get_colorpixel(conn, "#000000");
color_border = get_colorpixel(conn, "#ab7100");
color_border_bottom = get_colorpixel(conn, "#ab7100");
}
uint32_t font_id = get_font_id(conn, pattern, &font_height); uint32_t font_id = get_font_id(conn, pattern, &font_height);
/* Open an input window */ /* Open an input window */

View File

@ -147,7 +147,9 @@ bindsym Mod1+r mode "resize"
# Start i3bar to display a workspace bar (plus the system information i3status # Start i3bar to display a workspace bar (plus the system information i3status
# finds out, if available) # finds out, if available)
exec i3status | i3bar -d bar {
status_line i3status
}
####################################################################### #######################################################################
# automatically start i3-config-wizard to offer the user to create a # automatically start i3-config-wizard to offer the user to create a

View File

@ -148,4 +148,6 @@ bindcode $mod+27 mode "resize"
# Start i3bar to display a workspace bar (plus the system information i3status # Start i3bar to display a workspace bar (plus the system information i3status
# finds out, if available) # finds out, if available)
exec i3status | i3bar -d bar {
status_line i3status
}

View File

@ -10,8 +10,8 @@ CPPFLAGS += -I$(TOPDIR)/include
all: i3bar doc all: i3bar doc
i3bar: libi3/libi3.a ${FILES} i3bar: $(TOPDIR)/libi3/libi3.a ${FILES}
echo "LINK" echo "[i3bar] LINK"
$(CC) $(LDFLAGS) -o $@ $(filter-out libi3/libi3.a,$^) $(LIBS) $(CC) $(LDFLAGS) -o $@ $(filter-out libi3/libi3.a,$^) $(LIBS)
libi3/%.a: libi3/%.a:
@ -19,15 +19,15 @@ libi3/%.a:
doc: doc:
echo "" echo ""
echo "SUBDIR doc" echo "[i3bar] SUBDIR doc"
$(MAKE) -C doc $(MAKE) -C doc
src/%.o: src/%.c ${HEADERS} src/%.o: src/%.c ${HEADERS}
echo "CC $<" echo "[i3bar] CC $<"
$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
install: all install: all
echo "INSTALL" echo "[i3bar] INSTALL"
$(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
$(INSTALL) -m 0755 i3bar $(DESTDIR)$(PREFIX)/bin $(INSTALL) -m 0755 i3bar $(DESTDIR)$(PREFIX)/bin

View File

@ -1,7 +1,7 @@
i3bar(1) i3bar(1)
======== ========
Axel Wagner <mail+i3bar@merovius.de> Axel Wagner <mail+i3bar@merovius.de>
v0.7, July 2011 v4.1, October 2011
== NAME == NAME
@ -9,81 +9,59 @@ i3bar - xcb-based status- and workspace-bar
== SYNOPSIS == SYNOPSIS
*i3bar* [*-s* 'sock_path'] [*-c* 'command'] [*-m*|*-d*['pos']] [*-f* 'font'] [*-V*] [*-h*] *i3bar* [*-s* 'sock_path'] [*-b* 'bar_id'] [*-v*] [*-h*]
== WARNING
i3bar will automatically be invoked by i3 for every 'bar' configuration block.
Starting it manually is usually not what you want to do.
You have been warned!
== OPTIONS == OPTIONS
*-s, --socket* 'sock_path':: *-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 '/tmp/i3-ipc.sock' Overwrites the path to the i3 IPC socket.
*-c, --command* 'command':: *-b, --bar_id* 'bar_id'::
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* Specifies the bar ID for which to get the configuration from i3.
*-m, --hide*:: *-v, --version*::
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. Display version number and exit.
If *-c* is specified, the childprocess is sent a 'SIGSTOP' on hiding and a 'SIGCONT' on unhiding of the bars.
This is the default behavior of i3bar.
*-d*['pos']*, --dock*[*=*'pos']::
Put i3bar in dockmode. This will reserve some space for it, so it does not overlap other clients.
You can specify either *bottom* (default) or *top* as 'pos'.
*-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*:: *-h, --help*::
Display a short help-message and exit Display a short help-message and exit
== DESCRIPTION == 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. *i3bar* displays a bar at the bottom (or top) of your monitor(s) containing
workspace switching buttons and a statusline generated by i3status(1) or
similar. It is automatically invoked (and configured through) i3.
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
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 its config file. specify 'output_format = none' in the general section of its config file.
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, --color-focus-ws-fg, --color-focus-ws-bg*
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.
== ENVIRONMENT == ENVIRONMENT
=== I3SOCK === I3SOCK
If no ipc-socket is specified on the commandline, this variable is used Used as a fallback for the i3 IPC socket path if neither the commandline
to determine the path, at wich the unix domain socket is expected, on which contains an argument nor the I3_SOCKET_PATH property is set on the X11 root
to connect to i3. window.
== EXAMPLES == EXAMPLES
To get a docked bar with some statusinformation, you use Nothing to see here, move along. As stated above, you should not run i3bar manually.
*i3status | i3bar --dock* Instead, see the i3 documentation, especially the Users Guide.
If you rather have it displayed at the top of the screen, you use
*i3status | i3bar --dock=top*
If you want it to hide when not needed, you should instead simply use
*i3bar -c i3status*
== SEE ALSO == SEE ALSO
+i3(1)+, +i3-wsbar(1)+, +dzen2(1)+, +i3status(1)+ +i3status(1)+ or +conky(1)+ for programs generating a statusline.
+dzen2(1)+ or +xmobar(1)+ for similar programs to i3bar.
== AUTHORS == AUTHORS

View File

@ -34,5 +34,6 @@ struct rect_t {
#include "xcb.h" #include "xcb.h"
#include "ucs2_to_utf8.h" #include "ucs2_to_utf8.h"
#include "config.h" #include "config.h"
#include "libi3.h"
#endif #endif

View File

@ -4,19 +4,37 @@
#include "common.h" #include "common.h"
typedef enum { typedef enum {
DOCKPOS_NONE = 0, POS_NONE = 0,
DOCKPOS_TOP, POS_TOP,
DOCKPOS_BOT POS_BOT
} dockpos_t; } position_t;
typedef struct config_t { typedef struct config_t {
int hide_on_modifier; int hide_on_modifier;
dockpos_t dockpos; position_t position;
int verbose; int verbose;
xcb_colors_t *colors; struct xcb_color_strings_t colors;
int disable_ws; int disable_ws;
char *bar_id;
char *command;
char *fontname;
char *tray_output;
int num_outputs;
char **outputs;
} config_t; } config_t;
config_t config; config_t config;
/**
* Start parsing the received bar configuration json-string
*
*/
void parse_config_json(char *json);
/**
* free()s the color strings as soon as they are not needed anymore.
*
*/
void free_colors(struct xcb_color_strings_t *colors);
#endif #endif

View File

@ -1,527 +0,0 @@
/* $OpenBSD: queue.h,v 1.1 2007/10/26 03:14:08 niallo Exp $ */
/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
/*
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)queue.h 8.5 (Berkeley) 8/20/94
*/
#ifndef _SYS_QUEUE_H_
#define _SYS_QUEUE_H_
/*
* This file defines five types of data structures: singly-linked lists,
* lists, simple queues, tail queues, and circular queues.
*
*
* A singly-linked list is headed by a single forward pointer. The elements
* are singly linked for minimum space and pointer manipulation overhead at
* the expense of O(n) removal for arbitrary elements. New elements can be
* added to the list after an existing element or at the head of the list.
* Elements being removed from the head of the list should use the explicit
* macro for this purpose for optimum efficiency. A singly-linked list may
* only be traversed in the forward direction. Singly-linked lists are ideal
* for applications with large datasets and few or no removals or for
* implementing a LIFO queue.
*
* A list is headed by a single forward pointer (or an array of forward
* pointers for a hash table header). The elements are doubly linked
* so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before
* or after an existing element or at the head of the list. A list
* may only be traversed in the forward direction.
*
* A simple queue is headed by a pair of pointers, one the head of the
* list and the other to the tail of the list. The elements are singly
* linked to save space, so elements can only be removed from the
* head of the list. New elements can be added to the list before or after
* an existing element, at the head of the list, or at the end of the
* list. A simple queue may only be traversed in the forward direction.
*
* A tail queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or
* after an existing element, at the head of the list, or at the end of
* the list. A tail queue may be traversed in either direction.
*
* A circle queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or after
* an existing element, at the head of the list, or at the end of the list.
* A circle queue may be traversed in either direction, but has a more
* complex end of list detection.
*
* For details on the use of these macros, see the queue(3) manual page.
*/
#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
#define _Q_INVALIDATE(a) (a) = ((void *)-1)
#else
#define _Q_INVALIDATE(a)
#endif
/*
* Singly-linked List definitions.
*/
#define SLIST_HEAD(name, type) \
struct name { \
struct type *slh_first; /* first element */ \
}
#define SLIST_HEAD_INITIALIZER(head) \
{ NULL }
#define SLIST_ENTRY(type) \
struct { \
struct type *sle_next; /* next element */ \
}
/*
* Singly-linked List access methods.
*/
#define SLIST_FIRST(head) ((head)->slh_first)
#define SLIST_END(head) NULL
#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
#define SLIST_FOREACH(var, head, field) \
for((var) = SLIST_FIRST(head); \
(var) != SLIST_END(head); \
(var) = SLIST_NEXT(var, field))
#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
for ((varp) = &SLIST_FIRST((head)); \
((var) = *(varp)) != SLIST_END(head); \
(varp) = &SLIST_NEXT((var), field))
/*
* Singly-linked List functions.
*/
#define SLIST_INIT(head) { \
SLIST_FIRST(head) = SLIST_END(head); \
}
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
(elm)->field.sle_next = (slistelm)->field.sle_next; \
(slistelm)->field.sle_next = (elm); \
} while (0)
#define SLIST_INSERT_HEAD(head, elm, field) do { \
(elm)->field.sle_next = (head)->slh_first; \
(head)->slh_first = (elm); \
} while (0)
#define SLIST_REMOVE_NEXT(head, elm, field) do { \
(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
} while (0)
#define SLIST_REMOVE_HEAD(head, field) do { \
(head)->slh_first = (head)->slh_first->field.sle_next; \
} while (0)
#define SLIST_REMOVE(head, elm, type, field) do { \
if ((head)->slh_first == (elm)) { \
SLIST_REMOVE_HEAD((head), field); \
} else { \
struct type *curelm = (head)->slh_first; \
\
while (curelm->field.sle_next != (elm)) \
curelm = curelm->field.sle_next; \
curelm->field.sle_next = \
curelm->field.sle_next->field.sle_next; \
_Q_INVALIDATE((elm)->field.sle_next); \
} \
} while (0)
/*
* List definitions.
*/
#define LIST_HEAD(name, type) \
struct name { \
struct type *lh_first; /* first element */ \
}
#define LIST_HEAD_INITIALIZER(head) \
{ NULL }
#define LIST_ENTRY(type) \
struct { \
struct type *le_next; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
}
/*
* List access methods
*/
#define LIST_FIRST(head) ((head)->lh_first)
#define LIST_END(head) NULL
#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
#define LIST_FOREACH(var, head, field) \
for((var) = LIST_FIRST(head); \
(var)!= LIST_END(head); \
(var) = LIST_NEXT(var, field))
/*
* List functions.
*/
#define LIST_INIT(head) do { \
LIST_FIRST(head) = LIST_END(head); \
} while (0)
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
(listelm)->field.le_next->field.le_prev = \
&(elm)->field.le_next; \
(listelm)->field.le_next = (elm); \
(elm)->field.le_prev = &(listelm)->field.le_next; \
} while (0)
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.le_prev = (listelm)->field.le_prev; \
(elm)->field.le_next = (listelm); \
*(listelm)->field.le_prev = (elm); \
(listelm)->field.le_prev = &(elm)->field.le_next; \
} while (0)
#define LIST_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
(head)->lh_first = (elm); \
(elm)->field.le_prev = &(head)->lh_first; \
} while (0)
#define LIST_REMOVE(elm, field) do { \
if ((elm)->field.le_next != NULL) \
(elm)->field.le_next->field.le_prev = \
(elm)->field.le_prev; \
*(elm)->field.le_prev = (elm)->field.le_next; \
_Q_INVALIDATE((elm)->field.le_prev); \
_Q_INVALIDATE((elm)->field.le_next); \
} while (0)
#define LIST_REPLACE(elm, elm2, field) do { \
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
(elm2)->field.le_next->field.le_prev = \
&(elm2)->field.le_next; \
(elm2)->field.le_prev = (elm)->field.le_prev; \
*(elm2)->field.le_prev = (elm2); \
_Q_INVALIDATE((elm)->field.le_prev); \
_Q_INVALIDATE((elm)->field.le_next); \
} while (0)
/*
* Simple queue definitions.
*/
#define SIMPLEQ_HEAD(name, type) \
struct name { \
struct type *sqh_first; /* first element */ \
struct type **sqh_last; /* addr of last next element */ \
}
#define SIMPLEQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).sqh_first }
#define SIMPLEQ_ENTRY(type) \
struct { \
struct type *sqe_next; /* next element */ \
}
/*
* Simple queue access methods.
*/
#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
#define SIMPLEQ_END(head) NULL
#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
#define SIMPLEQ_FOREACH(var, head, field) \
for((var) = SIMPLEQ_FIRST(head); \
(var) != SIMPLEQ_END(head); \
(var) = SIMPLEQ_NEXT(var, field))
/*
* Simple queue functions.
*/
#define SIMPLEQ_INIT(head) do { \
(head)->sqh_first = NULL; \
(head)->sqh_last = &(head)->sqh_first; \
} while (0)
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
(head)->sqh_last = &(elm)->field.sqe_next; \
(head)->sqh_first = (elm); \
} while (0)
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.sqe_next = NULL; \
*(head)->sqh_last = (elm); \
(head)->sqh_last = &(elm)->field.sqe_next; \
} while (0)
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
(head)->sqh_last = &(elm)->field.sqe_next; \
(listelm)->field.sqe_next = (elm); \
} while (0)
#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
(head)->sqh_last = &(head)->sqh_first; \
} while (0)
/*
* Tail queue definitions.
*/
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element */ \
struct type **tqh_last; /* addr of last next element */ \
}
#define TAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).tqh_first }
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
/*
* tail queue access methods
*/
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_END(head) NULL
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->tqh_last))->tqh_last))
/* XXX */
#define TAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define TAILQ_EMPTY(head) \
(TAILQ_FIRST(head) == TAILQ_END(head))
#define TAILQ_FOREACH(var, head, field) \
for((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head); \
(var) = TAILQ_NEXT(var, field))
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
for((var) = TAILQ_LAST(head, headname); \
(var) != TAILQ_END(head); \
(var) = TAILQ_PREV(var, headname, field))
/*
* Tail queue functions.
*/
#define TAILQ_INIT(head) do { \
(head)->tqh_first = NULL; \
(head)->tqh_last = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
(head)->tqh_first->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(head)->tqh_first = (elm); \
(elm)->field.tqe_prev = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.tqe_next = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
(elm)->field.tqe_next->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(listelm)->field.tqe_next = (elm); \
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
(elm)->field.tqe_next = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_REMOVE(head, elm, field) do { \
if (((elm)->field.tqe_next) != NULL) \
(elm)->field.tqe_next->field.tqe_prev = \
(elm)->field.tqe_prev; \
else \
(head)->tqh_last = (elm)->field.tqe_prev; \
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
_Q_INVALIDATE((elm)->field.tqe_prev); \
_Q_INVALIDATE((elm)->field.tqe_next); \
} while (0)
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
(elm2)->field.tqe_next->field.tqe_prev = \
&(elm2)->field.tqe_next; \
else \
(head)->tqh_last = &(elm2)->field.tqe_next; \
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
*(elm2)->field.tqe_prev = (elm2); \
_Q_INVALIDATE((elm)->field.tqe_prev); \
_Q_INVALIDATE((elm)->field.tqe_next); \
} while (0)
/*
* Circular queue definitions.
*/
#define CIRCLEQ_HEAD(name, type) \
struct name { \
struct type *cqh_first; /* first element */ \
struct type *cqh_last; /* last element */ \
}
#define CIRCLEQ_HEAD_INITIALIZER(head) \
{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
#define CIRCLEQ_ENTRY(type) \
struct { \
struct type *cqe_next; /* next element */ \
struct type *cqe_prev; /* previous element */ \
}
/*
* Circular queue access methods
*/
#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
#define CIRCLEQ_LAST(head) ((head)->cqh_last)
#define CIRCLEQ_END(head) ((void *)(head))
#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
#define CIRCLEQ_EMPTY(head) \
(CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
#define CIRCLEQ_FOREACH(var, head, field) \
for((var) = CIRCLEQ_FIRST(head); \
(var) != CIRCLEQ_END(head); \
(var) = CIRCLEQ_NEXT(var, field))
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
for((var) = CIRCLEQ_LAST(head); \
(var) != CIRCLEQ_END(head); \
(var) = CIRCLEQ_PREV(var, field))
/*
* Circular queue functions.
*/
#define CIRCLEQ_INIT(head) do { \
(head)->cqh_first = CIRCLEQ_END(head); \
(head)->cqh_last = CIRCLEQ_END(head); \
} while (0)
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
(elm)->field.cqe_prev = (listelm); \
if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm); \
else \
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
(listelm)->field.cqe_next = (elm); \
} while (0)
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
(elm)->field.cqe_next = (listelm); \
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm); \
else \
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
(listelm)->field.cqe_prev = (elm); \
} while (0)
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
(elm)->field.cqe_next = (head)->cqh_first; \
(elm)->field.cqe_prev = CIRCLEQ_END(head); \
if ((head)->cqh_last == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm); \
else \
(head)->cqh_first->field.cqe_prev = (elm); \
(head)->cqh_first = (elm); \
} while (0)
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.cqe_next = CIRCLEQ_END(head); \
(elm)->field.cqe_prev = (head)->cqh_last; \
if ((head)->cqh_first == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm); \
else \
(head)->cqh_last->field.cqe_next = (elm); \
(head)->cqh_last = (elm); \
} while (0)
#define CIRCLEQ_REMOVE(head, elm, field) do { \
if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm)->field.cqe_prev; \
else \
(elm)->field.cqe_next->field.cqe_prev = \
(elm)->field.cqe_prev; \
if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm)->field.cqe_next; \
else \
(elm)->field.cqe_prev->field.cqe_next = \
(elm)->field.cqe_next; \
_Q_INVALIDATE((elm)->field.cqe_prev); \
_Q_INVALIDATE((elm)->field.cqe_next); \
} while (0)
#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
CIRCLEQ_END(head)) \
(head)->cqh_last = (elm2); \
else \
(elm2)->field.cqe_next->field.cqe_prev = (elm2); \
if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
CIRCLEQ_END(head)) \
(head)->cqh_first = (elm2); \
else \
(elm2)->field.cqe_prev->field.cqe_next = (elm2); \
_Q_INVALIDATE((elm)->field.cqe_prev); \
_Q_INVALIDATE((elm)->field.cqe_next); \
} while (0)
#endif /* !_SYS_QUEUE_H_ */

View File

@ -40,10 +40,18 @@ struct xcb_color_strings_t {
typedef struct xcb_colors_t xcb_colors_t; typedef struct xcb_colors_t xcb_colors_t;
/* /*
* Initialize xcb and use the specified fontname for text-rendering * Early initialization of the connection to X11: Everything which does not
* depend on 'config'.
* *
*/ */
char *init_xcb(char *fontname); char *init_xcb_early();
/**
* Initialization which depends on 'config' being usable. Called after the
* configuration has arrived.
*
*/
void init_xcb_late(char *fontname);
/* /*
* Initialize the colors * Initialize the colors

View File

@ -62,7 +62,7 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
int n = 0; int n = 0;
int rec = 0; int rec = 0;
int buffer_len = STDIN_CHUNK_SIZE; int buffer_len = STDIN_CHUNK_SIZE;
char *buffer = malloc(buffer_len); char *buffer = smalloc(buffer_len);
buffer[0] = '\0'; buffer[0] = '\0';
while(1) { while(1) {
n = read(fd, buffer + rec, buffer_len - rec); n = read(fd, buffer + rec, buffer_len - rec);
@ -91,7 +91,7 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
if (rec == buffer_len) { if (rec == buffer_len) {
buffer_len += STDIN_CHUNK_SIZE; buffer_len += STDIN_CHUNK_SIZE;
buffer = realloc(buffer, buffer_len); buffer = srealloc(buffer, buffer_len);
} }
} }
if (*buffer == '\0') { if (*buffer == '\0') {
@ -169,12 +169,12 @@ void start_child(char *command) {
/* We set O_NONBLOCK because blocking is evil in event-driven software */ /* 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 = smalloc(sizeof(ev_io));
ev_io_init(stdin_io, &stdin_io_cb, STDIN_FILENO, EV_READ); ev_io_init(stdin_io, &stdin_io_cb, STDIN_FILENO, EV_READ);
ev_io_start(main_loop, stdin_io); ev_io_start(main_loop, stdin_io);
/* We must cleanup, if the child unexpectedly terminates */ /* We must cleanup, if the child unexpectedly terminates */
child_sig = malloc(sizeof(ev_child)); child_sig = smalloc(sizeof(ev_child));
ev_child_init(child_sig, &child_sig_cb, child_pid, 0); ev_child_init(child_sig, &child_sig_cb, child_pid, 0);
ev_child_start(main_loop, child_sig); ev_child_start(main_loop, child_sig);

234
i3bar/src/config.c Normal file
View File

@ -0,0 +1,234 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3bar - an xcb-based status- and ws-bar for i3
*
* © 2010-2011 Axel Wagner and contributors
*
* See file LICENSE for license information
*
* src/outputs.c: Maintaining the output-list
*
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <i3/ipc.h>
#include <yajl/yajl_parse.h>
#include <yajl/yajl_version.h>
#include "common.h"
static char *cur_key;
/*
* Parse a key.
*
* Essentially we just save it in cur_key.
*
*/
#if YAJL_MAJOR >= 2
static int config_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
#else
static int config_map_key_cb(void *params_, const unsigned char *keyVal, unsigned keyLen) {
#endif
FREE(cur_key);
cur_key = smalloc(sizeof(unsigned char) * (keyLen + 1));
strncpy(cur_key, (const char*) keyVal, keyLen);
cur_key[keyLen] = '\0';
return 1;
}
/*
* Parse a null-value (current_workspace)
*
*/
static int config_null_cb(void *params_) {
if (!strcmp(cur_key, "id")) {
/* If 'id' is NULL, the bar config was not found. Error out. */
ELOG("No such bar config. Use 'i3-msg -t get_bar_config' to get the available configs.\n");
ELOG("Are you starting i3bar by hand? You should not:\n");
ELOG("Configure a 'bar' block in your i3 config and i3 will launch i3bar automatically.\n");
exit(EXIT_FAILURE);
}
return 1;
}
/*
* Parse a string
*
*/
#if YAJL_MAJOR >= 2
static int config_string_cb(void *params_, const unsigned char *val, size_t len) {
#else
static int config_string_cb(void *params_, const unsigned char *val, unsigned int len) {
#endif
/* The id is ignored, we already have it in config.bar_id */
if (!strcmp(cur_key, "id"))
return 1;
if (!strcmp(cur_key, "mode")) {
DLOG("mode = %.*s, len = %d\n", len, val, len);
config.hide_on_modifier = (len == 4 && !strncmp((const char*)val, "hide", strlen("hide")));
return 1;
}
if (!strcmp(cur_key, "position")) {
DLOG("position = %.*s\n", len, val);
config.position = (len == 3 && !strncmp((const char*)val, "top", strlen("top")) ? POS_TOP : POS_BOT);
return 1;
}
if (!strcmp(cur_key, "status_command")) {
/* We cannot directly start the child here, because start_child() also
* needs to be run when no command was specified (to setup stdin).
* Therefore we save the command in 'config' and access it later in
* got_bar_config() */
DLOG("command = %.*s\n", len, val);
asprintf(&config.command, "%.*s", len, val);
return 1;
}
if (!strcmp(cur_key, "font")) {
DLOG("font = %.*s\n", len, val);
asprintf(&config.fontname, "%.*s", len, val);
return 1;
}
if (!strcmp(cur_key, "outputs")) {
DLOG("+output %.*s\n", len, val);
int new_num_outputs = config.num_outputs + 1;
config.outputs = srealloc(config.outputs, sizeof(char*) * new_num_outputs);
asprintf(&config.outputs[config.num_outputs], "%.*s", len, val);
config.num_outputs = new_num_outputs;
return 1;
}
if (!strcmp(cur_key, "tray_output")) {
DLOG("tray_output %.*s\n", len, val);
FREE(config.tray_output);
asprintf(&config.tray_output, "%.*s", len, val);
return 1;
}
#define COLOR(json_name, struct_name) \
do { \
if (!strcmp(cur_key, #json_name)) { \
DLOG(#json_name " = " #struct_name " = %.*s\n", len, val); \
asprintf(&(config.colors.struct_name), "%.*s", len, val); \
return 1; \
} \
} while (0)
COLOR(statusline, bar_fg);
COLOR(background, bar_bg);
COLOR(focused_workspace_text, focus_ws_fg);
COLOR(focused_workspace_bg, focus_ws_bg);
COLOR(active_workspace_text, active_ws_fg);
COLOR(active_workspace_bg, active_ws_bg);
COLOR(inactive_workspace_text, inactive_ws_fg);
COLOR(inactive_workspace_bg, inactive_ws_bg);
COLOR(urgent_workspace_text, urgent_ws_fg);
COLOR(urgent_workspace_bg, urgent_ws_bg);
printf("got unexpected string %.*s for cur_key = %s\n", len, val, cur_key);
return 0;
}
/*
* Parse a boolean value
*
*/
static int config_boolean_cb(void *params_, int val) {
if (!strcmp(cur_key, "workspace_buttons")) {
DLOG("workspace_buttons = %d\n", val);
config.disable_ws = !val;
return 1;
}
if (!strcmp(cur_key, "verbose")) {
DLOG("verbose = %d\n", val);
config.verbose = val;
return 1;
}
return 0;
}
/* A datastructure to pass all these callbacks to yajl */
static yajl_callbacks outputs_callbacks = {
&config_null_cb,
&config_boolean_cb,
NULL,
NULL,
NULL,
&config_string_cb,
NULL,
&config_map_key_cb,
NULL,
NULL,
NULL
};
/*
* Start parsing the received bar configuration json-string
*
*/
void parse_config_json(char *json) {
yajl_handle handle;
yajl_status state;
#if YAJL_MAJOR < 2
yajl_parser_config parse_conf = { 0, 0 };
handle = yajl_alloc(&outputs_callbacks, &parse_conf, NULL, NULL);
#else
handle = yajl_alloc(&outputs_callbacks, NULL, NULL);
#endif
state = yajl_parse(handle, (const unsigned char*) json, strlen(json));
/* FIXME: Proper errorhandling for JSON-parsing */
switch (state) {
case yajl_status_ok:
break;
case yajl_status_client_canceled:
#if YAJL_MAJOR < 2
case yajl_status_insufficient_data:
#endif
case yajl_status_error:
ELOG("Could not parse config-reply!\n");
exit(EXIT_FAILURE);
break;
}
yajl_free(handle);
}
/*
* free()s the color strings as soon as they are not needed anymore.
*
*/
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);
FREE_COLOR(focus_ws_fg);
FREE_COLOR(focus_ws_bg);
#undef FREE_COLOR
}

View File

@ -24,55 +24,11 @@
#include "common.h" #include "common.h"
ev_io *i3_connection; ev_io *i3_connection;
ev_timer *reconn = NULL;
const char *sock_path; const char *sock_path;
typedef void(*handler_t)(char*); typedef void(*handler_t)(char*);
/*
* Retry to connect.
*
*/
void retry_connection(struct ev_loop *loop, ev_timer *w, int events) {
static int retries = 8;
if (init_connection(sock_path) == 0) {
if (retries == 0) {
ELOG("Retried 8 times - connection failed!\n");
exit(EXIT_FAILURE);
}
retries--;
return;
}
retries = 8;
ev_timer_stop(loop, w);
subscribe_events();
/* We get the current outputs and workspaces, to
* reconfigure all bars with the current configuration */
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
if (!config.disable_ws) {
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
}
}
/*
* Schedule a reconnect
*
*/
void reconnect() {
if (reconn == NULL) {
if ((reconn = malloc(sizeof(ev_timer))) == NULL) {
ELOG("malloc() failed: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
} else {
ev_timer_stop(main_loop, reconn);
}
ev_timer_init(reconn, retry_connection, 0.25, 0.25);
ev_timer_start(main_loop, reconn);
}
/* /*
* Called, when we get a reply to a command from i3. * Called, when we get a reply to a command from i3.
* Since i3 does not give us much feedback on commands, we do not much * Since i3 does not give us much feedback on commands, we do not much
@ -114,12 +70,47 @@ void got_output_reply(char *reply) {
reconfig_windows(); reconfig_windows();
} }
/*
* Called when we get the configuration for our bar instance
*
*/
void got_bar_config(char *reply) {
DLOG("Received bar config \"%s\"\n", reply);
/* 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-drivenness of the code */
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
parse_config_json(reply);
/* Now we can actually use 'config', so let's subscribe to the appropriate
* events and request the workspaces if necessary. */
subscribe_events();
if (!config.disable_ws)
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
/* Initialize the rest of XCB */
init_xcb_late(config.fontname);
/* Resolve color strings to colorpixels and save them, then free the strings. */
init_colors(&(config.colors));
free_colors(&(config.colors));
/* The name of this function is actually misleading. Even if no command is
* specified, this function initiates the watchers to listen on stdin and
* react accordingly */
start_child(config.command);
FREE(config.command);
}
/* Data-structure to easily call the reply-handlers later */ /* Data-structure to easily call the reply-handlers later */
handler_t reply_handlers[] = { handler_t reply_handlers[] = {
&got_command_reply, &got_command_reply,
&got_workspace_reply, &got_workspace_reply,
&got_subscribe_reply, &got_subscribe_reply,
&got_output_reply, &got_output_reply,
NULL,
NULL,
&got_bar_config,
}; };
/* /*
@ -159,11 +150,7 @@ void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
/* First we only read the header, because we know its length */ /* First we only read the header, because we know its 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 = smalloc(header_len);
if (header == NULL) {
ELOG("Could not allocate memory: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
/* We first parse the fixed-length IPC-header, to know, how much data /* We first parse the fixed-length IPC-header, to know, how much data
* we have to expect */ * we have to expect */
@ -175,12 +162,10 @@ void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (n == 0) { if (n == 0) {
/* EOF received. We try to recover a few times, because most likely /* EOF received. Since i3 will restart i3bar instances as appropriate,
* i3 just restarted */ * we exit here. */
ELOG("EOF received, try to recover...\n"); DLOG("EOF received, exiting...\n");
destroy_connection(); exit(EXIT_SUCCESS);
reconnect();
return;
} }
rec += n; rec += n;
} }
@ -202,15 +187,7 @@ void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
/* Now that we know, what to expect, we can start read()ing the rest /* Now that we know, what to expect, we can start read()ing the rest
* of the message */ * of the message */
char *buffer = malloc(size + 1); char *buffer = smalloc(size + 1);
if (buffer == NULL) {
/* EOF received. We try to recover a few times, because most likely
* i3 just restarted */
ELOG("EOF received, try to recover...\n");
destroy_connection();
reconnect();
return;
}
rec = 0; rec = 0;
while (rec < size) { while (rec < size) {
@ -232,7 +209,8 @@ void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
type ^= 1 << 31; type ^= 1 << 31;
event_handlers[type](buffer); event_handlers[type](buffer);
} else { } else {
reply_handlers[type](buffer); if (reply_handlers[type])
reply_handlers[type](buffer);
} }
FREE(header); FREE(header);
@ -255,12 +233,7 @@ int i3_send_msg(uint32_t type, const char *payload) {
/* TODO: I'm not entirely sure if this buffer really has to contain more /* 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?), * than the pure header (why not just write() the payload from *payload?),
* but we leave it for now */ * but we leave it for now */
char *buffer = malloc(to_write); char *buffer = smalloc(to_write);
if (buffer == NULL) {
ELOG("Could not allocate memory: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
char *walk = buffer; char *walk = buffer;
strncpy(buffer, I3_IPC_MAGIC, strlen(I3_IPC_MAGIC)); strncpy(buffer, I3_IPC_MAGIC, strlen(I3_IPC_MAGIC));
@ -310,15 +283,10 @@ int init_connection(const char *socket_path) {
strcpy(addr.sun_path, sock_path); strcpy(addr.sun_path, sock_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) {
ELOG("Could not connect to i3! %s: %s\n", sock_path, strerror(errno)); ELOG("Could not connect to i3! %s: %s\n", sock_path, strerror(errno));
reconnect();
return 0;
}
i3_connection = malloc(sizeof(ev_io));
if (i3_connection == NULL) {
ELOG("malloc() failed: %s\n", strerror(errno));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
i3_connection = smalloc(sizeof(ev_io));
ev_io_init(i3_connection, &got_data, sockfd, EV_READ); ev_io_init(i3_connection, &got_data, sockfd, EV_READ);
ev_io_start(main_loop, i3_connection); ev_io_start(main_loop, i3_connection);
return 1; return 1;

View File

@ -30,70 +30,23 @@ char *expand_path(char *path) {
ELOG("glob() failed\n"); ELOG("glob() failed\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
char *result = strdup(globbuf.gl_pathc > 0 ? globbuf.gl_pathv[0] : path); char *result = sstrdup(globbuf.gl_pathc > 0 ? globbuf.gl_pathv[0] : path);
if (result == NULL) {
ELOG("malloc() failed: %s\n", strerror(errno));
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);
FREE_COLOR(focus_ws_fg);
FREE_COLOR(focus_ws_bg);
#undef FREE_COLOR
}
void print_usage(char *elf_name) { void print_usage(char *elf_name) {
printf("Usage: %s [-s sock_path] [-c command] [-m|-d[pos]] [-f font] [-V] [-h]\n", elf_name); printf("Usage: %s [-b bar_id] [-s sock_path] [-h] [-v]\n", elf_name);
printf("\n");
printf("-b <bar_id>\tBar ID for which to get the configuration\n");
printf("-s <sock_path>\tConnect to i3 via <sock_path>\n"); 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("-d[<pos>]\tEnable dockmode. <pos> is \"top\" or \"bottom\". Default is bottom\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("-w\t\tDisable workspace-buttons\n");
printf("-V\t\tBe (very) verbose with the debug-output\n");
printf("-h\t\tDisplay this help-message and exit\n"); printf("-h\t\tDisplay this help-message and exit\n");
printf("-v\t\tDisplay version number and exit\n");
printf("\n");
printf(" PLEASE NOTE that i3bar will be automatically started by i3\n"
" as soon as there is a 'bar' configuration block in your\n"
" config file. You should never need to start it manually.\n");
printf("\n");
} }
/* /*
@ -120,107 +73,33 @@ int main(int argc, char **argv) {
int opt; int opt;
int option_index = 0; int option_index = 0;
char *socket_path = getenv("I3SOCK"); char *socket_path = getenv("I3SOCK");
char *command = NULL;
char *fontname = NULL;
char *i3_default_sock_path = "/tmp/i3-ipc.sock"; char *i3_default_sock_path = "/tmp/i3-ipc.sock";
struct xcb_color_strings_t colors = { NULL, };
/* Definition of the standard-config */ /* Initialize the standard config to use 0 as default */
config.hide_on_modifier = 0; memset(&config, '\0', sizeof(config_t));
config.dockpos = DOCKPOS_NONE;
config.disable_ws = 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' }, { "bar_id", required_argument, 0, 0 },
{ "hide", no_argument, 0, 'm' },
{ "dock", optional_argument, 0, 'd' },
{ "font", required_argument, 0, 'f' },
{ "nows", no_argument, 0, 'w' },
{ "help", no_argument, 0, 'h' }, { "help", no_argument, 0, 'h' },
{ "version", no_argument, 0, 'v' }, { "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' },
{ "color-focus-ws-bg", required_argument, 0, 'I' },
{ "color-focus-ws-fg", required_argument, 0, 'J' },
{ NULL, 0, 0, 0} { NULL, 0, 0, 0}
}; };
while ((opt = getopt_long(argc, argv, "s:c:d::mf:whvVA:B:C:D:E:F:G:H:I:J:", long_opt, &option_index)) != -1) { while ((opt = getopt_long(argc, argv, "s:hv", long_opt, &option_index)) != -1) {
switch (opt) { switch (opt) {
case 's': case 's':
socket_path = expand_path(optarg); socket_path = expand_path(optarg);
break; break;
case 'c':
command = strdup(optarg);
break;
case 'm':
config.hide_on_modifier = 1;
break;
case 'd':
config.hide_on_modifier = 0;
if (optarg == NULL) {
config.dockpos = DOCKPOS_BOT;
break;
}
if (!strcmp(optarg, "top")) {
config.dockpos = DOCKPOS_TOP;
} else if (!strcmp(optarg, "bottom")) {
config.dockpos = DOCKPOS_BOT;
} else {
print_usage(argv[0]);
exit(EXIT_FAILURE);
}
break;
case 'f':
fontname = strdup(optarg);
break;
case 'w':
config.disable_ws = 1;
break;
case 'v': case 'v':
printf("i3bar version " I3_VERSION " © 2010-2011 Axel Wagner and contributors\n"); printf("i3bar version " I3_VERSION " © 2010-2011 Axel Wagner and contributors\n");
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
break; break;
case 'V': case 0:
config.verbose = 1; if (!strcmp(long_opt[option_index].name, "bar_id")) {
break; FREE(config.bar_id);
case 'A': config.bar_id = sstrdup(optarg);
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;
case 'I':
read_color(&colors.focus_ws_bg);
break;
case 'J':
read_color(&colors.focus_ws_fg);
break; break;
default: default:
print_usage(argv[0]); print_usage(argv[0]);
@ -229,26 +108,16 @@ int main(int argc, char **argv) {
} }
} }
if (fontname == NULL) { if (!config.bar_id) {
/* This is a very restrictive default. More sensefull would be something like /* TODO: maybe we want -f which will automatically ask i3 for the first
* "-misc-*-*-*-*--*-*-*-*-*-*-*-*". But since that produces very ugly results * configured bar (and error out if there are too many)? */
* on my machine, let's stick with this until we have a configfile */ ELOG("No bar_id passed. Please let i3 start i3bar or specify --bar_id\n");
fontname = "-misc-fixed-medium-r-semicondensed--12-110-75-75-c-60-iso10646-1"; exit(EXIT_FAILURE);
}
if (config.dockpos != DOCKPOS_NONE) {
if (config.hide_on_modifier) {
ELOG("--dock and --hide are mutually exclusive!\n");
exit(EXIT_FAILURE);
}
} else {
config.hide_on_modifier = 1;
} }
main_loop = ev_default_loop(0); main_loop = ev_default_loop(0);
init_colors(&colors); char *atom_sock_path = init_xcb_early();
char *atom_sock_path = init_xcb(fontname);
if (socket_path == NULL) { if (socket_path == NULL) {
socket_path = atom_sock_path; socket_path = atom_sock_path;
@ -259,38 +128,18 @@ int main(int argc, char **argv) {
socket_path = expand_path(i3_default_sock_path); socket_path = expand_path(i3_default_sock_path);
} }
free_colors(&colors);
init_outputs(); init_outputs();
if (init_connection(socket_path)) { if (init_connection(socket_path)) {
/* We subscribe to the i3-events we need */ /* Request the bar configuration. When it arrives, we fill the config array. */
subscribe_events(); i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG, config.bar_id);
/* 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);
if (!config.disable_ws) {
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);
FREE(command);
/* We listen to SIGTERM/QUIT/INT and try to exit cleanly, by stopping the main-loop. /* 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 * We only need those watchers on the stack, so putting them on the stack saves us
* some calls to free() */ * some calls to free() */
ev_signal *sig_term = malloc(sizeof(ev_signal)); ev_signal *sig_term = smalloc(sizeof(ev_signal));
ev_signal *sig_int = malloc(sizeof(ev_signal)); ev_signal *sig_int = smalloc(sizeof(ev_signal));
ev_signal *sig_hup = malloc(sizeof(ev_signal)); ev_signal *sig_hup = smalloc(sizeof(ev_signal));
if (sig_term == NULL || sig_int == NULL || sig_hup == NULL) {
ELOG("malloc() failed: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
ev_signal_init(sig_term, &sig_cb, SIGTERM); ev_signal_init(sig_term, &sig_cb, SIGTERM);
ev_signal_init(sig_int, &sig_cb, SIGINT); ev_signal_init(sig_int, &sig_cb, SIGINT);

View File

@ -26,7 +26,7 @@ struct outputs_json_params {
i3_output *outputs_walk; i3_output *outputs_walk;
char *cur_key; char *cur_key;
char *json; char *json;
bool init; bool in_rect;
}; };
/* /*
@ -115,7 +115,7 @@ static int outputs_string_cb(void *params_, const unsigned char *val, unsigned i
struct outputs_json_params *params = (struct outputs_json_params*) params_; struct outputs_json_params *params = (struct outputs_json_params*) params_;
if (!strcmp(params->cur_key, "current_workspace")) { if (!strcmp(params->cur_key, "current_workspace")) {
char *copy = malloc(sizeof(const unsigned char) * (len + 1)); char *copy = smalloc(sizeof(const unsigned char) * (len + 1));
strncpy(copy, (const char*) val, len); strncpy(copy, (const char*) val, len);
copy[len] = '\0'; copy[len] = '\0';
@ -134,7 +134,7 @@ static int outputs_string_cb(void *params_, const unsigned char *val, unsigned i
return 0; return 0;
} }
char *name = malloc(sizeof(const unsigned char) * (len + 1)); char *name = smalloc(sizeof(const unsigned char) * (len + 1));
strncpy(name, (const char*) val, len); strncpy(name, (const char*) val, len);
name[len] = '\0'; name[len] = '\0';
@ -154,16 +154,16 @@ static int outputs_start_map_cb(void *params_) {
i3_output *new_output = NULL; i3_output *new_output = NULL;
if (params->cur_key == NULL) { if (params->cur_key == NULL) {
new_output = malloc(sizeof(i3_output)); new_output = smalloc(sizeof(i3_output));
new_output->name = NULL; new_output->name = NULL;
new_output->ws = 0, new_output->ws = 0,
memset(&new_output->rect, 0, sizeof(rect)); memset(&new_output->rect, 0, sizeof(rect));
new_output->bar = XCB_NONE; new_output->bar = XCB_NONE;
new_output->workspaces = malloc(sizeof(struct ws_head)); new_output->workspaces = smalloc(sizeof(struct ws_head));
TAILQ_INIT(new_output->workspaces); TAILQ_INIT(new_output->workspaces);
new_output->trayclients = malloc(sizeof(struct tc_head)); new_output->trayclients = smalloc(sizeof(struct tc_head));
TAILQ_INIT(new_output->trayclients); TAILQ_INIT(new_output->trayclients);
params->outputs_walk = new_output; params->outputs_walk = new_output;
@ -171,6 +171,10 @@ static int outputs_start_map_cb(void *params_) {
return 1; return 1;
} }
if (!strcmp(params->cur_key, "rect")) {
params->in_rect = true;
}
return 1; return 1;
} }
@ -180,7 +184,33 @@ static int outputs_start_map_cb(void *params_) {
*/ */
static int outputs_end_map_cb(void *params_) { static int outputs_end_map_cb(void *params_) {
struct outputs_json_params *params = (struct outputs_json_params*) params_; struct outputs_json_params *params = (struct outputs_json_params*) params_;
/* FIXME: What is at the end of a rect? */ if (params->in_rect) {
params->in_rect = false;
/* Ignore the end of a rect */
return 1;
}
/* See if we actually handle that output */
if (config.num_outputs > 0) {
bool handle_output = false;
for (int c = 0; c < config.num_outputs; c++) {
if (strcasecmp(params->outputs_walk->name, config.outputs[c]) != 0)
continue;
handle_output = true;
break;
}
if (!handle_output) {
DLOG("Ignoring output \"%s\", not configured to handle it.\n",
params->outputs_walk->name);
FREE(params->outputs_walk->name);
FREE(params->outputs_walk->workspaces);
FREE(params->outputs_walk->trayclients);
FREE(params->outputs_walk);
FREE(params->cur_key);
return 1;
}
}
i3_output *target = get_output_by_name(params->outputs_walk->name); i3_output *target = get_output_by_name(params->outputs_walk->name);
@ -208,7 +238,7 @@ static int outputs_map_key_cb(void *params_, const unsigned char *keyVal, unsign
struct outputs_json_params *params = (struct outputs_json_params*) params_; struct outputs_json_params *params = (struct outputs_json_params*) params_;
FREE(params->cur_key); FREE(params->cur_key);
params->cur_key = malloc(sizeof(unsigned char) * (keyLen + 1)); params->cur_key = smalloc(sizeof(unsigned char) * (keyLen + 1));
strncpy(params->cur_key, (const char*) keyVal, keyLen); strncpy(params->cur_key, (const char*) keyVal, keyLen);
params->cur_key[keyLen] = '\0'; params->cur_key[keyLen] = '\0';
@ -235,7 +265,7 @@ yajl_callbacks outputs_callbacks = {
* *
*/ */
void init_outputs() { void init_outputs() {
outputs = malloc(sizeof(struct outputs_head)); outputs = smalloc(sizeof(struct outputs_head));
SLIST_INIT(outputs); SLIST_INIT(outputs);
} }
@ -249,6 +279,7 @@ void parse_outputs_json(char *json) {
params.outputs_walk = NULL; params.outputs_walk = NULL;
params.cur_key = NULL; params.cur_key = NULL;
params.json = json; params.json = json;
params.in_rect = false;
yajl_handle handle; yajl_handle handle;
yajl_status state; yajl_status state;

View File

@ -14,6 +14,8 @@
#include <err.h> #include <err.h>
#include <iconv.h> #include <iconv.h>
#include "libi3.h"
static iconv_t conversion_descriptor = 0; static iconv_t conversion_descriptor = 0;
static iconv_t conversion_descriptor2 = 0; static iconv_t conversion_descriptor2 = 0;
@ -27,9 +29,7 @@ char *convert_ucs_to_utf8(char *input) {
/* UTF-8 may consume up to 4 byte */ /* UTF-8 may consume up to 4 byte */
int buffer_size = 8; int buffer_size = 8;
char *buffer = calloc(buffer_size, 1); char *buffer = scalloc(buffer_size);
if (buffer == NULL)
err(EXIT_FAILURE, "malloc() failed\n");
size_t output_size = buffer_size; size_t output_size = buffer_size;
/* We need to use an additional pointer, because iconv() modifies it */ /* We need to use an additional pointer, because iconv() modifies it */
char *output = buffer; char *output = buffer;
@ -68,9 +68,7 @@ char *convert_utf8_to_ucs2(char *input, int *real_strlen) {
/* UCS-2 consumes exactly two bytes for each glyph */ /* UCS-2 consumes exactly two bytes for each glyph */
int buffer_size = input_size * 2; int buffer_size = input_size * 2;
char *buffer = malloc(buffer_size); char *buffer = smalloc(buffer_size);
if (buffer == NULL)
err(EXIT_FAILURE, "malloc() failed\n");
size_t output_size = buffer_size; size_t output_size = buffer_size;
/* We need to use an additional pointer, because iconv() modifies it */ /* We need to use an additional pointer, because iconv() modifies it */
char *output = buffer; char *output = buffer;

View File

@ -117,7 +117,7 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, unsigne
if (!strcmp(params->cur_key, "name")) { if (!strcmp(params->cur_key, "name")) {
/* Save the name */ /* Save the name */
params->workspaces_walk->name = malloc(sizeof(const unsigned char) * (len + 1)); params->workspaces_walk->name = smalloc(sizeof(const unsigned char) * (len + 1));
strncpy(params->workspaces_walk->name, (const char*) val, len); strncpy(params->workspaces_walk->name, (const char*) val, len);
params->workspaces_walk->name[len] = '\0'; params->workspaces_walk->name[len] = '\0';
@ -141,14 +141,17 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, unsigne
if (!strcmp(params->cur_key, "output")) { if (!strcmp(params->cur_key, "output")) {
/* We add the ws to the TAILQ of the output, it belongs to */ /* We add the ws to the TAILQ of the output, it belongs to */
output_name = malloc(sizeof(const unsigned char) * (len + 1)); output_name = smalloc(sizeof(const unsigned char) * (len + 1));
strncpy(output_name, (const char*) val, len); strncpy(output_name, (const char*) val, len);
output_name[len] = '\0'; output_name[len] = '\0';
params->workspaces_walk->output = get_output_by_name(output_name); i3_output *target = get_output_by_name(output_name);
if (target) {
params->workspaces_walk->output = target;
TAILQ_INSERT_TAIL(params->workspaces_walk->output->workspaces, TAILQ_INSERT_TAIL(params->workspaces_walk->output->workspaces,
params->workspaces_walk, params->workspaces_walk,
tailq); tailq);
}
FREE(output_name); FREE(output_name);
return 1; return 1;
@ -167,7 +170,7 @@ static int workspaces_start_map_cb(void *params_) {
i3_ws *new_workspace = NULL; i3_ws *new_workspace = NULL;
if (params->cur_key == NULL) { if (params->cur_key == NULL) {
new_workspace = malloc(sizeof(i3_ws)); new_workspace = smalloc(sizeof(i3_ws));
new_workspace->num = -1; new_workspace->num = -1;
new_workspace->name = NULL; new_workspace->name = NULL;
new_workspace->visible = 0; new_workspace->visible = 0;
@ -197,11 +200,7 @@ static int workspaces_map_key_cb(void *params_, const unsigned char *keyVal, uns
struct workspaces_json_params *params = (struct workspaces_json_params*) params_; struct workspaces_json_params *params = (struct workspaces_json_params*) params_;
FREE(params->cur_key); FREE(params->cur_key);
params->cur_key = malloc(sizeof(unsigned char) * (keyLen + 1)); params->cur_key = smalloc(sizeof(unsigned char) * (keyLen + 1));
if (params->cur_key == NULL) {
ELOG("Could not allocate memory: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
strncpy(params->cur_key, (const char*) keyVal, keyLen); strncpy(params->cur_key, (const char*) keyVal, keyLen);
params->cur_key[keyLen] = '\0'; params->cur_key[keyLen] = '\0';

View File

@ -51,8 +51,7 @@ char *strndup(const char *str, size_t n) {
for (len = 0; len < n && str[len]; len++) for (len = 0; len < n && str[len]; len++)
continue; continue;
if ((copy = malloc(len + 1)) == NULL) copy = smalloc(len + 1);
return (NULL);
memcpy(copy, str, len); memcpy(copy, str, len);
copy[len] = '\0'; copy[len] = '\0';
return (copy); return (copy);
@ -282,7 +281,9 @@ void unhide_bars() {
XCB_CONFIG_WINDOW_HEIGHT | XCB_CONFIG_WINDOW_HEIGHT |
XCB_CONFIG_WINDOW_STACK_MODE; 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; if (config.position == POS_TOP)
values[1] = walk->rect.y;
else 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;
values[4] = XCB_STACK_MODE_ABOVE; values[4] = XCB_STACK_MODE_ABOVE;
@ -310,14 +311,14 @@ void init_colors(const struct xcb_color_strings_t *new_colors) {
} while (0) } while (0)
PARSE_COLOR(bar_fg, "FFFFFF"); PARSE_COLOR(bar_fg, "FFFFFF");
PARSE_COLOR(bar_bg, "000000"); PARSE_COLOR(bar_bg, "000000");
PARSE_COLOR(active_ws_fg, "FFFFFF"); PARSE_COLOR(active_ws_fg, "888888");
PARSE_COLOR(active_ws_bg, "480000"); PARSE_COLOR(active_ws_bg, "222222");
PARSE_COLOR(inactive_ws_fg, "FFFFFF"); PARSE_COLOR(inactive_ws_fg, "888888");
PARSE_COLOR(inactive_ws_bg, "240000"); PARSE_COLOR(inactive_ws_bg, "222222");
PARSE_COLOR(urgent_ws_fg, "FFFFFF"); PARSE_COLOR(urgent_ws_fg, "FFFFFF");
PARSE_COLOR(urgent_ws_bg, "002400"); PARSE_COLOR(urgent_ws_bg, "900000");
PARSE_COLOR(focus_ws_fg, "FFFFFF"); PARSE_COLOR(focus_ws_fg, "FFFFFF");
PARSE_COLOR(focus_ws_bg, "480000"); PARSE_COLOR(focus_ws_bg, "285577");
#undef PARSE_COLOR #undef PARSE_COLOR
} }
@ -490,6 +491,9 @@ static void handle_client_message(xcb_client_message_event_t* event) {
SLIST_FOREACH(walk, outputs, slist) { SLIST_FOREACH(walk, outputs, slist) {
if (!walk->active) if (!walk->active)
continue; continue;
if (config.tray_output &&
strcasecmp(walk->name, config.tray_output) != 0)
continue;
DLOG("using output %s\n", walk->name); DLOG("using output %s\n", walk->name);
output = walk; output = walk;
} }
@ -516,7 +520,7 @@ static void handle_client_message(xcb_client_message_event_t* event) {
values); values);
/* send the XEMBED_EMBEDDED_NOTIFY message */ /* send the XEMBED_EMBEDDED_NOTIFY message */
void *event = calloc(32, 1); void *event = scalloc(32);
xcb_client_message_event_t *ev = event; xcb_client_message_event_t *ev = event;
ev->response_type = XCB_CLIENT_MESSAGE; ev->response_type = XCB_CLIENT_MESSAGE;
ev->window = client; ev->window = client;
@ -539,7 +543,7 @@ static void handle_client_message(xcb_client_message_event_t* event) {
} else { } else {
DLOG("Not mapping dock client yet\n"); DLOG("Not mapping dock client yet\n");
} }
trayclient *tc = malloc(sizeof(trayclient)); trayclient *tc = smalloc(sizeof(trayclient));
tc->win = client; tc->win = client;
tc->mapped = map_it; tc->mapped = map_it;
tc->xe_version = xe_version; tc->xe_version = xe_version;
@ -790,10 +794,11 @@ void xkb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
} }
/* /*
* Initialize xcb and use the specified fontname for text-rendering * Early initialization of the connection to X11: Everything which does not
* depend on 'config'.
* *
*/ */
char *init_xcb(char *fontname) { char *init_xcb_early() {
/* FIXME: xcb_connect leaks Memory */ /* FIXME: xcb_connect leaks Memory */
xcb_connection = xcb_connect(NULL, &screen); xcb_connection = xcb_connect(NULL, &screen);
if (xcb_connection_has_error(xcb_connection)) { if (xcb_connection_has_error(xcb_connection)) {
@ -809,6 +814,93 @@ char *init_xcb(char *fontname) {
xcb_screen = 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_screen->root; xcb_root = xcb_screen->root;
/* 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 */
uint32_t mask = XCB_GC_FOREGROUND;
uint32_t vals[] = { colors.bar_bg, colors.bar_bg };
statusline_clear = xcb_generate_id(xcb_connection);
xcb_void_cookie_t clear_ctx_cookie = xcb_create_gc_checked(xcb_connection,
statusline_clear,
xcb_root,
mask,
vals);
mask |= XCB_GC_BACKGROUND;
vals[0] = colors.bar_fg;
statusline_ctx = xcb_generate_id(xcb_connection);
xcb_void_cookie_t sl_ctx_cookie = xcb_create_gc_checked(xcb_connection,
statusline_ctx,
xcb_root,
mask,
vals);
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,
xcb_screen->width_in_pixels,
xcb_screen->height_in_pixels);
/* The various Watchers to communicate with xcb */
xcb_io = smalloc(sizeof(ev_io));
xcb_prep = smalloc(sizeof(ev_prepare));
xcb_chk = smalloc(sizeof(ev_check));
ev_io_init(xcb_io, &xcb_io_cb, xcb_get_file_descriptor(xcb_connection), EV_READ);
ev_prepare_init(xcb_prep, &xcb_prep_cb);
ev_check_init(xcb_chk, &xcb_chk_cb);
ev_io_start(main_loop, xcb_io);
ev_prepare_start(main_loop, xcb_prep);
ev_check_start(main_loop, xcb_chk);
/* Now we get the atoms and save them in a nice data structure */
get_atoms();
xcb_get_property_cookie_t path_cookie;
path_cookie = xcb_get_property_unchecked(xcb_connection,
0,
xcb_root,
atoms[I3_SOCKET_PATH],
XCB_GET_PROPERTY_TYPE_ANY,
0, PATH_MAX);
/* We check, if i3 set its socket-path */
xcb_get_property_reply_t *path_reply = xcb_get_property_reply(xcb_connection,
path_cookie,
NULL);
char *path = NULL;
if (path_reply) {
int len = xcb_get_property_value_length(path_reply);
if (len != 0) {
path = strndup(xcb_get_property_value(path_reply), len);
}
}
if (xcb_request_failed(sl_pm_cookie, "Could not allocate statusline-buffer") ||
xcb_request_failed(clear_ctx_cookie, "Could not allocate statusline-buffer-clearcontext") ||
xcb_request_failed(sl_ctx_cookie, "Could not allocate statusline-buffer-context")) {
exit(EXIT_FAILURE);
}
return path;
}
/*
* Initialization which depends on 'config' being usable. Called after the
* configuration has arrived.
*
*/
void init_xcb_late(char *fontname) {
if (fontname == NULL) {
/* XXX: font fallback to 'misc' like i3 does it would be good. */
fontname = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1";
}
/* 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);
xcb_void_cookie_t open_font_cookie; xcb_void_cookie_t open_font_cookie;
@ -823,6 +915,13 @@ char *init_xcb(char *fontname) {
query_font_cookie = xcb_query_font(xcb_connection, query_font_cookie = xcb_query_font(xcb_connection,
xcb_font); xcb_font);
xcb_change_gc(xcb_connection,
statusline_ctx,
XCB_GC_FONT,
(uint32_t[]){ xcb_font });
xcb_flush(xcb_connection);
/* To grab modifiers without blocking other applications from receiving key-events /* 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 * involving that modifier, we sadly have to use xkb which is not yet fully supported
* in xcb */ * in xcb */
@ -859,78 +958,12 @@ char *init_xcb(char *fontname) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
xkb_io = malloc(sizeof(ev_io)); xkb_io = smalloc(sizeof(ev_io));
ev_io_init(xkb_io, &xkb_io_cb, ConnectionNumber(xkb_dpy), EV_READ); ev_io_init(xkb_io, &xkb_io_cb, ConnectionNumber(xkb_dpy), EV_READ);
ev_io_start(main_loop, xkb_io); ev_io_start(main_loop, xkb_io);
XFlush(xkb_dpy); 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 */
uint32_t mask = XCB_GC_FOREGROUND;
uint32_t vals[3] = { colors.bar_bg, colors.bar_bg, xcb_font };
statusline_clear = xcb_generate_id(xcb_connection);
xcb_void_cookie_t clear_ctx_cookie = xcb_create_gc_checked(xcb_connection,
statusline_clear,
xcb_root,
mask,
vals);
mask |= XCB_GC_BACKGROUND | XCB_GC_FONT;
vals[0] = colors.bar_fg;
statusline_ctx = xcb_generate_id(xcb_connection);
xcb_void_cookie_t sl_ctx_cookie = xcb_create_gc_checked(xcb_connection,
statusline_ctx,
xcb_root,
mask,
vals);
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,
xcb_screen->width_in_pixels,
xcb_screen->height_in_pixels);
/* The various Watchers to communicate with xcb */
xcb_io = malloc(sizeof(ev_io));
xcb_prep = malloc(sizeof(ev_prepare));
xcb_chk = malloc(sizeof(ev_check));
ev_io_init(xcb_io, &xcb_io_cb, xcb_get_file_descriptor(xcb_connection), EV_READ);
ev_prepare_init(xcb_prep, &xcb_prep_cb);
ev_check_init(xcb_chk, &xcb_chk_cb);
ev_io_start(main_loop, xcb_io);
ev_prepare_start(main_loop, xcb_prep);
ev_check_start(main_loop, xcb_chk);
/* Now we get the atoms and save them in a nice data structure */
get_atoms();
xcb_get_property_cookie_t path_cookie;
path_cookie = xcb_get_property_unchecked(xcb_connection,
0,
xcb_root,
atoms[I3_SOCKET_PATH],
XCB_GET_PROPERTY_TYPE_ANY,
0, PATH_MAX);
/* We check, if i3 set its socket-path */
xcb_get_property_reply_t *path_reply = xcb_get_property_reply(xcb_connection,
path_cookie,
NULL);
char *path = NULL;
if (path_reply) {
int len = xcb_get_property_value_length(path_reply);
if (len != 0) {
path = strndup(xcb_get_property_value(path_reply), len);
}
}
/* Now we save the font-infos */ /* Now we save the font-infos */
font_info = xcb_query_font_reply(xcb_connection, font_info = xcb_query_font_reply(xcb_connection,
query_font_cookie, query_font_cookie,
@ -949,14 +982,6 @@ char *init_xcb(char *fontname) {
} }
DLOG("Calculated Font-height: %d\n", font_height); DLOG("Calculated Font-height: %d\n", font_height);
if (xcb_request_failed(sl_pm_cookie, "Could not allocate statusline-buffer") ||
xcb_request_failed(clear_ctx_cookie, "Could not allocate statusline-buffer-clearcontext") ||
xcb_request_failed(sl_ctx_cookie, "Could not allocate statusline-buffer-context")) {
exit(EXIT_FAILURE);
}
return path;
} }
/* /*
@ -966,6 +991,7 @@ char *init_xcb(char *fontname) {
* *
*/ */
void init_tray() { void init_tray() {
DLOG("Initializing system tray functionality\n");
/* request the tray manager atom for the X11 display we are running on */ /* request the tray manager atom for the X11 display we are running on */
char atomname[strlen("_NET_SYSTEM_TRAY_S") + 11]; char atomname[strlen("_NET_SYSTEM_TRAY_S") + 11];
snprintf(atomname, strlen("_NET_SYSTEM_TRAY_S") + 11, "_NET_SYSTEM_TRAY_S%d", screen); snprintf(atomname, strlen("_NET_SYSTEM_TRAY_S") + 11, "_NET_SYSTEM_TRAY_S%d", screen);
@ -1030,7 +1056,7 @@ void init_tray() {
} }
/* Inform clients waiting for a new _NET_SYSTEM_TRAY that we are here */ /* Inform clients waiting for a new _NET_SYSTEM_TRAY that we are here */
void *event = calloc(32, 1); void *event = scalloc(32);
xcb_client_message_event_t *ev = event; xcb_client_message_event_t *ev = event;
ev->response_type = XCB_CLIENT_MESSAGE; ev->response_type = XCB_CLIENT_MESSAGE;
ev->window = xcb_root; ev->window = xcb_root;
@ -1172,6 +1198,7 @@ void realloc_sl_buffer() {
void reconfig_windows() { void reconfig_windows() {
uint32_t mask; uint32_t mask;
uint32_t values[5]; uint32_t values[5];
static bool tray_configured = false;
i3_output *walk; i3_output *walk;
SLIST_FOREACH(walk, outputs, slist) { SLIST_FOREACH(walk, outputs, slist) {
@ -1185,9 +1212,6 @@ void reconfig_windows() {
if (walk->bar == XCB_NONE) { if (walk->bar == XCB_NONE) {
DLOG("Creating Window for output %s\n", walk->name); DLOG("Creating Window for output %s\n", walk->name);
/* TODO: only call init_tray() if the tray is configured for this output */
init_tray();
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_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK; mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
@ -1281,15 +1305,15 @@ void reconfig_windows() {
uint32_t bottom_start_x; uint32_t bottom_start_x;
uint32_t bottom_end_x; uint32_t bottom_end_x;
} __attribute__((__packed__)) strut_partial = {0,}; } __attribute__((__packed__)) strut_partial = {0,};
switch (config.dockpos) { switch (config.position) {
case DOCKPOS_NONE: case POS_NONE:
break; break;
case DOCKPOS_TOP: case POS_TOP:
strut_partial.top = font_height + 6; strut_partial.top = font_height + 6;
strut_partial.top_start_x = walk->rect.x; strut_partial.top_start_x = walk->rect.x;
strut_partial.top_end_x = walk->rect.x + walk->rect.w; strut_partial.top_end_x = walk->rect.x + walk->rect.w;
break; break;
case DOCKPOS_BOT: case POS_BOT:
strut_partial.bottom = font_height + 6; strut_partial.bottom = font_height + 6;
strut_partial.bottom_start_x = walk->rect.x; strut_partial.bottom_start_x = walk->rect.x;
strut_partial.bottom_end_x = walk->rect.x + walk->rect.w; strut_partial.bottom_end_x = walk->rect.x + walk->rect.w;
@ -1331,6 +1355,13 @@ void reconfig_windows() {
(!config.hide_on_modifier && xcb_request_failed(map_cookie, "Could not map window"))) { (!config.hide_on_modifier && xcb_request_failed(map_cookie, "Could not map window"))) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (!tray_configured &&
(!config.tray_output ||
strcasecmp("none", config.tray_output) != 0)) {
init_tray();
tray_configured = true;
}
} else { } else {
/* We already have a bar, so we just reconfigure it */ /* We already have a bar, so we just reconfigure it */
mask = XCB_CONFIG_WINDOW_X | mask = XCB_CONFIG_WINDOW_X |

View File

@ -1,5 +1,5 @@
/* /*
* vim:ts=8:expandtab * vim:ts=4:sw=4:expandtab
* *
* i3 - an improved dynamic tiling window manager * i3 - an improved dynamic tiling window manager
* *
@ -22,9 +22,11 @@
#include "i3.h" #include "i3.h"
typedef struct Config Config; typedef struct Config Config;
typedef struct Barconfig Barconfig;
extern char *current_configpath; extern char *current_configpath;
extern Config config; extern Config config;
extern SLIST_HEAD(modes_head, Mode) modes; extern SLIST_HEAD(modes_head, Mode) modes;
extern TAILQ_HEAD(barconfig_head, Barconfig) barconfigs;
/** /**
* Used during the config file lexing/parsing to keep the state of the lexer * Used during the config file lexing/parsing to keep the state of the lexer
@ -32,17 +34,18 @@ extern SLIST_HEAD(modes_head, Mode) modes;
* *
*/ */
struct context { struct context {
bool has_errors; bool has_errors;
bool has_warnings;
int line_number; int line_number;
char *line_copy; char *line_copy;
const char *filename; const char *filename;
char *compact_error; char *compact_error;
/* These are the same as in YYLTYPE */ /* These are the same as in YYLTYPE */
int first_column; int first_column;
int last_column; int last_column;
}; };
/** /**
@ -51,9 +54,9 @@ struct context {
* *
*/ */
struct Colortriple { struct Colortriple {
uint32_t border; uint32_t border;
uint32_t background; uint32_t background;
uint32_t text; uint32_t text;
}; };
/** /**
@ -62,11 +65,11 @@ struct Colortriple {
* *
*/ */
struct Variable { struct Variable {
char *key; char *key;
char *value; char *value;
char *next_match; char *next_match;
SLIST_ENTRY(Variable) variables; SLIST_ENTRY(Variable) variables;
}; };
/** /**
@ -76,10 +79,10 @@ struct Variable {
* *
*/ */
struct Mode { struct Mode {
char *name; char *name;
struct bindings_head *bindings; struct bindings_head *bindings;
SLIST_ENTRY(Mode) modes; SLIST_ENTRY(Mode) modes;
}; };
/** /**
@ -88,86 +91,152 @@ struct Mode {
* *
*/ */
struct Config { struct Config {
const char *terminal; const char *terminal;
i3Font font; i3Font font;
char *ipc_socket_path; char *ipc_socket_path;
const char *restart_state_path; const char *restart_state_path;
int default_layout; int default_layout;
int container_stack_limit; int container_stack_limit;
int container_stack_limit_value; int container_stack_limit_value;
/** Default orientation for new containers */ /** Default orientation for new containers */
int default_orientation; int default_orientation;
/** By default, focus follows mouse. If the user explicitly wants to /** By default, focus follows mouse. If the user explicitly wants to
* turn this off (and instead rely only on the keyboard for changing * turn this off (and instead rely only on the keyboard for changing
* focus), we allow him to do this with this relatively special option. * focus), we allow him to do this with this relatively special option.
* It is not planned to add any different focus models. */ * It is not planned to add any different focus models. */
bool disable_focus_follows_mouse; bool disable_focus_follows_mouse;
/** By default, a workspace bar is drawn at the bottom of the screen. /** By default, a workspace bar is drawn at the bottom of the screen.
* If you want to have a more fancy bar, it is recommended to replace * If you want to have a more fancy bar, it is recommended to replace
* the whole bar by dzen2, for example using the i3-wsbar script which * the whole bar by dzen2, for example using the i3-wsbar script which
* comes with i3. Thus, you can turn it off entirely. */ * comes with i3. Thus, you can turn it off entirely. */
bool disable_workspace_bar; bool disable_workspace_bar;
/** Think of the following layout: Horizontal workspace with a tabbed /** Think of the following layout: Horizontal workspace with a tabbed
* con on the left of the screen and a terminal on the right of the * con on the left of the screen and a terminal on the right of the
* screen. You are in the second container in the tabbed container and * screen. You are in the second container in the tabbed container and
* focus to the right. By default, i3 will set focus to the terminal on * focus to the right. By default, i3 will set focus to the terminal on
* the right. If you are in the first container in the tabbed container * the right. If you are in the first container in the tabbed container
* however, focusing to the left will wrap. This option forces i3 to * however, focusing to the left will wrap. This option forces i3 to
* always wrap, which will result in you having to use "focus parent" * always wrap, which will result in you having to use "focus parent"
* more often. */ * more often. */
bool force_focus_wrapping; bool force_focus_wrapping;
/** By default, use the RandR API for multi-monitor setups. /** By default, use the RandR API for multi-monitor setups.
* Unfortunately, the nVidia binary graphics driver doesn't support * Unfortunately, the nVidia binary graphics driver doesn't support
* this API. Instead, it only support the less powerful Xinerama API, * this API. Instead, it only support the less powerful Xinerama API,
* which can be enabled by this option. * which can be enabled by this option.
* *
* Note: this option takes only effect on the initial startup (eg. * Note: this option takes only effect on the initial startup (eg.
* reconfiguration is not possible). On startup, the list of screens * reconfiguration is not possible). On startup, the list of screens
* is fetched once and never updated. */ * is fetched once and never updated. */
bool force_xinerama; bool force_xinerama;
/** Automatic workspace back and forth switching. If this is set, a /** Automatic workspace back and forth switching. If this is set, a
* switch to the currently active workspace will switch to the * switch to the currently active workspace will switch to the
* previously focused one instead, making it possible to fast toggle * previously focused one instead, making it possible to fast toggle
* between two workspaces. */ * between two workspaces. */
bool workspace_auto_back_and_forth; bool workspace_auto_back_and_forth;
/** The default border style for new windows. */ /** The default border style for new windows. */
border_style_t default_border; border_style_t default_border;
/** The default border style for new floating windows. */ /** The default border style for new floating windows. */
border_style_t default_floating_border; border_style_t default_floating_border;
/** The modifier which needs to be pressed in combination with your mouse /** The modifier which needs to be pressed in combination with your mouse
* buttons to do things with floating windows (move, resize) */ * buttons to do things with floating windows (move, resize) */
uint32_t floating_modifier; uint32_t floating_modifier;
/* Color codes are stored here */ /* Color codes are stored here */
struct config_client { struct config_client {
uint32_t background; uint32_t background;
struct Colortriple focused; struct Colortriple focused;
struct Colortriple focused_inactive; struct Colortriple focused_inactive;
struct Colortriple unfocused; struct Colortriple unfocused;
struct Colortriple urgent; struct Colortriple urgent;
} client; } client;
struct config_bar { struct config_bar {
struct Colortriple focused; struct Colortriple focused;
struct Colortriple unfocused; struct Colortriple unfocused;
struct Colortriple urgent; struct Colortriple urgent;
} bar; } bar;
/** What should happen when a new popup is opened during fullscreen mode */ /** What should happen when a new popup is opened during fullscreen mode */
enum { enum {
PDF_LEAVE_FULLSCREEN = 0, PDF_LEAVE_FULLSCREEN = 0,
PDF_IGNORE = 1 PDF_IGNORE = 1
} popup_during_fullscreen; } popup_during_fullscreen;
};
/**
* Holds the status bar configuration (i3bar). One of these structures is
* created for each 'bar' block in the config.
*
*/
struct Barconfig {
/** Automatically generated ID for this bar config. Used by the bar process
* to request a specific configuration. */
char *id;
/** Number of outputs in the outputs array */
int num_outputs;
/** Outputs on which this bar should show up on. We use an array for
* simplicity (since we store just strings). */
char **outputs;
/** Output on which the tray should be shown. The special value of 'no'
* disables the tray (its enabled by default). */
char *tray_output;
/** Path to the i3 IPC socket. This option is discouraged since programs
* can find out the path by looking for the I3_SOCKET_PATH property on the
* root window! */
char *socket_path;
/** Bar display mode (hide unless modifier is pressed or show in dock mode) */
enum { M_DOCK = 0, M_HIDE = 1 } mode;
/** Bar position (bottom by default). */
enum { P_BOTTOM = 0, P_TOP = 1 } position;
/** Command that should be run to get a statusline, for example 'i3status'.
* Will be passed to the shell. */
char *status_command;
/** Font specification for all text rendered on the bar. */
char *font;
/** Hide workspace buttons? Configuration option is 'workspace_buttons no'
* but we invert the bool to get the correct default when initializing with
* zero. */
bool hide_workspace_buttons;
/** Enable verbose mode? Useful for debugging purposes. */
bool verbose;
struct bar_colors {
char *background;
char *statusline;
char *focused_workspace_text;
char *focused_workspace_bg;
char *active_workspace_text;
char *active_workspace_bg;
char *inactive_workspace_text;
char *inactive_workspace_bg;
char *urgent_workspace_text;
char *urgent_workspace_bg;
} colors;
TAILQ_ENTRY(Barconfig) configs;
}; };
/** /**

View File

@ -44,5 +44,6 @@ extern uint8_t root_depth;
extern bool xcursor_supported, xkb_supported; extern bool xcursor_supported, xkb_supported;
extern xcb_window_t root; extern xcb_window_t root;
extern struct ev_loop *main_loop; extern struct ev_loop *main_loop;
extern bool only_check_config;
#endif #endif

View File

@ -41,6 +41,9 @@
/** Request the current defined marks from i3 */ /** Request the current defined marks from i3 */
#define I3_IPC_MESSAGE_TYPE_GET_MARKS 5 #define I3_IPC_MESSAGE_TYPE_GET_MARKS 5
/** Request the configuration for a specific 'bar' */
#define I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG 6
/* /*
* Messages from i3 to clients * Messages from i3 to clients
* *
@ -61,9 +64,12 @@
/** Tree reply type */ /** Tree reply type */
#define I3_IPC_REPLY_TYPE_TREE 4 #define I3_IPC_REPLY_TYPE_TREE 4
/** Marks reply type*/ /** Marks reply type */
#define I3_IPC_REPLY_TYPE_MARKS 5 #define I3_IPC_REPLY_TYPE_MARKS 5
/** Bar config reply type */
#define I3_IPC_REPLY_TYPE_BAR_CONFIG 6
/* /*
* Events from i3 to clients. Events have the first bit set high. * Events from i3 to clients. Events have the first bit set high.
* *

View File

@ -37,6 +37,11 @@ int yycolumn = 1;
yy_push_state(EAT_WHITESPACE); \ yy_push_state(EAT_WHITESPACE); \
} while (0) } while (0)
#define BAR_DOUBLE_COLOR do { \
yy_push_state(BAR_COLOR); \
yy_push_state(BAR_COLOR); \
} while (0)
%} %}
EOL (\r?\n) EOL (\r?\n)
@ -50,7 +55,13 @@ EOL (\r?\n)
%s OUTPUT_COND %s OUTPUT_COND
%s FOR_WINDOW_COND %s FOR_WINDOW_COND
%s EAT_WHITESPACE %s EAT_WHITESPACE
%x BUFFER_LINE %x BUFFER_LINE
%x BAR
%x BAR_MODE
%x BAR_POSITION
%x BAR_COLORS
%x BAR_COLOR
%% %%
@ -74,6 +85,37 @@ EOL (\r?\n)
yycolumn = 1; yycolumn = 1;
} }
/* This part of the lexer handles the bar {} blocks */
<BAR,BAR_MODE,BAR_POSITION,BAR_COLORS,BAR_COLOR>[ \t]+ { /* ignore whitespace */ ; }
<BAR>"{" { return '{'; }
<BAR>"}" { yy_pop_state(); return '}'; }
<BAR>^[ \t]*#[^\n]* { return TOKCOMMENT; }
<BAR>output { WS_STRING; return TOK_BAR_OUTPUT; }
<BAR>tray_output { WS_STRING; return TOK_BAR_TRAY_OUTPUT; }
<BAR>socket_path { WS_STRING; return TOK_BAR_SOCKET_PATH; }
<BAR>mode { yy_push_state(BAR_MODE); return TOK_BAR_MODE; }
<BAR_MODE>hide { yy_pop_state(); return TOK_BAR_HIDE; }
<BAR_MODE>dock { yy_pop_state(); return TOK_BAR_DOCK; }
<BAR>position { yy_push_state(BAR_POSITION); return TOK_BAR_POSITION; }
<BAR_POSITION>bottom { yy_pop_state(); return TOK_BAR_BOTTOM; }
<BAR_POSITION>top { yy_pop_state(); return TOK_BAR_TOP; }
<BAR>status_command { WS_STRING; return TOK_BAR_STATUS_COMMAND; }
<BAR>font { WS_STRING; return TOK_BAR_FONT; }
<BAR>workspace_buttons { return TOK_BAR_WORKSPACE_BUTTONS; }
<BAR>verbose { return TOK_BAR_VERBOSE; }
<BAR>colors { yy_push_state(BAR_COLORS); return TOK_BAR_COLORS; }
<BAR_COLORS>"{" { return '{'; }
<BAR_COLORS>"}" { yy_pop_state(); return '}'; }
<BAR_COLORS>background { yy_push_state(BAR_COLOR); return TOK_BAR_COLOR_BACKGROUND; }
<BAR_COLORS>statusline { yy_push_state(BAR_COLOR); return TOK_BAR_COLOR_STATUSLINE; }
<BAR_COLORS>focused_workspace { BAR_DOUBLE_COLOR; return TOK_BAR_COLOR_FOCUSED_WORKSPACE; }
<BAR_COLORS>active_workspace { BAR_DOUBLE_COLOR; return TOK_BAR_COLOR_ACTIVE_WORKSPACE; }
<BAR_COLORS>inactive_workspace { BAR_DOUBLE_COLOR; return TOK_BAR_COLOR_INACTIVE_WORKSPACE; }
<BAR_COLORS>urgent_workspace { BAR_DOUBLE_COLOR; return TOK_BAR_COLOR_URGENT_WORKSPACE; }
<BAR_COLOR>#[0-9a-fA-F]+ { yy_pop_state(); yylval.string = sstrdup(yytext+1); return HEXCOLOR; }
<BAR,BAR_COLORS,BAR_MODE,BAR_POSITION>[a-zA-Z]+ { yylval.string = sstrdup(yytext); return WORD; }
<FOR_WINDOW_COND>"]" { yy_pop_state(); return ']'; } <FOR_WINDOW_COND>"]" { yy_pop_state(); return ']'; }
<ASSIGN_COND>"[" { <ASSIGN_COND>"[" {
@ -93,19 +135,20 @@ EOL (\r?\n)
yylval.string = copy; yylval.string = copy;
return STR; return STR;
} }
<WANT_STRING>[^\n]+ { BEGIN(INITIAL); yylval.string = sstrdup(yytext); return STR; } <WANT_STRING>[^\n]+ { yy_pop_state(); yylval.string = sstrdup(yytext); return STR; }
<OUTPUT_COND>[a-zA-Z0-9_-]+ { yylval.string = sstrdup(yytext); return OUTPUT; } <OUTPUT_COND>[a-zA-Z0-9_-]+ { yy_pop_state(); yylval.string = sstrdup(yytext); return OUTPUT; }
^[ \t]*#[^\n]* { return TOKCOMMENT; } ^[ \t]*#[^\n]* { return TOKCOMMENT; }
<COLOR_COND>[0-9a-fA-F]+ { yylval.string = sstrdup(yytext); return HEX; } <COLOR_COND>#[0-9a-fA-F]+ { yy_pop_state(); yylval.string = sstrdup(yytext); return HEXCOLOR; }
<ASSIGN_TARGET_COND>[ \t]*→[ \t]* { BEGIN(WANT_STRING); } <ASSIGN_TARGET_COND>[ \t]*→[ \t]* { BEGIN(WANT_STRING); }
<ASSIGN_TARGET_COND>[ \t]+ { BEGIN(WANT_STRING); } <ASSIGN_TARGET_COND>[ \t]+ { BEGIN(WANT_STRING); }
[0-9]+ { yylval.number = atoi(yytext); return NUMBER; } [0-9]+ { yylval.number = atoi(yytext); return NUMBER; }
bar { yy_push_state(BAR); return TOK_BAR; }
mode { return TOKMODE; } mode { return TOKMODE; }
bind { yy_push_state(WANT_STRING); yy_push_state(EAT_WHITESPACE); yy_push_state(EAT_WHITESPACE); return TOKBINDCODE; } bind { yy_push_state(WANT_STRING); yy_push_state(EAT_WHITESPACE); yy_push_state(EAT_WHITESPACE); return TOKBINDCODE; }
bindcode { yy_push_state(WANT_STRING); yy_push_state(EAT_WHITESPACE); yy_push_state(EAT_WHITESPACE); return TOKBINDCODE; } bindcode { yy_push_state(WANT_STRING); yy_push_state(EAT_WHITESPACE); yy_push_state(EAT_WHITESPACE); return TOKBINDCODE; }
bindsym { yy_push_state(BINDSYM_COND); yy_push_state(EAT_WHITESPACE); return TOKBINDSYM; } bindsym { yy_push_state(BINDSYM_COND); yy_push_state(EAT_WHITESPACE); return TOKBINDSYM; }
floating_modifier { BEGIN(INITIAL); return TOKFLOATING_MODIFIER; } floating_modifier { return TOKFLOATING_MODIFIER; }
workspace { BEGIN(INITIAL); return TOKWORKSPACE; } workspace { return TOKWORKSPACE; }
output { yy_push_state(OUTPUT_COND); yy_push_state(EAT_WHITESPACE); return TOKOUTPUT; } output { yy_push_state(OUTPUT_COND); yy_push_state(EAT_WHITESPACE); return TOKOUTPUT; }
terminal { WS_STRING; return TOKTERMINAL; } terminal { WS_STRING; return TOKTERMINAL; }
font { WS_STRING; return TOKFONT; } font { WS_STRING; return TOKFONT; }
@ -153,14 +196,14 @@ cols { /* yylval.number = STACK_LIMIT_COLS; */return
rows { /* yylval.number = STACK_LIMIT_ROWS; */return TOKSTACKLIMIT; } rows { /* yylval.number = STACK_LIMIT_ROWS; */return TOKSTACKLIMIT; }
exec { WS_STRING; return TOKEXEC; } exec { WS_STRING; return TOKEXEC; }
exec_always { WS_STRING; return TOKEXEC_ALWAYS; } exec_always { WS_STRING; return TOKEXEC_ALWAYS; }
client.background { BEGIN(COLOR_COND); yylval.single_color = &config.client.background; return TOKSINGLECOLOR; } client.background { yy_push_state(COLOR_COND); yylval.single_color = &config.client.background; return TOKSINGLECOLOR; }
client.focused { BEGIN(COLOR_COND); yylval.color = &config.client.focused; return TOKCOLOR; } client.focused { yy_push_state(COLOR_COND); yy_push_state(COLOR_COND); yy_push_state(COLOR_COND); yylval.color = &config.client.focused; return TOKCOLOR; }
client.focused_inactive { BEGIN(COLOR_COND); yylval.color = &config.client.focused_inactive; return TOKCOLOR; } client.focused_inactive { yy_push_state(COLOR_COND); yy_push_state(COLOR_COND); yy_push_state(COLOR_COND); yylval.color = &config.client.focused_inactive; return TOKCOLOR; }
client.unfocused { BEGIN(COLOR_COND); yylval.color = &config.client.unfocused; return TOKCOLOR; } client.unfocused { yy_push_state(COLOR_COND); yy_push_state(COLOR_COND); yy_push_state(COLOR_COND); yylval.color = &config.client.unfocused; return TOKCOLOR; }
client.urgent { BEGIN(COLOR_COND); yylval.color = &config.client.urgent; return TOKCOLOR; } client.urgent { yy_push_state(COLOR_COND); yy_push_state(COLOR_COND); yy_push_state(COLOR_COND); yylval.color = &config.client.urgent; return TOKCOLOR; }
bar.focused { BEGIN(COLOR_COND); yylval.color = &config.bar.focused; return TOKCOLOR; } bar.focused { yy_push_state(COLOR_COND); yylval.color = &config.bar.focused; return TOKCOLOR; }
bar.unfocused { BEGIN(COLOR_COND); yylval.color = &config.bar.unfocused; return TOKCOLOR; } bar.unfocused { yy_push_state(COLOR_COND); yylval.color = &config.bar.unfocused; return TOKCOLOR; }
bar.urgent { BEGIN(COLOR_COND); yylval.color = &config.bar.urgent; return TOKCOLOR; } bar.urgent { yy_push_state(COLOR_COND); yylval.color = &config.bar.urgent; return TOKCOLOR; }
Mod1 { yylval.number = BIND_MOD1; return MODIFIER; } Mod1 { yylval.number = BIND_MOD1; return MODIFIER; }
Mod2 { yylval.number = BIND_MOD2; return MODIFIER; } Mod2 { yylval.number = BIND_MOD2; return MODIFIER; }
Mod3 { yylval.number = BIND_MOD3; return MODIFIER; } Mod3 { yylval.number = BIND_MOD3; return MODIFIER; }
@ -179,20 +222,18 @@ con_id { yy_push_state(WANT_QSTRING); return TOK_CON_ID
con_mark { yy_push_state(WANT_QSTRING); return TOK_MARK; } con_mark { yy_push_state(WANT_QSTRING); return TOK_MARK; }
title { yy_push_state(WANT_QSTRING); return TOK_TITLE; } title { yy_push_state(WANT_QSTRING); return TOK_TITLE; }
{EOL} { <*>{EOL} {
FREE(context->line_copy); FREE(context->line_copy);
context->line_number++; context->line_number++;
BEGIN(INITIAL);
yy_push_state(BUFFER_LINE); yy_push_state(BUFFER_LINE);
} }
<BINDSYM_COND>[ \t]+ { BEGIN(WANT_STRING); } <BINDSYM_COND>[ \t]+ { yy_pop_state(); yy_push_state(WANT_STRING); }
<OUTPUT_COND>[ \t]+ { BEGIN(WANT_STRING); } <OUTPUT_COND>[ \t]+ { yy_pop_state(); yy_push_state(WANT_STRING); }
[ \t]+ { /* ignore whitespace */ ; } [ \t]+ { /* ignore whitespace */ ; }
\"[^\"]+\" { \"[^\"]+\" {
/* if ASSIGN_COND then */ /* if ASSIGN_COND then */
if (yy_start_stack_ptr > 0) if (yy_start_stack_ptr > 0)
yy_pop_state(); yy_pop_state();
else BEGIN(INITIAL);
/* yylval will be the string, but without quotes */ /* yylval will be the string, but without quotes */
char *copy = sstrdup(yytext+1); char *copy = sstrdup(yytext+1);
copy[strlen(copy)-1] = '\0'; copy[strlen(copy)-1] = '\0';

View File

@ -14,6 +14,7 @@
static pid_t configerror_pid = -1; static pid_t configerror_pid = -1;
static Match current_match; static Match current_match;
static Barconfig current_bar;
typedef struct yy_buffer_state *YY_BUFFER_STATE; typedef struct yy_buffer_state *YY_BUFFER_STATE;
extern int yylex(struct context *context); extern int yylex(struct context *context);
@ -265,7 +266,10 @@ static void nagbar_cleanup(EV_P_ ev_cleanup *watcher, int revent) {
* *
*/ */
static void start_configerror_nagbar(const char *config_path) { static void start_configerror_nagbar(const char *config_path) {
fprintf(stderr, "Would start i3-nagscreen now\n"); if (only_check_config)
return;
fprintf(stderr, "Starting i3-nagbar due to configuration errors\n");
configerror_pid = fork(); configerror_pid = fork();
if (configerror_pid == -1) { if (configerror_pid == -1) {
warn("Could not fork()"); warn("Could not fork()");
@ -282,13 +286,17 @@ static void start_configerror_nagbar(const char *config_path) {
exit(1); exit(1);
char *argv[] = { char *argv[] = {
NULL, /* will be replaced by the executable path */ NULL, /* will be replaced by the executable path */
"-t",
(context->has_errors ? "error" : "warning"),
"-m", "-m",
"You have an error in your i3 config file!", (context->has_errors ?
"You have an error in your i3 config file!" :
"Your config is outdated. Please fix the warnings to make sure everything works."),
"-b", "-b",
"edit config", "edit config",
editaction, editaction,
(errorfilename ? "-b" : NULL), (errorfilename ? "-b" : NULL),
"show errors", (context->has_errors ? "show errors" : "show warnings"),
pageraction, pageraction,
NULL NULL
}; };
@ -386,6 +394,31 @@ static void check_for_duplicate_bindings(struct context *context) {
} }
} }
static void migrate_i3bar_exec(struct Autostart *exec) {
ELOG("**********************************************************************\n");
ELOG("IGNORING exec command: %s\n", exec->command);
ELOG("It contains \"i3bar\". Since i3 v4.1, i3bar will be automatically started\n");
ELOG("for each 'bar' configuration block in your i3 config. Please remove the exec\n");
ELOG("line and add the following to your i3 config:\n");
ELOG("\n");
ELOG(" bar {\n");
ELOG(" status_command i3status\n");
ELOG(" }\n");
ELOG("**********************************************************************\n");
/* Generate a dummy bar configuration */
Barconfig *bar_config = scalloc(sizeof(Barconfig));
/* The hard-coded ID is not a problem. It does not conflict with the
* auto-generated bar IDs and having multiple hard-coded IDs is irrelevant
* they all just contain status_command = i3status */
bar_config->id = sstrdup("migrate-bar");
bar_config->status_command = sstrdup("i3status");
TAILQ_INSERT_TAIL(&barconfigs, bar_config, configs);
/* Trigger an i3-nagbar */
context->has_warnings = true;
}
void parse_file(const char *f) { void parse_file(const char *f) {
SLIST_HEAD(variables_head, Variable) variables = SLIST_HEAD_INITIALIZER(&variables); SLIST_HEAD(variables_head, Variable) variables = SLIST_HEAD_INITIALIZER(&variables);
int fd, ret, read_bytes = 0; int fd, ret, read_bytes = 0;
@ -549,7 +582,31 @@ void parse_file(const char *f) {
check_for_duplicate_bindings(context); check_for_duplicate_bindings(context);
if (context->has_errors) { /* XXX: The following code will be removed in i3 v4.3 (three releases from
* now, as of 2011-10-22) */
/* Check for any exec or exec_always lines starting i3bar. We remove these
* and add a bar block instead. Additionally, a i3-nagbar warning (not an
* error) will be displayed so that users update their config file. */
struct Autostart *exec, *next;
for (exec = TAILQ_FIRST(&autostarts); exec; ) {
next = TAILQ_NEXT(exec, autostarts);
if (strstr(exec->command, "i3bar") != NULL) {
migrate_i3bar_exec(exec);
TAILQ_REMOVE(&autostarts, exec, autostarts);
}
exec = next;
}
for (exec = TAILQ_FIRST(&autostarts_always); exec; ) {
next = TAILQ_NEXT(exec, autostarts_always);
if (strstr(exec->command, "i3bar") != NULL) {
migrate_i3bar_exec(exec);
TAILQ_REMOVE(&autostarts_always, exec, autostarts_always);
}
exec = next;
}
if (context->has_errors || context->has_warnings) {
start_configerror_nagbar(f); start_configerror_nagbar(f);
} }
@ -586,7 +643,7 @@ void parse_file(const char *f) {
%token <string> WORD "<word>" %token <string> WORD "<word>"
%token <string> STR "<string>" %token <string> STR "<string>"
%token <string> STR_NG "<string (non-greedy)>" %token <string> STR_NG "<string (non-greedy)>"
%token <string> HEX "<hex>" %token <string> HEXCOLOR "#<hex>"
%token <string> OUTPUT "<RandR output>" %token <string> OUTPUT "<RandR output>"
%token TOKBINDCODE %token TOKBINDCODE
%token TOKTERMINAL %token TOKTERMINAL
@ -610,6 +667,7 @@ void parse_file(const char *f) {
%token <color> TOKCOLOR %token <color> TOKCOLOR
%token TOKARROW "→" %token TOKARROW "→"
%token TOKMODE "mode" %token TOKMODE "mode"
%token TOK_BAR "bar"
%token TOK_ORIENTATION "default_orientation" %token TOK_ORIENTATION "default_orientation"
%token TOK_HORIZ "horizontal" %token TOK_HORIZ "horizontal"
%token TOK_VERT "vertical" %token TOK_VERT "vertical"
@ -634,6 +692,27 @@ void parse_file(const char *f) {
%token TOK_LEAVE_FULLSCREEN "leave_fullscreen" %token TOK_LEAVE_FULLSCREEN "leave_fullscreen"
%token TOK_FOR_WINDOW "for_window" %token TOK_FOR_WINDOW "for_window"
%token TOK_BAR_OUTPUT "output (bar)"
%token TOK_BAR_TRAY_OUTPUT "tray_output"
%token TOK_BAR_SOCKET_PATH "socket_path"
%token TOK_BAR_MODE "mode"
%token TOK_BAR_HIDE "hide"
%token TOK_BAR_DOCK "dock"
%token TOK_BAR_POSITION "position"
%token TOK_BAR_BOTTOM "bottom"
%token TOK_BAR_TOP "top"
%token TOK_BAR_STATUS_COMMAND "status_command"
%token TOK_BAR_FONT "font"
%token TOK_BAR_WORKSPACE_BUTTONS "workspace_buttons"
%token TOK_BAR_VERBOSE "verbose"
%token TOK_BAR_COLORS "colors"
%token TOK_BAR_COLOR_BACKGROUND "background"
%token TOK_BAR_COLOR_STATUSLINE "statusline"
%token TOK_BAR_COLOR_FOCUSED_WORKSPACE "focused_workspace"
%token TOK_BAR_COLOR_ACTIVE_WORKSPACE "active_workspace"
%token TOK_BAR_COLOR_INACTIVE_WORKSPACE "inactive_workspace"
%token TOK_BAR_COLOR_URGENT_WORKSPACE "urgent_workspace"
%token TOK_MARK "mark" %token TOK_MARK "mark"
%token TOK_CLASS "class" %token TOK_CLASS "class"
%token TOK_INSTANCE "instance" %token TOK_INSTANCE "instance"
@ -655,6 +734,8 @@ void parse_file(const char *f) {
%type <number> colorpixel %type <number> colorpixel
%type <number> bool %type <number> bool
%type <number> popup_setting %type <number> popup_setting
%type <number> bar_position_position
%type <number> bar_mode_mode
%type <string> command %type <string> command
%type <string> word_or_number %type <string> word_or_number
%type <string> optional_workspace_name %type <string> optional_workspace_name
@ -672,6 +753,7 @@ line:
bindline bindline
| for_window | for_window
| mode | mode
| bar
| floating_modifier | floating_modifier
| orientation | orientation
| workspace_layout | workspace_layout
@ -902,6 +984,207 @@ modeline:
} }
; ;
bar:
TOK_BAR '{' barlines '}'
{
printf("\t new bar configuration finished, saving.\n");
/* Generate a unique ID for this bar */
current_bar.id = sstrdup("bar-XXXXXX");
/* This works similar to mktemp in that it replaces the last six X with
* random letters, but without the restriction that the given buffer
* has to contain a valid path name. */
char *x = current_bar.id + strlen("bar-");
while (*x != '\0') {
*(x++) = (rand() % 26) + 'a';
}
/* Copy the current (static) structure into a dynamically allocated
* one, then cleanup our static one. */
Barconfig *bar_config = scalloc(sizeof(Barconfig));
memcpy(bar_config, &current_bar, sizeof(Barconfig));
TAILQ_INSERT_TAIL(&barconfigs, bar_config, configs);
memset(&current_bar, '\0', sizeof(Barconfig));
}
;
barlines:
/* empty */
| barlines barline
;
barline:
comment
| bar_status_command
| bar_output
| bar_tray_output
| bar_position
| bar_mode
| bar_font
| bar_workspace_buttons
| bar_verbose
| bar_socket_path
| bar_colors
| bar_color_background
| bar_color_statusline
| bar_color_focused_workspace
| bar_color_active_workspace
| bar_color_inactive_workspace
| bar_color_urgent_workspace
;
bar_status_command:
TOK_BAR_STATUS_COMMAND STR
{
DLOG("should add status command %s\n", $2);
FREE(current_bar.status_command);
current_bar.status_command = $2;
}
;
bar_output:
TOK_BAR_OUTPUT STR
{
DLOG("bar output %s\n", $2);
int new_outputs = current_bar.num_outputs + 1;
current_bar.outputs = srealloc(current_bar.outputs, sizeof(char*) * new_outputs);
current_bar.outputs[current_bar.num_outputs] = $2;
current_bar.num_outputs = new_outputs;
}
;
bar_tray_output:
TOK_BAR_TRAY_OUTPUT STR
{
DLOG("tray %s\n", $2);
FREE(current_bar.tray_output);
current_bar.tray_output = $2;
}
;
bar_position:
TOK_BAR_POSITION bar_position_position
{
DLOG("position %d\n", $2);
current_bar.position = $2;
}
;
bar_position_position:
TOK_BAR_TOP { $$ = P_TOP; }
| TOK_BAR_BOTTOM { $$ = P_BOTTOM; }
;
bar_mode:
TOK_BAR_MODE bar_mode_mode
{
DLOG("mode %d\n", $2);
current_bar.mode = $2;
}
;
bar_mode_mode:
TOK_BAR_HIDE { $$ = M_HIDE; }
| TOK_BAR_DOCK { $$ = M_DOCK; }
;
bar_font:
TOK_BAR_FONT STR
{
DLOG("font %s\n", $2);
FREE(current_bar.font);
current_bar.font = $2;
}
;
bar_workspace_buttons:
TOK_BAR_WORKSPACE_BUTTONS bool
{
DLOG("workspace_buttons = %d\n", $2);
/* We store this inverted to make the default setting right when
* initializing the struct with zero. */
current_bar.hide_workspace_buttons = !($2);
}
;
bar_verbose:
TOK_BAR_VERBOSE bool
{
DLOG("verbose = %d\n", $2);
current_bar.verbose = $2;
}
;
bar_socket_path:
TOK_BAR_SOCKET_PATH STR
{
DLOG("socket_path = %s\n", $2);
FREE(current_bar.socket_path);
current_bar.socket_path = $2;
}
;
bar_colors:
TOK_BAR_COLORS '{' barlines '}'
{
/* At the moment, the TOK_BAR_COLORS token is only to make the config
* friendlier for humans. We might change this in the future if it gets
* more complex. */
}
;
bar_color_background:
TOK_BAR_COLOR_BACKGROUND HEXCOLOR
{
DLOG("background = %s\n", $2);
current_bar.colors.background = $2;
}
;
bar_color_statusline:
TOK_BAR_COLOR_STATUSLINE HEXCOLOR
{
DLOG("statusline = %s\n", $2);
current_bar.colors.statusline = $2;
}
;
bar_color_focused_workspace:
TOK_BAR_COLOR_FOCUSED_WORKSPACE HEXCOLOR HEXCOLOR
{
DLOG("focused_ws = %s and %s\n", $2, $3);
current_bar.colors.focused_workspace_text = $2;
current_bar.colors.focused_workspace_bg = $3;
}
;
bar_color_active_workspace:
TOK_BAR_COLOR_ACTIVE_WORKSPACE HEXCOLOR HEXCOLOR
{
DLOG("active_ws = %s and %s\n", $2, $3);
current_bar.colors.active_workspace_text = $2;
current_bar.colors.active_workspace_bg = $3;
}
;
bar_color_inactive_workspace:
TOK_BAR_COLOR_INACTIVE_WORKSPACE HEXCOLOR HEXCOLOR
{
DLOG("inactive_ws = %s and %s\n", $2, $3);
current_bar.colors.inactive_workspace_text = $2;
current_bar.colors.inactive_workspace_bg = $3;
}
;
bar_color_urgent_workspace:
TOK_BAR_COLOR_URGENT_WORKSPACE HEXCOLOR HEXCOLOR
{
DLOG("urgent_ws = %s and %s\n", $2, $3);
current_bar.colors.urgent_workspace_text = $2;
current_bar.colors.urgent_workspace_bg = $3;
}
;
floating_modifier: floating_modifier:
TOKFLOATING_MODIFIER binding_modifiers TOKFLOATING_MODIFIER binding_modifiers
{ {
@ -1126,7 +1409,7 @@ assign:
ELOG("You are using the old assign syntax (without criteria). " ELOG("You are using the old assign syntax (without criteria). "
"Please see the User's Guide for the new syntax and fix " "Please see the User's Guide for the new syntax and fix "
"your config file.\n"); "your config file.\n");
context->has_errors = true; context->has_warnings = true;
printf("assignment of %s to *%s*\n", $2, $3); printf("assignment of %s to *%s*\n", $2, $3);
char *workspace = $3; char *workspace = $3;
char *criteria = $2; char *criteria = $2;
@ -1275,14 +1558,10 @@ color:
; ;
colorpixel: colorpixel:
'#' HEX HEXCOLOR
{ {
char *hex; $$ = get_colorpixel($1);
if (asprintf(&hex, "#%s", $2) == -1) free($1);
die("asprintf()");
free($2);
$$ = get_colorpixel(hex);
free(hex);
} }
; ;

View File

@ -21,6 +21,7 @@
char *current_configpath = NULL; char *current_configpath = NULL;
Config config; Config config;
struct modes_head modes; struct modes_head modes;
struct barconfig_head barconfigs = TAILQ_HEAD_INITIALIZER(barconfigs);
/** /**
* Ungrabs all keys, to be called before re-grabbing the keys because of a * Ungrabs all keys, to be called before re-grabbing the keys because of a
@ -294,6 +295,32 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
FREE(assign); FREE(assign);
} }
/* Clear bar configs */
Barconfig *barconfig;
while (!TAILQ_EMPTY(&barconfigs)) {
barconfig = TAILQ_FIRST(&barconfigs);
FREE(barconfig->id);
for (int c = 0; c < barconfig->num_outputs; c++)
free(barconfig->outputs[c]);
FREE(barconfig->outputs);
FREE(barconfig->tray_output);
FREE(barconfig->socket_path);
FREE(barconfig->status_command);
FREE(barconfig->font);
FREE(barconfig->colors.background);
FREE(barconfig->colors.statusline);
FREE(barconfig->colors.focused_workspace_text);
FREE(barconfig->colors.focused_workspace_bg);
FREE(barconfig->colors.active_workspace_text);
FREE(barconfig->colors.active_workspace_bg);
FREE(barconfig->colors.inactive_workspace_text);
FREE(barconfig->colors.inactive_workspace_bg);
FREE(barconfig->colors.urgent_workspace_text);
FREE(barconfig->colors.urgent_workspace_bg);
TAILQ_REMOVE(&barconfigs, barconfig, configs);
FREE(barconfig);
}
/* Clear workspace names */ /* Clear workspace names */
#if 0 #if 0
Workspace *ws; Workspace *ws;

142
src/ipc.c
View File

@ -475,6 +475,143 @@ IPC_HANDLER(get_marks) {
y(free); y(free);
} }
/*
* Formats the reply message for a GET_BAR_CONFIG request and sends it to the
* client.
*
*/
IPC_HANDLER(get_bar_config) {
#if YAJL_MAJOR >= 2
yajl_gen gen = yajl_gen_alloc(NULL);
#else
yajl_gen gen = yajl_gen_alloc(NULL, NULL);
#endif
/* If no ID was passed, we return a JSON array with all IDs */
if (message_size == 0) {
y(array_open);
Barconfig *current;
TAILQ_FOREACH(current, &barconfigs, configs) {
ystr(current->id);
}
y(array_close);
const unsigned char *payload;
#if YAJL_MAJOR >= 2
size_t length;
#else
unsigned int length;
#endif
y(get_buf, &payload, &length);
ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_BAR_CONFIG, payload);
y(free);
return;
}
/* To get a properly terminated buffer, we copy
* message_size bytes out of the buffer */
char *bar_id = scalloc(message_size + 1);
strncpy(bar_id, (const char*)message, message_size);
LOG("IPC: looking for config for bar ID \"%s\"\n", bar_id);
Barconfig *current, *config = NULL;
TAILQ_FOREACH(current, &barconfigs, configs) {
if (strcmp(current->id, bar_id) != 0)
continue;
config = current;
break;
}
y(map_open);
if (!config) {
/* If we did not find a config for the given ID, the reply will contain
* a null 'id' field. */
ystr("id");
y(null);
} else {
ystr("id");
ystr(config->id);
if (config->num_outputs > 0) {
ystr("outputs");
y(array_open);
for (int c = 0; c < config->num_outputs; c++)
ystr(config->outputs[c]);
y(array_close);
}
#define YSTR_IF_SET(name) \
do { \
if (config->name) { \
ystr( # name); \
ystr(config->name); \
} \
} while (0)
YSTR_IF_SET(tray_output);
YSTR_IF_SET(socket_path);
ystr("mode");
if (config->mode == M_HIDE)
ystr("hide");
else ystr("dock");
ystr("position");
if (config->position == P_BOTTOM)
ystr("bottom");
else ystr("top");
YSTR_IF_SET(status_command);
YSTR_IF_SET(font);
ystr("workspace_buttons");
y(bool, !config->hide_workspace_buttons);
ystr("verbose");
y(bool, config->verbose);
#undef YSTR_IF_SET
#define YSTR_IF_SET(name) \
do { \
if (config->colors.name) { \
ystr( # name); \
ystr(config->colors.name); \
} \
} while (0)
ystr("colors");
y(map_open);
YSTR_IF_SET(background);
YSTR_IF_SET(statusline);
YSTR_IF_SET(focused_workspace_text);
YSTR_IF_SET(focused_workspace_bg);
YSTR_IF_SET(active_workspace_text);
YSTR_IF_SET(active_workspace_bg);
YSTR_IF_SET(inactive_workspace_text);
YSTR_IF_SET(inactive_workspace_bg);
YSTR_IF_SET(urgent_workspace_text);
YSTR_IF_SET(urgent_workspace_bg);
y(map_close);
#undef YSTR_IF_SET
}
y(map_close);
const unsigned char *payload;
#if YAJL_MAJOR >= 2
size_t length;
#else
unsigned int length;
#endif
y(get_buf, &payload, &length);
ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_BAR_CONFIG, payload);
y(free);
}
/* /*
* Callback for the YAJL parser (will be called when a string is parsed). * Callback for the YAJL parser (will be called when a string is parsed).
* *
@ -560,13 +697,14 @@ IPC_HANDLER(subscribe) {
/* The index of each callback function corresponds to the numeric /* The index of each callback function corresponds to the numeric
* value of the message type (see include/i3/ipc.h) */ * value of the message type (see include/i3/ipc.h) */
handler_t handlers[6] = { handler_t handlers[7] = {
handle_command, handle_command,
handle_get_workspaces, handle_get_workspaces,
handle_subscribe, handle_subscribe,
handle_get_outputs, handle_get_outputs,
handle_tree, handle_tree,
handle_get_marks handle_get_marks,
handle_get_bar_config
}; };
/* /*

View File

@ -61,6 +61,11 @@ struct ws_assignments_head ws_assignments = TAILQ_HEAD_INITIALIZER(ws_assignment
bool xcursor_supported = true; bool xcursor_supported = true;
bool xkb_supported = true; bool xkb_supported = true;
/* This will be set to true when -C is used so that functions can behave
* slightly differently. We dont want i3-nagbar to be started when validating
* the config, for example. */
bool only_check_config = false;
/* /*
* This callback is only a dummy, see xcb_prepare_cb and xcb_check_cb. * This callback is only a dummy, see xcb_prepare_cb and xcb_check_cb.
* See also man libev(3): "ev_prepare" and "ev_check" - customise your event loop * See also man libev(3): "ev_prepare" and "ev_check" - customise your event loop
@ -194,7 +199,6 @@ int main(int argc, char *argv[]) {
bool autostart = true; bool autostart = true;
char *layout_path = NULL; char *layout_path = NULL;
bool delete_layout_path = false; bool delete_layout_path = false;
bool only_check_config = false;
bool force_xinerama = false; bool force_xinerama = false;
bool disable_signalhandler = false; bool disable_signalhandler = false;
static struct option long_options[] = { static struct option long_options[] = {
@ -217,6 +221,8 @@ int main(int argc, char *argv[]) {
if (!isatty(fileno(stdout))) if (!isatty(fileno(stdout)))
setbuf(stdout, NULL); setbuf(stdout, NULL);
srand(time(NULL));
init_logging(); init_logging();
start_argv = argv; start_argv = argv;
@ -631,6 +637,17 @@ int main(int argc, char *argv[]) {
start_application(exec_always->command); start_application(exec_always->command);
} }
/* Start i3bar processes for all configured bars */
Barconfig *barconfig;
TAILQ_FOREACH(barconfig, &barconfigs, configs) {
char *command = NULL;
asprintf(&command, "i3bar --bar_id=%s --socket=\"%s\"",
barconfig->id, current_socketpath);
LOG("Starting bar process: %s\n", command);
start_application(command);
free(command);
}
/* Make sure to destroy the event loop to invoke the cleeanup callbacks /* Make sure to destroy the event loop to invoke the cleeanup callbacks
* when calling exit() */ * when calling exit() */
atexit(i3_exit); atexit(i3_exit);

View File

@ -8,7 +8,7 @@ WriteMakefile(
MIN_PERL_VERSION => '5.010000', # 5.10.0 MIN_PERL_VERSION => '5.010000', # 5.10.0
PREREQ_PM => { PREREQ_PM => {
'AnyEvent' => 0, 'AnyEvent' => 0,
'AnyEvent::I3' => '0.08', 'AnyEvent::I3' => '0.09',
'X11::XCB' => '0.03', 'X11::XCB' => '0.03',
'Test::Most' => 0, 'Test::Most' => 0,
'Test::Deep' => 0, 'Test::Deep' => 0,

View File

@ -54,6 +54,14 @@ sub activate_i3 {
$ENV{LISTEN_PID} = $$; $ENV{LISTEN_PID} = $$;
$ENV{LISTEN_FDS} = 1; $ENV{LISTEN_FDS} = 1;
$ENV{DISPLAY} = $args{display}; $ENV{DISPLAY} = $args{display};
$ENV{PATH} = join(':',
'../i3-nagbar',
'../i3-msg',
'../i3-config-wizard',
'../i3bar',
'..',
$ENV{PATH}
);
# Only pass file descriptors 0 (stdin), 1 (stdout), 2 (stderr) and # Only pass file descriptors 0 (stdin), 1 (stdout), 2 (stderr) and
# 3 (socket) to the child. # 3 (socket) to the child.
$^F = 3; $^F = 3;

View File

@ -0,0 +1,173 @@
#!perl
# vim:ts=4:sw=4:expandtab
# !NO_I3_INSTANCE! will prevent complete-run.pl from starting i3
#
# Checks that the bar config is parsed correctly.
#
use i3test;
#####################################################################
# test a config without any bars
#####################################################################
my $config = <<EOT;
# i3 config file (v4)
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
EOT
my $pid = launch_with_config($config);
my $i3 = i3(get_socket_path(0));
my $bars = $i3->get_bar_config()->recv;
is(@$bars, 0, 'no bars configured');
exit_gracefully($pid);
#####################################################################
# now provide a simple bar configuration
#####################################################################
$config = <<EOT;
# i3 config file (v4)
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
bar {
# Start a default instance of i3bar which provides workspace buttons.
# Additionally, i3status will provide a statusline.
status_command i3status --foo
}
EOT
$pid = launch_with_config($config);
$i3 = i3(get_socket_path(0));
$bars = $i3->get_bar_config()->recv;
is(@$bars, 1, 'one bar configured');
my $bar_id = shift @$bars;
my $bar_config = $i3->get_bar_config($bar_id)->recv;
is($bar_config->{status_command}, 'i3status --foo', 'status_command correct');
ok(!$bar_config->{verbose}, 'verbose off by default');
ok($bar_config->{workspace_buttons}, 'workspace buttons enabled per default');
is($bar_config->{mode}, 'dock', 'dock mode by default');
is($bar_config->{position}, 'bottom', 'position bottom by default');
#####################################################################
# ensure that reloading cleans up the old bar configs
#####################################################################
cmd 'reload';
$bars = $i3->get_bar_config()->recv;
is(@$bars, 1, 'still one bar configured');
exit_gracefully($pid);
#####################################################################
# validate a more complex configuration
#####################################################################
$config = <<EOT;
# i3 config file (v4)
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
bar {
# Start a default instance of i3bar which provides workspace buttons.
# Additionally, i3status will provide a statusline.
status_command i3status --bar
output HDMI1
output HDMI2
tray_output LVDS1
tray_output HDMI2
position top
mode dock
font Terminus
workspace_buttons no
verbose yes
socket_path /tmp/foobar
colors {
background #ff0000
statusline #00ff00
focused_workspace #ffffff #285577
active_workspace #888888 #222222
inactive_workspace #888888 #222222
urgent_workspace #ffffff #900000
}
}
EOT
$pid = launch_with_config($config);
$i3 = i3(get_socket_path(0));
$bars = $i3->get_bar_config()->recv;
is(@$bars, 1, 'one bar configured');
$bar_id = shift @$bars;
$bar_config = $i3->get_bar_config($bar_id)->recv;
is($bar_config->{status_command}, 'i3status --bar', 'status_command correct');
ok($bar_config->{verbose}, 'verbose on');
ok(!$bar_config->{workspace_buttons}, 'workspace buttons disabled');
is($bar_config->{mode}, 'dock', 'dock mode');
is($bar_config->{position}, 'top', 'position top');
is_deeply($bar_config->{outputs}, [ 'HDMI1', 'HDMI2' ], 'outputs ok');
is($bar_config->{tray_output}, 'HDMI2', 'tray_output ok');
is($bar_config->{font}, 'Terminus', 'font ok');
is($bar_config->{socket_path}, '/tmp/foobar', 'socket_path ok');
is_deeply($bar_config->{colors},
{
background => 'ff0000',
statusline => '00ff00',
focused_workspace_text => 'ffffff',
focused_workspace_bg => '285577',
active_workspace_text => '888888',
active_workspace_bg => '222222',
inactive_workspace_text => '888888',
inactive_workspace_bg => '222222',
urgent_workspace_text => 'ffffff',
urgent_workspace_bg => '900000',
}, 'colors ok');
exit_gracefully($pid);
#####################################################################
# ensure that multiple bars get different IDs
#####################################################################
$config = <<EOT;
# i3 config file (v4)
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
bar {
# Start a default instance of i3bar which provides workspace buttons.
# Additionally, i3status will provide a statusline.
status_command i3status --bar
output HDMI1
}
bar {
output VGA1
}
EOT
$pid = launch_with_config($config);
$i3 = i3(get_socket_path(0));
$bars = $i3->get_bar_config()->recv;
is(@$bars, 2, 'two bars configured');
isnt($bars->[0], $bars->[1], 'bar IDs are different');
my $bar1_config = $i3->get_bar_config($bars->[0])->recv;
my $bar2_config = $i3->get_bar_config($bars->[1])->recv;
isnt($bar1_config->{outputs}, $bar2_config->{outputs}, 'outputs different');
exit_gracefully($pid);
done_testing;