Merge branch 'next'
This commit is contained in:
commit
d6480be215
4
Makefile
4
Makefile
|
@ -18,7 +18,7 @@ else
|
||||||
UNUSED:=$(shell $(MAKE) loglevels.h)
|
UNUSED:=$(shell $(MAKE) loglevels.h)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
SUBDIRS=i3-msg i3-input i3-nagbar i3-config-wizard
|
SUBDIRS=i3-msg i3-input i3-nagbar i3-config-wizard i3bar
|
||||||
|
|
||||||
# Depend on the specific file (.c for each .o) and on all headers
|
# Depend on the specific file (.c for each .o) and on all headers
|
||||||
src/%.o: src/%.c ${HEADERS}
|
src/%.o: src/%.c ${HEADERS}
|
||||||
|
@ -114,7 +114,7 @@ dist: distclean
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f src/*.o src/*.gcno src/cfgparse.tab.{c,h} src/cfgparse.yy.c src/cfgparse.{output,dot} src/cmdparse.tab.{c,h} src/cmdparse.yy.c src/cmdparse.{output,dot} loglevels.tmp include/loglevels.h
|
rm -f src/*.o src/*.gcno src/cfgparse.tab.{c,h} src/cfgparse.yy.c src/cfgparse.{output,dot} src/cmdparse.tab.{c,h} src/cmdparse.yy.c src/cmdparse.{output,dot} loglevels.tmp include/loglevels.h
|
||||||
(which lcov >/dev/null && lcov -d . --zerocounters) || true
|
(which lcov >/dev/null 2>&1 && lcov -d . --zerocounters) || true
|
||||||
$(MAKE) -C docs clean
|
$(MAKE) -C docs clean
|
||||||
$(MAKE) -C man clean
|
$(MAKE) -C man clean
|
||||||
for dir in $(SUBDIRS); do \
|
for dir in $(SUBDIRS); do \
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
|
||||||
|
┌─────────────────────────────┐
|
||||||
|
│ Release notes for i3 v4.0.1 │
|
||||||
|
└─────────────────────────────┘
|
||||||
|
|
||||||
|
This is the second release of the new major version of i3, v4.0.1. It has been
|
||||||
|
a long time since v3.ε was released (over one year). A lot has been happening
|
||||||
|
since then, we made 736 commits – compare that to the total number of 1664
|
||||||
|
commits for i3.
|
||||||
|
|
||||||
|
The reason for the high number of commits and long time for this release is the
|
||||||
|
big refactoring we have been doing. Instead of using several lists and a table
|
||||||
|
as data structures, we now use a single tree of containers. These containers
|
||||||
|
represent invisible entities like your X11 root window, your different monitors
|
||||||
|
and workspaces, but also visible entities like actual windows.
|
||||||
|
|
||||||
|
Using a tree has made a lot of things cleaner and easier – in the code *and* in
|
||||||
|
the user interface. Admittedly though, you will probably need a day or two to
|
||||||
|
get used to a few more advanced movement commands if you are used to v3.ε right
|
||||||
|
now.
|
||||||
|
|
||||||
|
┌────────────────────────────┐
|
||||||
|
│ New features in v4.0.1 │
|
||||||
|
└────────────────────────────┘
|
||||||
|
|
||||||
|
• Fix the build process of i3bar
|
||||||
|
• Fix the build process on Mac OS X
|
||||||
|
• i3-config-wizard: also start i3bar in the keycode config template
|
||||||
|
• userguide: Remove the obsolete bar.* colors
|
||||||
|
• userguide: Use i3bar instead of dzen2 in the 'exec' example
|
||||||
|
|
||||||
|
┌────────────────────────────┐
|
||||||
|
│ New features in v4.0 │
|
||||||
|
└────────────────────────────┘
|
||||||
|
|
||||||
|
• In addition to the proper flex/bison based parser for the config file
|
||||||
|
introduced in 3.δ, we now also have a flex/bison parser for commands. What
|
||||||
|
this means is that we can have more human-readable, beautiful command names
|
||||||
|
instead of cryptic commands like 'f' for fullscreen or 'mh' for move left.
|
||||||
|
In fact, the commands for the aforementioned functions *are* 'fullscreen'
|
||||||
|
and 'move left'!
|
||||||
|
|
||||||
|
• You can now chain commands using ';' (a semicolon). One example for that is
|
||||||
|
'workspace 3 ; exec /usr/bin/urxvt' to switch to a new workspace and open a
|
||||||
|
terminal.
|
||||||
|
|
||||||
|
• You can specify which windows should be affected by your command by using
|
||||||
|
different criteria. A good example is '[class="Firefox"] kill' to get rid
|
||||||
|
of all Firefox windows.
|
||||||
|
|
||||||
|
• As the configuration file needs new commands (and a few options are
|
||||||
|
obsolete), you need to change it. To make this process a little bit easier
|
||||||
|
for you, this release comes with the script i3-migrate-config-to-v4. Just
|
||||||
|
run it on your current config file and it will spit out a v4 config file to
|
||||||
|
stdout. To make things even better, i3 automatically detects v3 config files
|
||||||
|
and calls that script, so you never end up with a non-working config :).
|
||||||
|
|
||||||
|
• Similarly to the criteria when using commands, we now have a 'for_window'
|
||||||
|
configuration directive, which lets you automatically apply certain commands
|
||||||
|
to certain windows. Use it to set border styles per window, for example with
|
||||||
|
'for_window [class="XTerm"] border 1pixel'.
|
||||||
|
|
||||||
|
• Since dock clients (like dzen2) are now part of the layout tree (as opposed
|
||||||
|
to a custom data structure as before), it was easy to implement top and
|
||||||
|
bottom dock areas. Programs which properly specify the dock hint get placed
|
||||||
|
on the edge of the screen they request. i3bar has the -dtop and -dbottom
|
||||||
|
parameters, for example.
|
||||||
|
|
||||||
|
• The internal workspace bar is obsolete. Use i3bar instead.
|
||||||
|
|
||||||
|
• Resizing now works between all windows!
|
||||||
|
|
||||||
|
• Fullscreen now works for everything!
|
||||||
|
|
||||||
|
• Floating now works for everything!
|
||||||
|
|
||||||
|
• Your layout is now preserved when doing an inplace restart.
|
||||||
|
|
||||||
|
• When you have an error in your config file, a new program called i3-nagbar
|
||||||
|
will tell you so. It offers you two buttons: One to view the error in your
|
||||||
|
$PAGER and one to edit your config in your $EDITOR.
|
||||||
|
|
||||||
|
• The default config used key symbols (like 'bind Mod1+f fullscreen') instead
|
||||||
|
of key codes. If you use a non-qwerty layout, the program i3-config-wizard
|
||||||
|
can create a key symbol based config file based on your current layout. You
|
||||||
|
can also chose between Windows (Mod4) and Alt (Mod1) as your default
|
||||||
|
modifier. i3-config-wizard will automatically be started as long as you
|
||||||
|
don’t have a configuration file for i3.
|
||||||
|
|
||||||
|
• Custom X cursor themes are now supported.
|
||||||
|
|
||||||
|
• The RandR backend now respects the primary output.
|
||||||
|
|
||||||
|
• A wrong 'font' configuration in your config file will no longer make i3
|
||||||
|
exit. Instead, it will fall back to a different font and tell you about the
|
||||||
|
error in its log.
|
||||||
|
|
||||||
|
• The default split direction (whether a new window gets placed right next to
|
||||||
|
the current one or below the current one) is now automatically set to
|
||||||
|
horizontal if you have a monitor that is wider than high or vertical if you
|
||||||
|
a monitor which is higher than wide. This works great with rotated monitors.
|
||||||
|
|
||||||
|
• Sockets and temporary files are now placed in XDG_RUNTIME_DIR, if set (this
|
||||||
|
is used on systemd based systems).
|
||||||
|
|
||||||
|
• Tools like i3bar, i3-msg etc. use the I3_SOCKET_PATH property which is set
|
||||||
|
to the X11 root window, so you don’t have to configure your socket path
|
||||||
|
anywhere.
|
||||||
|
|
||||||
|
• The kill command kills single windows by default now. To kill a whole
|
||||||
|
application, use 'kill client'.
|
||||||
|
|
||||||
|
• IPC: Commands can now have custom replies. When the parser encounters an
|
||||||
|
error, a proper error reply is sent.
|
||||||
|
|
||||||
|
• There is now an 'exec_always' configuration directive which works like
|
||||||
|
'exec' but will also be run when restarting.
|
||||||
|
|
||||||
|
┌────────────────────────────┐
|
||||||
|
│ Future features │
|
||||||
|
└────────────────────────────┘
|
||||||
|
|
||||||
|
Our plans were big but our time and manpower is limited. Therefore, the
|
||||||
|
following features did not make it into this release. However, the foundation
|
||||||
|
is now in place and implementing them is possible, so stay tuned!
|
||||||
|
|
||||||
|
• Saving/Restoring specific parts of your layout
|
||||||
|
|
||||||
|
• Session saving
|
||||||
|
|
||||||
|
• Sticky windows
|
||||||
|
|
||||||
|
┌────────────────────────────┐
|
||||||
|
│ Thanks! │
|
||||||
|
└────────────────────────────┘
|
||||||
|
|
||||||
|
Thanks for testing, bugfixes, discussions and everything I forgot go out to:
|
||||||
|
|
||||||
|
aniou, artoj, badboy, cloud, cradle, David Coppa, dbp, dothebart, eeemsi,
|
||||||
|
eelvex, f8l, fernando, jan, jimdigriz, jon, julien, kacper, ktosiek,
|
||||||
|
lexszero, litemotiv, lourens, madroach, marcus, merovius, mike, mirko, mseed,
|
||||||
|
mxf, phnom, quaec, rogutes, sardemff7, smartass, thepub, tiago, tucos,
|
||||||
|
woddf2, xpt, ys
|
||||||
|
|
||||||
|
-- Michael Stapelberg, 2011-08-01
|
|
@ -71,6 +71,12 @@ LIBS += $(call ldflags_for_lib, x11, X11)
|
||||||
LIBS += $(call ldflags_for_lib, yajl, yajl)
|
LIBS += $(call ldflags_for_lib, yajl, yajl)
|
||||||
LIBS += $(call ldflags_for_lib, libev, ev)
|
LIBS += $(call ldflags_for_lib, libev, ev)
|
||||||
|
|
||||||
|
# Please test if -Wl,--as-needed works on your platform and send me a patch.
|
||||||
|
# it is known not to work on Darwin (Mac OS X)
|
||||||
|
ifneq (,$(filter Linux GNU GNU/%, $(UNAME)))
|
||||||
|
LDFLAGS += -Wl,--as-needed
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(UNAME),NetBSD)
|
ifeq ($(UNAME),NetBSD)
|
||||||
# We need -idirafter instead of -I to prefer the system’s iconv over GNU libiconv
|
# We need -idirafter instead of -I to prefer the system’s iconv over GNU libiconv
|
||||||
CFLAGS += -idirafter /usr/pkg/include
|
CFLAGS += -idirafter /usr/pkg/include
|
||||||
|
@ -93,7 +99,7 @@ endif
|
||||||
|
|
||||||
# Fallback for libyajl 1 which did not include yajl_version.h. We need
|
# Fallback for libyajl 1 which did not include yajl_version.h. We need
|
||||||
# YAJL_MAJOR from that file to decide which code path should be used.
|
# YAJL_MAJOR from that file to decide which code path should be used.
|
||||||
CFLAGS += -idirafter yajl-fallback
|
CFLAGS += -idirafter $(TOPDIR)/yajl-fallback
|
||||||
|
|
||||||
ifneq (,$(filter Linux GNU GNU/%, $(UNAME)))
|
ifneq (,$(filter Linux GNU GNU/%, $(UNAME)))
|
||||||
CPPFLAGS += -D_GNU_SOURCE
|
CPPFLAGS += -D_GNU_SOURCE
|
||||||
|
|
|
@ -26,7 +26,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, x11-utils
|
||||||
Provides: x-window-manager
|
Provides: x-window-manager
|
||||||
Suggests: rxvt-unicode | x-terminal-emulator
|
Suggests: rxvt-unicode | x-terminal-emulator
|
||||||
Recommends: xfonts-base, libanyevent-i3-perl, libanyevent-perl, libipc-run-perl
|
Recommends: xfonts-base, libanyevent-i3-perl, libanyevent-perl, libipc-run-perl
|
||||||
Description: an improved dynamic tiling window manager
|
Description: improved dynamic tiling window manager
|
||||||
Key features of i3 are correct implementation of Xinerama (workspaces are
|
Key features of i3 are correct implementation of Xinerama (workspaces are
|
||||||
assigned to virtual screens, i3 does the right thing when attaching new
|
assigned to virtual screens, i3 does the right thing when attaching new
|
||||||
monitors), XrandR support (not done yet), horizontal and vertical columns
|
monitors), XrandR support (not done yet), horizontal and vertical columns
|
||||||
|
|
|
@ -13,11 +13,16 @@ config.status: configure
|
||||||
dh_testdir
|
dh_testdir
|
||||||
touch $@
|
touch $@
|
||||||
|
|
||||||
build:
|
build: build-arch build-indep
|
||||||
|
|
||||||
|
build-arch: build-stamp
|
||||||
|
build-indep: build-stamp
|
||||||
|
|
||||||
|
build-stamp:
|
||||||
dh_testdir
|
dh_testdir
|
||||||
|
|
||||||
# Add here commands to compile the package.
|
# Add here commands to compile the package.
|
||||||
$(MAKE)
|
$(MAKE) TERM_EMU=x-terminal-emulator
|
||||||
$(MAKE) -C man
|
$(MAKE) -C man
|
||||||
$(MAKE) -C docs
|
$(MAKE) -C docs
|
||||||
|
|
||||||
|
@ -45,6 +50,9 @@ install: build
|
||||||
cp man/i3.1 $(CURDIR)/debian/i3-wm/usr/share/man/man1
|
cp man/i3.1 $(CURDIR)/debian/i3-wm/usr/share/man/man1
|
||||||
cp man/i3-msg.1 $(CURDIR)/debian/i3-wm/usr/share/man/man1
|
cp man/i3-msg.1 $(CURDIR)/debian/i3-wm/usr/share/man/man1
|
||||||
cp man/i3-input.1 $(CURDIR)/debian/i3-wm/usr/share/man/man1
|
cp man/i3-input.1 $(CURDIR)/debian/i3-wm/usr/share/man/man1
|
||||||
|
cp man/i3-nagbar.1 $(CURDIR)/debian/i3-wm/usr/share/man/man1
|
||||||
|
cp man/i3-config-wizard.1 $(CURDIR)/debian/i3-wm/usr/share/man/man1
|
||||||
|
cp man/i3-migrate-config-to-v4.1 $(CURDIR)/debian/i3-wm/usr/share/man/man1
|
||||||
|
|
||||||
|
|
||||||
# Build architecture-independent files here.
|
# Build architecture-independent files here.
|
||||||
|
|
|
@ -523,7 +523,7 @@ exec_always command
|
||||||
|
|
||||||
*Examples*:
|
*Examples*:
|
||||||
--------------------------------
|
--------------------------------
|
||||||
exec i3status | dzen2 -dock
|
exec i3status | i3bar -d
|
||||||
exec_always ~/my_script.sh
|
exec_always ~/my_script.sh
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
|
@ -572,12 +572,6 @@ client.unfocused::
|
||||||
A client which is not the focused one of its container.
|
A client which is not the focused one of its container.
|
||||||
client.urgent::
|
client.urgent::
|
||||||
A client which has its urgency hint activated.
|
A client which has its urgency hint activated.
|
||||||
bar.focused::
|
|
||||||
The current workspace in the bottom bar.
|
|
||||||
bar.unfocused::
|
|
||||||
All other workspaces in the bottom bar.
|
|
||||||
bar.urgent::
|
|
||||||
A workspace which has at least one client with an activated urgency hint.
|
|
||||||
|
|
||||||
You can also specify the color to be used to paint the background of the client
|
You can also specify the color to be used to paint the background of the client
|
||||||
windows. This color will be used to paint the window on top of which the client
|
windows. This color will be used to paint the window on top of which the client
|
||||||
|
|
|
@ -145,3 +145,7 @@ mode "resize" {
|
||||||
}
|
}
|
||||||
|
|
||||||
bindcode $mod+27 mode "resize"
|
bindcode $mod+27 mode "resize"
|
||||||
|
|
||||||
|
# Start i3bar to display a workspace bar (plus the system information i3status
|
||||||
|
# finds out, if available)
|
||||||
|
exec i3status | i3bar -d
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
i3bar
|
||||||
|
*.o
|
||||||
|
core
|
||||||
|
doc/i3bar.1
|
|
@ -0,0 +1,30 @@
|
||||||
|
v0.7
|
||||||
|
=====
|
||||||
|
- Make i3bar compatible with i3-4.0
|
||||||
|
- Implement disabling the workspace buttons
|
||||||
|
- Add Color for focused ws
|
||||||
|
- Add support for I3_SOCKET_PATH-atom
|
||||||
|
- Implement different dock-positions
|
||||||
|
- Hide-on-modifier is now the default behavior
|
||||||
|
- Change default socketpath to /tmp/i3-ipc.sock
|
||||||
|
- Use I3SOCK environment-variable
|
||||||
|
- Bugfix: Stop the reconn-timer before starting it again, else it's running twice
|
||||||
|
- Bugfix: Don't SIGSTOP child in dockmode
|
||||||
|
- Bugfix: If hide-on-modifier is set, stop the child after starting
|
||||||
|
- Bugfix: Recover from closed socket
|
||||||
|
- Some minor bugfixes
|
||||||
|
|
||||||
|
v0.6
|
||||||
|
=====
|
||||||
|
- Add manpage
|
||||||
|
- Implement hide-on-modifier
|
||||||
|
- Custom colors can be set from the commandline
|
||||||
|
- Use double-buffering
|
||||||
|
- Bugfix: Correctly render long text
|
||||||
|
- Bugfix: Don't segfault on SIGCHILD
|
||||||
|
- Bugfix: Double-fork() to avoid zombies
|
||||||
|
- Some minor bugfixes
|
||||||
|
|
||||||
|
v0.5
|
||||||
|
=====
|
||||||
|
- Initial release
|
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2010-2011, Axel Wagner
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* 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.
|
||||||
|
|
||||||
|
* Neither the name of Axel Wagner nor the
|
||||||
|
names of contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY Axel Wagner ''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 Axel Wagner 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.
|
|
@ -0,0 +1,39 @@
|
||||||
|
TOPDIR=..
|
||||||
|
|
||||||
|
include $(TOPDIR)/common.mk
|
||||||
|
|
||||||
|
FILES:=$(wildcard src/*.c)
|
||||||
|
FILES:=$(FILES:.c=.o)
|
||||||
|
HEADERS:=$(wildcard include/*.h)
|
||||||
|
|
||||||
|
CPPFLAGS += -I$(TOPDIR)/include
|
||||||
|
|
||||||
|
all: i3bar doc
|
||||||
|
|
||||||
|
i3bar: ${FILES}
|
||||||
|
echo "LINK"
|
||||||
|
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||||
|
|
||||||
|
doc:
|
||||||
|
echo ""
|
||||||
|
echo "SUBDIR doc"
|
||||||
|
$(MAKE) -C doc
|
||||||
|
|
||||||
|
src/%.o: src/%.c ${HEADERS}
|
||||||
|
echo "CC $<"
|
||||||
|
$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
install: all
|
||||||
|
echo "INSTALL"
|
||||||
|
$(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
|
||||||
|
$(INSTALL) -m 0755 i3bar $(DESTDIR)$(PREFIX)/bin
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f src/*.o
|
||||||
|
make -C doc clean
|
||||||
|
|
||||||
|
distclean: clean
|
||||||
|
rm -f i3bar
|
||||||
|
make -C doc distclean
|
||||||
|
|
||||||
|
.PHONY: install clean distclean doc
|
|
@ -0,0 +1,9 @@
|
||||||
|
all: i3bar.1
|
||||||
|
|
||||||
|
i3bar.1: i3bar.man
|
||||||
|
echo "A2X i3bar"
|
||||||
|
a2x --no-xmllint -f manpage i3bar.man
|
||||||
|
clean:
|
||||||
|
rm -f i3bar.xml i3bar.1 i3bar.html
|
||||||
|
|
||||||
|
distclean: clean
|
|
@ -0,0 +1,90 @@
|
||||||
|
i3bar(1)
|
||||||
|
========
|
||||||
|
Axel Wagner <mail+i3bar@merovius.de>
|
||||||
|
v0.7, July 2011
|
||||||
|
|
||||||
|
== NAME
|
||||||
|
|
||||||
|
i3bar - xcb-based status- and workspace-bar
|
||||||
|
|
||||||
|
== SYNOPSIS
|
||||||
|
|
||||||
|
*i3bar* [*-s* 'sock_path'] [*-c* 'command'] [*-m*|*-d*['pos']] [*-f* 'font'] [*-V*] [*-h*]
|
||||||
|
|
||||||
|
== OPTIONS
|
||||||
|
|
||||||
|
*-s, --socket* 'sock_path'::
|
||||||
|
Specifies the 'socketpath', via which *i3bar* connects to *i3*(1). If *i3bar* can not connect to *i3*, it will exit. Defaults to '/tmp/i3-ipc.sock'
|
||||||
|
|
||||||
|
*-c, --command* 'command'::
|
||||||
|
Execute '<command>' to get 'stdin'. You can also simply pipe into 'stdin', but starting the coomand for itself, *i3bar* is able to send 'SIGCONT' and 'SIGSTOP', when combined with *-m*
|
||||||
|
|
||||||
|
*-m, --hide*::
|
||||||
|
Hide the bar, when 'mod4' is not pressed. With this, dockmode will not be set, and the bar is out of the way most of the time so you have more room.
|
||||||
|
If *-c* is specified, the childprocess is sent a 'SIGSTOP' on hiding and a 'SIGCONT' on unhiding of the bars.
|
||||||
|
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*::
|
||||||
|
Display a short help-message and exit
|
||||||
|
|
||||||
|
== DESCRIPTION
|
||||||
|
|
||||||
|
*i3bar* is an xcb- and libev-based status- and ws-bar. It is best thought of as an replacement for the *i3-wsbar*(1) + *dzen2*(1)-combination. It creates a workspace-bar for every active output ("screen") and displays a piped in statusline rightaligned on every bar.
|
||||||
|
|
||||||
|
It does not sample any status-information itself, so you still need a program like *i3status*(1) or *conky*(1) for that.
|
||||||
|
|
||||||
|
i3bar does not support any color or other markups, so stdin should be plain utf8, one line at a time. If you use *i3status*(1), you therefore should specify 'output_format = none' in the general section of 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
|
||||||
|
|
||||||
|
=== I3SOCK
|
||||||
|
|
||||||
|
If no ipc-socket is specified on the commandline, this variable is used
|
||||||
|
to determine the path, at wich the unix domain socket is expected, on which
|
||||||
|
to connect to i3.
|
||||||
|
|
||||||
|
== EXAMPLES
|
||||||
|
|
||||||
|
To get a docked bar with some statusinformation, you use
|
||||||
|
|
||||||
|
*i3status | i3bar --dock*
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
+i3(1)+, +i3-wsbar(1)+, +dzen2(1)+, +i3status(1)+
|
||||||
|
|
||||||
|
== AUTHORS
|
||||||
|
|
||||||
|
Axel Wagner and contributors
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
|
*
|
||||||
|
* © 2010-2011 Axel Wagner and contributors
|
||||||
|
*
|
||||||
|
* See file LICNSE for license information
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef CHILD_H_
|
||||||
|
#define CHILD_H_
|
||||||
|
|
||||||
|
#define STDIN_CHUNK_SIZE 1024
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start a child-process with the specified command and reroute stdin.
|
||||||
|
* We actually start a $SHELL to execute the command so we don't have to care
|
||||||
|
* about arguments and such
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void start_child(char *command);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kill()s the child-prozess (if existend) and closes and
|
||||||
|
* free()s the stdin- and sigchild-watchers
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void kill_child();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sends a SIGSTOP to the child-process (if existent)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void stop_child();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sends a SIGCONT to the child-process (if existent)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void cont_child();
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
|
*
|
||||||
|
* © 2010-2011 Axel Wagner and contributors
|
||||||
|
*
|
||||||
|
* See file LICNSE for license information
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef COMMON_H_
|
||||||
|
#define COMMON_H_
|
||||||
|
|
||||||
|
typedef struct rect_t rect;
|
||||||
|
typedef int bool;
|
||||||
|
|
||||||
|
struct ev_loop* main_loop;
|
||||||
|
char *statusline;
|
||||||
|
char *statusline_buffer;
|
||||||
|
|
||||||
|
struct rect_t {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
int w;
|
||||||
|
int h;
|
||||||
|
};
|
||||||
|
|
||||||
|
#include "queue.h"
|
||||||
|
#include "child.h"
|
||||||
|
#include "ipc.h"
|
||||||
|
#include "outputs.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "workspaces.h"
|
||||||
|
#include "xcb.h"
|
||||||
|
#include "ucs2_to_utf8.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef CONFIG_H_
|
||||||
|
#define CONFIG_H_
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DOCKPOS_NONE = 0,
|
||||||
|
DOCKPOS_TOP,
|
||||||
|
DOCKPOS_BOT
|
||||||
|
} dockpos_t;
|
||||||
|
|
||||||
|
typedef struct config_t {
|
||||||
|
int hide_on_modifier;
|
||||||
|
dockpos_t dockpos;
|
||||||
|
int verbose;
|
||||||
|
xcb_colors_t *colors;
|
||||||
|
int disable_ws;
|
||||||
|
} config_t;
|
||||||
|
|
||||||
|
config_t config;
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
|
*
|
||||||
|
* © 2010-2011 Axel Wagner and contributors
|
||||||
|
*
|
||||||
|
* See file LICNSE for license information
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef IPC_H_
|
||||||
|
#define IPC_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initiate a connection to i3.
|
||||||
|
* socket-path must be a valid path to the ipc_socket of i3
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int init_connection(const char *socket_path);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Destroy the connection to i3.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void destroy_connection();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sends a Message to i3.
|
||||||
|
* type must be a valid I3_IPC_MESSAGE_TYPE (see i3/ipc.h for further information)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int i3_send_msg(uint32_t type, const char* payload);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Subscribe to all the i3-events, we need
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void subscribe_events();
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
|
*
|
||||||
|
* © 2010-2011 Axel Wagner and contributors
|
||||||
|
*
|
||||||
|
* See file LICNSE for license information
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef OUTPUTS_H_
|
||||||
|
#define OUTPUTS_H_
|
||||||
|
|
||||||
|
#include <xcb/xcb.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
typedef struct i3_output i3_output;
|
||||||
|
|
||||||
|
SLIST_HEAD(outputs_head, i3_output);
|
||||||
|
struct outputs_head *outputs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start parsing the received json-string
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void parse_outputs_json(char* json);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initiate the output-list
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void init_outputs();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the output with the given name
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
i3_output* get_output_by_name(char* name);
|
||||||
|
|
||||||
|
struct i3_output {
|
||||||
|
char* name; /* Name of the output */
|
||||||
|
bool active; /* If the output is active */
|
||||||
|
int ws; /* The number of the currently visible ws */
|
||||||
|
rect rect; /* The rect (relative to the root-win) */
|
||||||
|
|
||||||
|
xcb_window_t bar; /* The id of the bar of the output */
|
||||||
|
xcb_pixmap_t buffer; /* An extra pixmap for double-buffering */
|
||||||
|
xcb_gcontext_t bargc; /* The graphical context of the bar */
|
||||||
|
|
||||||
|
struct ws_head *workspaces; /* The workspaces on this output */
|
||||||
|
|
||||||
|
SLIST_ENTRY(i3_output) slist; /* Pointer for the SLIST-Macro */
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,527 @@
|
||||||
|
/* $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_ */
|
|
@ -0,0 +1 @@
|
||||||
|
char *convert_utf8_to_ucs2(char *input, int *real_strlen);
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
|
*
|
||||||
|
* © 2010-2011 Axel Wagner and contributors
|
||||||
|
*
|
||||||
|
* See file LICNSE for license information
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef UTIL_H_
|
||||||
|
#define UTIL_H_
|
||||||
|
|
||||||
|
#include "queue.h"
|
||||||
|
|
||||||
|
/* Get the maximum/minimum of x and y */
|
||||||
|
#define MAX(x,y) ((x) > (y) ? (x) : (y))
|
||||||
|
#define MIN(x,y) ((x) < (y) ? (x) : (y))
|
||||||
|
|
||||||
|
/* Securely free p */
|
||||||
|
#define FREE(p) do { \
|
||||||
|
if (p != NULL) { \
|
||||||
|
free(p); \
|
||||||
|
p = NULL; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* Securely fee single-linked list */
|
||||||
|
#define FREE_SLIST(l, type) do { \
|
||||||
|
type *walk = SLIST_FIRST(l); \
|
||||||
|
while (!SLIST_EMPTY(l)) { \
|
||||||
|
SLIST_REMOVE_HEAD(l, slist); \
|
||||||
|
FREE(walk); \
|
||||||
|
walk = SLIST_FIRST(l); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Securely fee tail-queues */
|
||||||
|
#define FREE_TAILQ(l, type) do { \
|
||||||
|
type *walk = TAILQ_FIRST(l); \
|
||||||
|
while (!TAILQ_EMPTY(l)) { \
|
||||||
|
TAILQ_REMOVE(l, TAILQ_FIRST(l), tailq); \
|
||||||
|
FREE(walk); \
|
||||||
|
walk = TAILQ_FIRST(l); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* Use cool logging-macros */
|
||||||
|
#define DLOG(fmt, ...) do { \
|
||||||
|
if (config.verbose) { \
|
||||||
|
printf("[%s:%d] " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define ELOG(fmt, ...) do { \
|
||||||
|
fprintf(stderr, "[%s:%d] ERROR: " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
|
||||||
|
} while(0)
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
|
*
|
||||||
|
* © 2010-2011 Axel Wagner and contributors
|
||||||
|
*
|
||||||
|
* See file LICNSE for license information
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef WORKSPACES_H_
|
||||||
|
#define WORKSPACES_H_
|
||||||
|
|
||||||
|
#include <xcb/xproto.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
typedef struct i3_ws i3_ws;
|
||||||
|
|
||||||
|
TAILQ_HEAD(ws_head, i3_ws);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start parsing the received json-string
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void parse_workspaces_json();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* free() all workspace data-structures
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void free_workspaces();
|
||||||
|
|
||||||
|
struct i3_ws {
|
||||||
|
int num; /* The internal number of the ws */
|
||||||
|
char *name; /* The name (in utf8) of the ws */
|
||||||
|
xcb_char2b_t *ucs2_name; /* The name (in ucs2) of the ws */
|
||||||
|
int name_glyphs; /* The length (in glyphs) of the name */
|
||||||
|
int name_width; /* The rendered width of the name */
|
||||||
|
bool visible; /* If the ws is currently visible on an output */
|
||||||
|
bool focused; /* If the ws is currently focused */
|
||||||
|
bool urgent; /* If the urgent-hint of the ws is set */
|
||||||
|
rect rect; /* The rect of the ws (not used (yet)) */
|
||||||
|
struct i3_output *output; /* The current output of the ws */
|
||||||
|
|
||||||
|
TAILQ_ENTRY(i3_ws) tailq; /* Pointer for the TAILQ-Macro */
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
|
*
|
||||||
|
* © 2010-2011 Axel Wagner and contributors
|
||||||
|
*
|
||||||
|
* See file LICNSE for license information
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef XCB_H_
|
||||||
|
#define XCB_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
//#include "outputs.h"
|
||||||
|
|
||||||
|
struct xcb_color_strings_t {
|
||||||
|
char *bar_fg;
|
||||||
|
char *bar_bg;
|
||||||
|
char *active_ws_fg;
|
||||||
|
char *active_ws_bg;
|
||||||
|
char *inactive_ws_fg;
|
||||||
|
char *inactive_ws_bg;
|
||||||
|
char *focus_ws_bg;
|
||||||
|
char *focus_ws_fg;
|
||||||
|
char *urgent_ws_bg;
|
||||||
|
char *urgent_ws_fg;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct xcb_colors_t xcb_colors_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize xcb and use the specified fontname for text-rendering
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
char *init_xcb();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize the colors
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void init_colors(const struct xcb_color_strings_t *colors);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cleanup the xcb-stuff.
|
||||||
|
* Called once, before the program terminates.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void clean_xcb();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the earlier requested atoms and save them in the prepared data-structure
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void get_atoms();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Destroy the bar of the specified output
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void destroy_window(i3_output *output);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reallocate the statusline-buffer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void realloc_sl_buffer();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reconfigure all bars and create new for newly activated outputs
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void reconfig_windows();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Render the bars, with buttons and statusline
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void draw_bars();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Redraw the bars, i.e. simply copy the buffer to the barwindow
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void redraw_bars();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Predicts the length of text based on cached data.
|
||||||
|
* The string has to be encoded in ucs2 and glyph_len has to be the length
|
||||||
|
* of the string (in glyphs).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
uint32_t predict_text_extents(xcb_char2b_t *text, uint32_t length);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,5 @@
|
||||||
|
ATOM_DO(_NET_WM_WINDOW_TYPE)
|
||||||
|
ATOM_DO(_NET_WM_WINDOW_TYPE_DOCK)
|
||||||
|
ATOM_DO(_NET_WM_STRUT_PARTIAL)
|
||||||
|
ATOM_DO(I3_SOCKET_PATH)
|
||||||
|
#undef ATOM_DO
|
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
|
*
|
||||||
|
* © 2010-2011 Axel Wagner and contributors
|
||||||
|
*
|
||||||
|
* See file LICNSE for license information
|
||||||
|
*
|
||||||
|
* src/child.c: Getting Input for the statusline
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <ev.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
/* Global variables for child_*() */
|
||||||
|
pid_t child_pid;
|
||||||
|
|
||||||
|
/* stdin- and sigchild-watchers */
|
||||||
|
ev_io *stdin_io;
|
||||||
|
ev_child *child_sig;
|
||||||
|
|
||||||
|
/* The buffer statusline points to */
|
||||||
|
char *statusline_buffer = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stop and free() the stdin- and sigchild-watchers
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void cleanup() {
|
||||||
|
if (stdin_io != NULL) {
|
||||||
|
ev_io_stop(main_loop, stdin_io);
|
||||||
|
FREE(stdin_io);
|
||||||
|
FREE(statusline_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (child_sig != NULL) {
|
||||||
|
ev_child_stop(main_loop, child_sig);
|
||||||
|
FREE(child_sig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callbalk for stdin. We read a line from stdin and store the result
|
||||||
|
* in statusline
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
|
||||||
|
int fd = watcher->fd;
|
||||||
|
int n = 0;
|
||||||
|
int rec = 0;
|
||||||
|
int buffer_len = STDIN_CHUNK_SIZE;
|
||||||
|
char *buffer = malloc(buffer_len);
|
||||||
|
buffer[0] = '\0';
|
||||||
|
while(1) {
|
||||||
|
n = read(fd, buffer + rec, buffer_len - rec);
|
||||||
|
if (n == -1) {
|
||||||
|
if (errno == EAGAIN) {
|
||||||
|
/* remove trailing newline and finish up */
|
||||||
|
buffer[rec-1] = '\0';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ELOG("read() failed!: %s\n", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
if (n == 0) {
|
||||||
|
if (rec != 0) {
|
||||||
|
/* remove trailing newline and finish up */
|
||||||
|
buffer[rec-1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* end of file, kill the watcher */
|
||||||
|
DLOG("stdin: EOF\n");
|
||||||
|
cleanup();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rec += n;
|
||||||
|
|
||||||
|
if (rec == buffer_len) {
|
||||||
|
buffer_len += STDIN_CHUNK_SIZE;
|
||||||
|
buffer = realloc(buffer, buffer_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (*buffer == '\0') {
|
||||||
|
FREE(buffer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FREE(statusline_buffer);
|
||||||
|
statusline = statusline_buffer = buffer;
|
||||||
|
for (n = 0; buffer[n] != '\0'; ++n) {
|
||||||
|
if (buffer[n] == '\n')
|
||||||
|
statusline = &buffer[n + 1];
|
||||||
|
}
|
||||||
|
DLOG("%s\n", statusline);
|
||||||
|
draw_bars();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We received a sigchild, meaning, that the child-process terminated.
|
||||||
|
* We simply free the respective data-structures and don't care for input
|
||||||
|
* anymore
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) {
|
||||||
|
DLOG("Child (pid: %d) unexpectedly exited with status %d\n",
|
||||||
|
child_pid,
|
||||||
|
watcher->rstatus);
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start a child-process with the specified command and reroute stdin.
|
||||||
|
* We actually start a $SHELL to execute the command so we don't have to care
|
||||||
|
* about arguments and such
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void start_child(char *command) {
|
||||||
|
child_pid = 0;
|
||||||
|
if (command != NULL) {
|
||||||
|
int fd[2];
|
||||||
|
pipe(fd);
|
||||||
|
child_pid = fork();
|
||||||
|
switch (child_pid) {
|
||||||
|
case -1:
|
||||||
|
ELOG("Couldn't fork(): %s\n", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
case 0:
|
||||||
|
/* Child-process. Reroute stdout and start shell */
|
||||||
|
close(fd[0]);
|
||||||
|
|
||||||
|
dup2(fd[1], STDOUT_FILENO);
|
||||||
|
|
||||||
|
static const char *shell = NULL;
|
||||||
|
|
||||||
|
if ((shell = getenv("SHELL")) == NULL)
|
||||||
|
shell = "/bin/sh";
|
||||||
|
|
||||||
|
execl(shell, shell, "-c", command, (char*) NULL);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
/* Parent-process. Rerout stdin */
|
||||||
|
close(fd[1]);
|
||||||
|
|
||||||
|
dup2(fd[0], STDIN_FILENO);
|
||||||
|
|
||||||
|
/* If hide-on-modifier is set, we start of by sending the
|
||||||
|
* child a SIGSTOP, because the bars aren't mapped at start */
|
||||||
|
if (config.hide_on_modifier) {
|
||||||
|
stop_child();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We set O_NONBLOCK because blocking is evil in event-driven software */
|
||||||
|
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
|
||||||
|
|
||||||
|
stdin_io = malloc(sizeof(ev_io));
|
||||||
|
ev_io_init(stdin_io, &stdin_io_cb, STDIN_FILENO, EV_READ);
|
||||||
|
ev_io_start(main_loop, stdin_io);
|
||||||
|
|
||||||
|
/* We must cleanup, if the child unexpectedly terminates */
|
||||||
|
child_sig = malloc(sizeof(ev_child));
|
||||||
|
ev_child_init(child_sig, &child_sig_cb, child_pid, 0);
|
||||||
|
ev_child_start(main_loop, child_sig);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kill()s the child-process (if existent) and closes and
|
||||||
|
* free()s the stdin- and sigchild-watchers
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void kill_child() {
|
||||||
|
if (child_pid != 0) {
|
||||||
|
kill(child_pid, SIGCONT);
|
||||||
|
kill(child_pid, SIGTERM);
|
||||||
|
int status;
|
||||||
|
waitpid(child_pid, &status, 0);
|
||||||
|
child_pid = 0;
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sends a SIGSTOP to the child-process (if existent)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void stop_child() {
|
||||||
|
if (child_pid != 0) {
|
||||||
|
kill(child_pid, SIGSTOP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sends a SIGCONT to the child-process (if existent)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void cont_child() {
|
||||||
|
if (child_pid != 0) {
|
||||||
|
kill(child_pid, SIGCONT);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,343 @@
|
||||||
|
/*
|
||||||
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
|
*
|
||||||
|
* © 2010-2011 Axel Wagner and contributors
|
||||||
|
*
|
||||||
|
* See file LICNSE for license information
|
||||||
|
*
|
||||||
|
* src/ipc.c: Communicating with i3
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <i3/ipc.h>
|
||||||
|
#include <ev.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
ev_io *i3_connection;
|
||||||
|
ev_timer *reconn = NULL;
|
||||||
|
|
||||||
|
const char *sock_path;
|
||||||
|
|
||||||
|
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.
|
||||||
|
* Since i3 does not give us much feedback on commands, we do not much
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void got_command_reply(char *reply) {
|
||||||
|
/* TODO: Error handling for command-replies */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called, when we get a reply with workspaces-data
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void got_workspace_reply(char *reply) {
|
||||||
|
DLOG("Got Workspace-Data!\n");
|
||||||
|
parse_workspaces_json(reply);
|
||||||
|
draw_bars();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called, when we get a reply for a subscription.
|
||||||
|
* Since i3 does not give us much feedback on commands, we do not much
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void got_subscribe_reply(char *reply) {
|
||||||
|
DLOG("Got Subscribe Reply: %s\n", reply);
|
||||||
|
/* TODO: Error handling for subscribe-commands */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called, when we get a reply with outputs-data
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void got_output_reply(char *reply) {
|
||||||
|
DLOG("Parsing Outputs-JSON...\n");
|
||||||
|
parse_outputs_json(reply);
|
||||||
|
DLOG("Reconfiguring Windows...\n");
|
||||||
|
realloc_sl_buffer();
|
||||||
|
reconfig_windows();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Data-structure to easily call the reply-handlers later */
|
||||||
|
handler_t reply_handlers[] = {
|
||||||
|
&got_command_reply,
|
||||||
|
&got_workspace_reply,
|
||||||
|
&got_subscribe_reply,
|
||||||
|
&got_output_reply,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called, when a workspace-event arrives (i.e. the user changed the workspace)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void got_workspace_event(char *event) {
|
||||||
|
DLOG("Got Workspace Event!\n");
|
||||||
|
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called, when an output-event arrives (i.e. the screen-configuration changed)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void got_output_event(char *event) {
|
||||||
|
DLOG("Got Output Event!\n");
|
||||||
|
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
|
||||||
|
if (!config.disable_ws) {
|
||||||
|
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Data-structure to easily call the reply-handlers later */
|
||||||
|
handler_t event_handlers[] = {
|
||||||
|
&got_workspace_event,
|
||||||
|
&got_output_event
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called, when we get a message from i3
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
|
||||||
|
DLOG("Got data!\n");
|
||||||
|
int fd = watcher->fd;
|
||||||
|
|
||||||
|
/* First we only read the header, because we know its length */
|
||||||
|
uint32_t header_len = strlen(I3_IPC_MAGIC) + sizeof(uint32_t)*2;
|
||||||
|
char *header = malloc(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 have to expect */
|
||||||
|
uint32_t rec = 0;
|
||||||
|
while (rec < header_len) {
|
||||||
|
int n = read(fd, header + rec, header_len - rec);
|
||||||
|
if (n == -1) {
|
||||||
|
ELOG("read() failed: %s\n", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
if (n == 0) {
|
||||||
|
/* 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 += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strncmp(header, I3_IPC_MAGIC, strlen(I3_IPC_MAGIC))) {
|
||||||
|
ELOG("Wrong magic code: %.*s\n Expected: %s\n",
|
||||||
|
(int) strlen(I3_IPC_MAGIC),
|
||||||
|
header,
|
||||||
|
I3_IPC_MAGIC);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *walk = header + strlen(I3_IPC_MAGIC);
|
||||||
|
uint32_t size;
|
||||||
|
memcpy(&size, (uint32_t*)walk, sizeof(uint32_t));
|
||||||
|
walk += sizeof(uint32_t);
|
||||||
|
uint32_t type;
|
||||||
|
memcpy(&type, (uint32_t*)walk, sizeof(uint32_t));
|
||||||
|
|
||||||
|
/* Now that we know, what to expect, we can start read()ing the rest
|
||||||
|
* of the message */
|
||||||
|
char *buffer = malloc(size + 1);
|
||||||
|
if (buffer == NULL) {
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
while (rec < size) {
|
||||||
|
int n = read(fd, buffer + rec, size - rec);
|
||||||
|
if (n == -1) {
|
||||||
|
ELOG("read() failed: %s\n", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
if (n == 0) {
|
||||||
|
ELOG("Nothing to read!\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
rec += n;
|
||||||
|
}
|
||||||
|
buffer[size] = '\0';
|
||||||
|
|
||||||
|
/* And call the callback (indexed by the type) */
|
||||||
|
if (type & (1 << 31)) {
|
||||||
|
type ^= 1 << 31;
|
||||||
|
event_handlers[type](buffer);
|
||||||
|
} else {
|
||||||
|
reply_handlers[type](buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
FREE(header);
|
||||||
|
FREE(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sends a Message to i3.
|
||||||
|
* type must be a valid I3_IPC_MESSAGE_TYPE (see i3/ipc.h for further information)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int i3_send_msg(uint32_t type, const char *payload) {
|
||||||
|
uint32_t len = 0;
|
||||||
|
if (payload != NULL) {
|
||||||
|
len = strlen(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We are a wellbehaved client and send a proper header first */
|
||||||
|
uint32_t to_write = strlen (I3_IPC_MAGIC) + sizeof(uint32_t)*2 + len;
|
||||||
|
/* TODO: I'm not entirely sure if this buffer really has to contain more
|
||||||
|
* than the pure header (why not just write() the payload from *payload?),
|
||||||
|
* but we leave it for now */
|
||||||
|
char *buffer = malloc(to_write);
|
||||||
|
if (buffer == NULL) {
|
||||||
|
ELOG("Could not allocate memory: %s\n", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *walk = buffer;
|
||||||
|
|
||||||
|
strncpy(buffer, I3_IPC_MAGIC, strlen(I3_IPC_MAGIC));
|
||||||
|
walk += strlen(I3_IPC_MAGIC);
|
||||||
|
memcpy(walk, &len, sizeof(uint32_t));
|
||||||
|
walk += sizeof(uint32_t);
|
||||||
|
memcpy(walk, &type, sizeof(uint32_t));
|
||||||
|
walk += sizeof(uint32_t);
|
||||||
|
|
||||||
|
if (payload != NULL)
|
||||||
|
strncpy(walk, payload, len);
|
||||||
|
|
||||||
|
uint32_t written = 0;
|
||||||
|
|
||||||
|
while (to_write > 0) {
|
||||||
|
int n = write(i3_connection->fd, buffer + written, to_write);
|
||||||
|
if (n == -1) {
|
||||||
|
ELOG("write() failed: %s\n", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
to_write -= n;
|
||||||
|
written += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
FREE(buffer);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initiate a connection to i3.
|
||||||
|
* socket-path must be a valid path to the ipc_socket of i3
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int init_connection(const char *socket_path) {
|
||||||
|
sock_path = socket_path;
|
||||||
|
int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
|
||||||
|
if (sockfd == -1) {
|
||||||
|
ELOG("Could not create Socket: %s\n", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_un addr;
|
||||||
|
memset(&addr, 0, sizeof(struct sockaddr_un));
|
||||||
|
addr.sun_family = AF_LOCAL;
|
||||||
|
strcpy(addr.sun_path, sock_path);
|
||||||
|
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));
|
||||||
|
reconnect();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
i3_connection = malloc(sizeof(ev_io));
|
||||||
|
if (i3_connection == NULL) {
|
||||||
|
ELOG("malloc() failed: %s\n", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
ev_io_init(i3_connection, &got_data, sockfd, EV_READ);
|
||||||
|
ev_io_start(main_loop, i3_connection);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Destroy the connection to i3.
|
||||||
|
*/
|
||||||
|
void destroy_connection() {
|
||||||
|
close(i3_connection->fd);
|
||||||
|
ev_io_stop(main_loop, i3_connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Subscribe to all the i3-events, we need
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void subscribe_events() {
|
||||||
|
if (config.disable_ws) {
|
||||||
|
i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"output\" ]");
|
||||||
|
} else {
|
||||||
|
i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"workspace\", \"output\" ]");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,315 @@
|
||||||
|
/*
|
||||||
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
|
*
|
||||||
|
* © 2010-2011 Axel Wagner and contributors
|
||||||
|
*
|
||||||
|
* See file LICNSE for license information
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <i3/ipc.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <ev.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <glob.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Glob path, i.e. expand ~
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
char *expand_path(char *path) {
|
||||||
|
static glob_t globbuf;
|
||||||
|
if (glob(path, GLOB_NOCHECK | GLOB_TILDE, NULL, &globbuf) < 0) {
|
||||||
|
ELOG("glob() failed\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
char *result = strdup(globbuf.gl_pathc > 0 ? globbuf.gl_pathv[0] : path);
|
||||||
|
if (result == NULL) {
|
||||||
|
ELOG("malloc() failed: %s\n", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
globfree(&globbuf);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void read_color(char **color) {
|
||||||
|
int len = strlen(optarg);
|
||||||
|
if (len == 6 || (len == 7 && optarg[0] == '#')) {
|
||||||
|
int offset = len - 6;
|
||||||
|
int good = 1, i;
|
||||||
|
for (i = offset; good && i < 6 + offset; ++i) {
|
||||||
|
char c = optarg[i];
|
||||||
|
if (!(c >= 'a' && c <= 'f')
|
||||||
|
&& !(c >= 'A' && c <= 'F')
|
||||||
|
&& !(c >= '0' && c <= '9')) {
|
||||||
|
good = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (good) {
|
||||||
|
*color = strdup(optarg + offset);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Bad color value \"%s\"\n", optarg);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_colors(struct xcb_color_strings_t *colors) {
|
||||||
|
#define FREE_COLOR(x) \
|
||||||
|
do { \
|
||||||
|
if (colors->x) \
|
||||||
|
free(colors->x); \
|
||||||
|
} while (0)
|
||||||
|
FREE_COLOR(bar_fg);
|
||||||
|
FREE_COLOR(bar_bg);
|
||||||
|
FREE_COLOR(active_ws_fg);
|
||||||
|
FREE_COLOR(active_ws_bg);
|
||||||
|
FREE_COLOR(inactive_ws_fg);
|
||||||
|
FREE_COLOR(inactive_ws_bg);
|
||||||
|
FREE_COLOR(urgent_ws_fg);
|
||||||
|
FREE_COLOR(urgent_ws_bg);
|
||||||
|
FREE_COLOR(focus_ws_fg);
|
||||||
|
FREE_COLOR(focus_ws_bg);
|
||||||
|
#undef FREE_COLOR
|
||||||
|
}
|
||||||
|
|
||||||
|
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("-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");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We watch various signals, that are there to make our application stop.
|
||||||
|
* If we get one of those, we ev_unloop() and invoke the cleanup-routines
|
||||||
|
* in main() with that
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void sig_cb(struct ev_loop *loop, ev_signal *watcher, int revents) {
|
||||||
|
switch (watcher->signum) {
|
||||||
|
case SIGTERM:
|
||||||
|
DLOG("Got a SIGTERM, stopping\n");
|
||||||
|
break;
|
||||||
|
case SIGINT:
|
||||||
|
DLOG("Got a SIGINT, stopping\n");
|
||||||
|
break;
|
||||||
|
case SIGHUP:
|
||||||
|
DLOG("Got a SIGHUP, stopping\n");
|
||||||
|
}
|
||||||
|
ev_unloop(main_loop, EVUNLOOP_ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
int opt;
|
||||||
|
int option_index = 0;
|
||||||
|
char *socket_path = getenv("I3SOCK");
|
||||||
|
char *command = NULL;
|
||||||
|
char *fontname = NULL;
|
||||||
|
char *i3_default_sock_path = "/tmp/i3-ipc.sock";
|
||||||
|
struct xcb_color_strings_t colors = { NULL, };
|
||||||
|
|
||||||
|
/* Definition of the standard-config */
|
||||||
|
config.hide_on_modifier = 0;
|
||||||
|
config.dockpos = DOCKPOS_NONE;
|
||||||
|
config.disable_ws = 0;
|
||||||
|
|
||||||
|
static struct option long_opt[] = {
|
||||||
|
{ "socket", required_argument, 0, 's' },
|
||||||
|
{ "command", required_argument, 0, 'c' },
|
||||||
|
{ "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' },
|
||||||
|
{ "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}
|
||||||
|
};
|
||||||
|
|
||||||
|
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) {
|
||||||
|
switch (opt) {
|
||||||
|
case 's':
|
||||||
|
socket_path = expand_path(optarg);
|
||||||
|
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':
|
||||||
|
printf("i3bar version " I3_VERSION " © 2010-2011 Axel Wagner and contributors\n");
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
break;
|
||||||
|
case 'V':
|
||||||
|
config.verbose = 1;
|
||||||
|
break;
|
||||||
|
case 'A':
|
||||||
|
read_color(&colors.bar_fg);
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
read_color(&colors.bar_bg);
|
||||||
|
break;
|
||||||
|
case 'C':
|
||||||
|
read_color(&colors.active_ws_fg);
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
read_color(&colors.active_ws_bg);
|
||||||
|
break;
|
||||||
|
case 'E':
|
||||||
|
read_color(&colors.inactive_ws_fg);
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
read_color(&colors.inactive_ws_bg);
|
||||||
|
break;
|
||||||
|
case 'G':
|
||||||
|
read_color(&colors.urgent_ws_bg);
|
||||||
|
break;
|
||||||
|
case 'H':
|
||||||
|
read_color(&colors.urgent_ws_fg);
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
read_color(&colors.focus_ws_bg);
|
||||||
|
break;
|
||||||
|
case 'J':
|
||||||
|
read_color(&colors.focus_ws_fg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
print_usage(argv[0]);
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fontname == NULL) {
|
||||||
|
/* This is a very restrictive default. More sensefull would be something like
|
||||||
|
* "-misc-*-*-*-*--*-*-*-*-*-*-*-*". But since that produces very ugly results
|
||||||
|
* on my machine, let's stick with this until we have a configfile */
|
||||||
|
fontname = "-misc-fixed-medium-r-semicondensed--12-110-75-75-c-60-iso10646-1";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (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);
|
||||||
|
|
||||||
|
init_colors(&colors);
|
||||||
|
char *atom_sock_path = init_xcb(fontname);
|
||||||
|
|
||||||
|
if (socket_path == NULL) {
|
||||||
|
socket_path = atom_sock_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (socket_path == NULL) {
|
||||||
|
ELOG("No Socket Path Specified, default to %s\n", i3_default_sock_path);
|
||||||
|
socket_path = expand_path(i3_default_sock_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
free_colors(&colors);
|
||||||
|
|
||||||
|
init_outputs();
|
||||||
|
if (init_connection(socket_path)) {
|
||||||
|
/* We subscribe to the i3-events we need */
|
||||||
|
subscribe_events();
|
||||||
|
|
||||||
|
/* We initiate the main-function by requesting infos about the outputs and
|
||||||
|
* workspaces. Everything else (creating the bars, showing the right workspace-
|
||||||
|
* buttons and more) is taken care of by the event-driveniness of the code */
|
||||||
|
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
|
||||||
|
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 only need those watchers on the stack, so putting them on the stack saves us
|
||||||
|
* some calls to free() */
|
||||||
|
ev_signal *sig_term = malloc(sizeof(ev_signal));
|
||||||
|
ev_signal *sig_int = malloc(sizeof(ev_signal));
|
||||||
|
ev_signal *sig_hup = malloc(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_int, &sig_cb, SIGINT);
|
||||||
|
ev_signal_init(sig_hup, &sig_cb, SIGHUP);
|
||||||
|
|
||||||
|
ev_signal_start(main_loop, sig_term);
|
||||||
|
ev_signal_start(main_loop, sig_int);
|
||||||
|
ev_signal_start(main_loop, sig_hup);
|
||||||
|
|
||||||
|
/* From here on everything should run smooth for itself, just start listening for
|
||||||
|
* events. We stop simply stop the event-loop, when we are finished */
|
||||||
|
ev_loop(main_loop, 0);
|
||||||
|
|
||||||
|
kill_child();
|
||||||
|
|
||||||
|
FREE(statusline_buffer);
|
||||||
|
|
||||||
|
clean_xcb();
|
||||||
|
ev_default_destroy();
|
||||||
|
|
||||||
|
free_workspaces();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,293 @@
|
||||||
|
/*
|
||||||
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
|
*
|
||||||
|
* © 2010-2011 Axel Wagner and contributors
|
||||||
|
*
|
||||||
|
* See file LICNSE 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"
|
||||||
|
|
||||||
|
/* A datatype to pass through the callbacks to save the state */
|
||||||
|
struct outputs_json_params {
|
||||||
|
struct outputs_head *outputs;
|
||||||
|
i3_output *outputs_walk;
|
||||||
|
char *cur_key;
|
||||||
|
char *json;
|
||||||
|
bool init;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse a null-value (current_workspace)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static int outputs_null_cb(void *params_) {
|
||||||
|
struct outputs_json_params *params = (struct outputs_json_params*) params_;
|
||||||
|
|
||||||
|
FREE(params->cur_key);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse a booleant-value (active)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static int outputs_boolean_cb(void *params_, bool val) {
|
||||||
|
struct outputs_json_params *params = (struct outputs_json_params*) params_;
|
||||||
|
|
||||||
|
if (strcmp(params->cur_key, "active")) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
params->outputs_walk->active = val;
|
||||||
|
|
||||||
|
FREE(params->cur_key);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse an integer (current_workspace or the rect)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if YAJL_MAJOR >= 2
|
||||||
|
static int outputs_integer_cb(void *params_, long long val) {
|
||||||
|
#else
|
||||||
|
static int outputs_integer_cb(void *params_, long val) {
|
||||||
|
#endif
|
||||||
|
struct outputs_json_params *params = (struct outputs_json_params*) params_;
|
||||||
|
|
||||||
|
if (!strcmp(params->cur_key, "current_workspace")) {
|
||||||
|
params->outputs_walk->ws = (int) val;
|
||||||
|
FREE(params->cur_key);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(params->cur_key, "x")) {
|
||||||
|
params->outputs_walk->rect.x = (int) val;
|
||||||
|
FREE(params->cur_key);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(params->cur_key, "y")) {
|
||||||
|
params->outputs_walk->rect.y = (int) val;
|
||||||
|
FREE(params->cur_key);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(params->cur_key, "width")) {
|
||||||
|
params->outputs_walk->rect.w = (int) val;
|
||||||
|
FREE(params->cur_key);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(params->cur_key, "height")) {
|
||||||
|
params->outputs_walk->rect.h = (int) val;
|
||||||
|
FREE(params->cur_key);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse a string (name)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if YAJL_MAJOR >= 2
|
||||||
|
static int outputs_string_cb(void *params_, const unsigned char *val, size_t len) {
|
||||||
|
#else
|
||||||
|
static int outputs_string_cb(void *params_, const unsigned char *val, unsigned int len) {
|
||||||
|
#endif
|
||||||
|
struct outputs_json_params *params = (struct outputs_json_params*) params_;
|
||||||
|
|
||||||
|
if (!strcmp(params->cur_key, "current_workspace")) {
|
||||||
|
char *copy = malloc(sizeof(const unsigned char) * (len + 1));
|
||||||
|
strncpy(copy, (const char*) val, len);
|
||||||
|
copy[len] = '\0';
|
||||||
|
|
||||||
|
char *end;
|
||||||
|
errno = 0;
|
||||||
|
long parsed_num = strtol(copy, &end, 10);
|
||||||
|
if (errno == 0 &&
|
||||||
|
(end && *end == '\0'))
|
||||||
|
params->outputs_walk->ws = parsed_num;
|
||||||
|
free(copy);
|
||||||
|
FREE(params->cur_key);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(params->cur_key, "name")) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *name = malloc(sizeof(const unsigned char) * (len + 1));
|
||||||
|
strncpy(name, (const char*) val, len);
|
||||||
|
name[len] = '\0';
|
||||||
|
|
||||||
|
params->outputs_walk->name = name;
|
||||||
|
|
||||||
|
FREE(params->cur_key);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We hit the start of a json-map (rect or a new output)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static int outputs_start_map_cb(void *params_) {
|
||||||
|
struct outputs_json_params *params = (struct outputs_json_params*) params_;
|
||||||
|
i3_output *new_output = NULL;
|
||||||
|
|
||||||
|
if (params->cur_key == NULL) {
|
||||||
|
new_output = malloc(sizeof(i3_output));
|
||||||
|
new_output->name = NULL;
|
||||||
|
new_output->ws = 0,
|
||||||
|
memset(&new_output->rect, 0, sizeof(rect));
|
||||||
|
new_output->bar = XCB_NONE;
|
||||||
|
|
||||||
|
new_output->workspaces = malloc(sizeof(struct ws_head));
|
||||||
|
TAILQ_INIT(new_output->workspaces);
|
||||||
|
|
||||||
|
params->outputs_walk = new_output;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We hit the end of a map (rect or a new output)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static int outputs_end_map_cb(void *params_) {
|
||||||
|
struct outputs_json_params *params = (struct outputs_json_params*) params_;
|
||||||
|
/* FIXME: What is at the end of a rect? */
|
||||||
|
|
||||||
|
i3_output *target = get_output_by_name(params->outputs_walk->name);
|
||||||
|
|
||||||
|
if (target == NULL) {
|
||||||
|
SLIST_INSERT_HEAD(outputs, params->outputs_walk, slist);
|
||||||
|
} else {
|
||||||
|
target->active = params->outputs_walk->active;
|
||||||
|
target->ws = params->outputs_walk->ws;
|
||||||
|
target->rect = params->outputs_walk->rect;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse a key.
|
||||||
|
*
|
||||||
|
* Essentially we just save it in the parsing-state
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if YAJL_MAJOR >= 2
|
||||||
|
static int outputs_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
|
||||||
|
#else
|
||||||
|
static int outputs_map_key_cb(void *params_, const unsigned char *keyVal, unsigned keyLen) {
|
||||||
|
#endif
|
||||||
|
struct outputs_json_params *params = (struct outputs_json_params*) params_;
|
||||||
|
FREE(params->cur_key);
|
||||||
|
|
||||||
|
params->cur_key = malloc(sizeof(unsigned char) * (keyLen + 1));
|
||||||
|
strncpy(params->cur_key, (const char*) keyVal, keyLen);
|
||||||
|
params->cur_key[keyLen] = '\0';
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A datastructure to pass all these callbacks to yajl */
|
||||||
|
yajl_callbacks outputs_callbacks = {
|
||||||
|
&outputs_null_cb,
|
||||||
|
&outputs_boolean_cb,
|
||||||
|
&outputs_integer_cb,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&outputs_string_cb,
|
||||||
|
&outputs_start_map_cb,
|
||||||
|
&outputs_map_key_cb,
|
||||||
|
&outputs_end_map_cb,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initiate the output-list
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void init_outputs() {
|
||||||
|
outputs = malloc(sizeof(struct outputs_head));
|
||||||
|
SLIST_INIT(outputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start parsing the received json-string
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void parse_outputs_json(char *json) {
|
||||||
|
struct outputs_json_params params;
|
||||||
|
|
||||||
|
params.outputs_walk = NULL;
|
||||||
|
params.cur_key = NULL;
|
||||||
|
params.json = 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, (void*) ¶ms);
|
||||||
|
#else
|
||||||
|
handle = yajl_alloc(&outputs_callbacks, NULL, (void*) ¶ms);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
state = yajl_parse(handle, (const unsigned char*) json, strlen(json));
|
||||||
|
|
||||||
|
/* FIXME: Propper 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 outputs-reply!\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
yajl_free(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the output with the given name
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
i3_output *get_output_by_name(char *name) {
|
||||||
|
i3_output *walk;
|
||||||
|
if (name == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
SLIST_FOREACH(walk, outputs, slist) {
|
||||||
|
if (!strcmp(walk->name, name)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return walk;
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
* vim:ts=8:expandtab
|
||||||
|
*
|
||||||
|
* i3 - an improved dynamic tiling window manager
|
||||||
|
*
|
||||||
|
* © 2009 Michael Stapelberg and contributors
|
||||||
|
*
|
||||||
|
* See file LICENSE for license information.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <iconv.h>
|
||||||
|
|
||||||
|
static iconv_t conversion_descriptor = 0;
|
||||||
|
static iconv_t conversion_descriptor2 = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the input string, but converted from UCS-2 to UTF-8. Memory will be
|
||||||
|
* allocated, thus the caller has to free the output.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
char *convert_ucs_to_utf8(char *input) {
|
||||||
|
size_t input_size = 2;
|
||||||
|
/* UTF-8 may consume up to 4 byte */
|
||||||
|
int buffer_size = 8;
|
||||||
|
|
||||||
|
char *buffer = calloc(buffer_size, 1);
|
||||||
|
if (buffer == NULL)
|
||||||
|
err(EXIT_FAILURE, "malloc() failed\n");
|
||||||
|
size_t output_size = buffer_size;
|
||||||
|
/* We need to use an additional pointer, because iconv() modifies it */
|
||||||
|
char *output = buffer;
|
||||||
|
|
||||||
|
/* We convert the input into UCS-2 big endian */
|
||||||
|
if (conversion_descriptor == 0) {
|
||||||
|
conversion_descriptor = iconv_open("UTF-8", "UCS-2BE");
|
||||||
|
if (conversion_descriptor == 0) {
|
||||||
|
fprintf(stderr, "error opening the conversion context\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the conversion descriptor back to original state */
|
||||||
|
iconv(conversion_descriptor, NULL, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
/* Convert our text */
|
||||||
|
int rc = iconv(conversion_descriptor, (void*)&input, &input_size, &output, &output_size);
|
||||||
|
if (rc == (size_t)-1) {
|
||||||
|
perror("Converting to UCS-2 failed");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Converts the given string to UCS-2 big endian for use with
|
||||||
|
* xcb_image_text_16(). The amount of real glyphs is stored in real_strlen,
|
||||||
|
* a buffer containing the UCS-2 encoded string (16 bit per glyph) is
|
||||||
|
* returned. It has to be freed when done.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
char *convert_utf8_to_ucs2(char *input, int *real_strlen) {
|
||||||
|
size_t input_size = strlen(input) + 1;
|
||||||
|
/* UCS-2 consumes exactly two bytes for each glyph */
|
||||||
|
int buffer_size = input_size * 2;
|
||||||
|
|
||||||
|
char *buffer = malloc(buffer_size);
|
||||||
|
if (buffer == NULL)
|
||||||
|
err(EXIT_FAILURE, "malloc() failed\n");
|
||||||
|
size_t output_size = buffer_size;
|
||||||
|
/* We need to use an additional pointer, because iconv() modifies it */
|
||||||
|
char *output = buffer;
|
||||||
|
|
||||||
|
/* We convert the input into UCS-2 big endian */
|
||||||
|
if (conversion_descriptor2 == 0) {
|
||||||
|
conversion_descriptor2 = iconv_open("UCS-2BE", "UTF-8");
|
||||||
|
if (conversion_descriptor2 == 0) {
|
||||||
|
fprintf(stderr, "error opening the conversion context\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the conversion descriptor back to original state */
|
||||||
|
iconv(conversion_descriptor2, NULL, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
/* Convert our text */
|
||||||
|
int rc = iconv(conversion_descriptor2, (void*)&input, &input_size, &output, &output_size);
|
||||||
|
if (rc == (size_t)-1) {
|
||||||
|
perror("Converting to UCS-2 failed");
|
||||||
|
if (real_strlen != NULL)
|
||||||
|
*real_strlen = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (real_strlen != NULL)
|
||||||
|
*real_strlen = ((buffer_size - output_size) / 2) - 1;
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
|
@ -0,0 +1,290 @@
|
||||||
|
/*
|
||||||
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
|
*
|
||||||
|
* © 2010-2011 Axel Wagner and contributors
|
||||||
|
*
|
||||||
|
* See file LICNSE for license information
|
||||||
|
*
|
||||||
|
* src/workspaces.c: Maintaining the workspace-lists
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <yajl/yajl_parse.h>
|
||||||
|
#include <yajl/yajl_version.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
/* A datatype to pass through the callbacks to save the state */
|
||||||
|
struct workspaces_json_params {
|
||||||
|
struct ws_head *workspaces;
|
||||||
|
i3_ws *workspaces_walk;
|
||||||
|
char *cur_key;
|
||||||
|
char *json;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse a booleant-value (visible, focused, urgent)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static int workspaces_boolean_cb(void *params_, bool val) {
|
||||||
|
struct workspaces_json_params *params = (struct workspaces_json_params*) params_;
|
||||||
|
|
||||||
|
if (!strcmp(params->cur_key, "visible")) {
|
||||||
|
params->workspaces_walk->visible = val;
|
||||||
|
FREE(params->cur_key);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(params->cur_key, "focused")) {
|
||||||
|
params->workspaces_walk->focused = val;
|
||||||
|
FREE(params->cur_key);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(params->cur_key, "urgent")) {
|
||||||
|
params->workspaces_walk->urgent = val;
|
||||||
|
FREE(params->cur_key);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FREE(params->cur_key);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse an integer (num or the rect)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if YAJL_MAJOR >= 2
|
||||||
|
static int workspaces_integer_cb(void *params_, long long val) {
|
||||||
|
#else
|
||||||
|
static int workspaces_integer_cb(void *params_, long val) {
|
||||||
|
#endif
|
||||||
|
struct workspaces_json_params *params = (struct workspaces_json_params*) params_;
|
||||||
|
|
||||||
|
if (!strcmp(params->cur_key, "num")) {
|
||||||
|
params->workspaces_walk->num = (int) val;
|
||||||
|
FREE(params->cur_key);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(params->cur_key, "x")) {
|
||||||
|
params->workspaces_walk->rect.x = (int) val;
|
||||||
|
FREE(params->cur_key);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(params->cur_key, "y")) {
|
||||||
|
params->workspaces_walk->rect.y = (int) val;
|
||||||
|
FREE(params->cur_key);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(params->cur_key, "width")) {
|
||||||
|
params->workspaces_walk->rect.w = (int) val;
|
||||||
|
FREE(params->cur_key);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(params->cur_key, "height")) {
|
||||||
|
params->workspaces_walk->rect.h = (int) val;
|
||||||
|
FREE(params->cur_key);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FREE(params->cur_key);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse a string (name, output)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if YAJL_MAJOR >= 2
|
||||||
|
static int workspaces_string_cb(void *params_, const unsigned char *val, size_t len) {
|
||||||
|
#else
|
||||||
|
static int workspaces_string_cb(void *params_, const unsigned char *val, unsigned int len) {
|
||||||
|
#endif
|
||||||
|
struct workspaces_json_params *params = (struct workspaces_json_params*) params_;
|
||||||
|
|
||||||
|
char *output_name;
|
||||||
|
|
||||||
|
if (!strcmp(params->cur_key, "name")) {
|
||||||
|
/* Save the name */
|
||||||
|
params->workspaces_walk->name = malloc(sizeof(const unsigned char) * (len + 1));
|
||||||
|
strncpy(params->workspaces_walk->name, (const char*) val, len);
|
||||||
|
params->workspaces_walk->name[len] = '\0';
|
||||||
|
|
||||||
|
/* Convert the name to ucs2, save it's length in glyphs and calculate it'srendered width */
|
||||||
|
int ucs2_len;
|
||||||
|
xcb_char2b_t *ucs2_name = (xcb_char2b_t*) convert_utf8_to_ucs2(params->workspaces_walk->name, &ucs2_len);
|
||||||
|
params->workspaces_walk->ucs2_name = ucs2_name;
|
||||||
|
params->workspaces_walk->name_glyphs = ucs2_len;
|
||||||
|
params->workspaces_walk->name_width =
|
||||||
|
predict_text_extents(params->workspaces_walk->ucs2_name,
|
||||||
|
params->workspaces_walk->name_glyphs);
|
||||||
|
|
||||||
|
DLOG("Got Workspace %s, name_width: %d, glyphs: %d\n",
|
||||||
|
params->workspaces_walk->name,
|
||||||
|
params->workspaces_walk->name_width,
|
||||||
|
params->workspaces_walk->name_glyphs);
|
||||||
|
FREE(params->cur_key);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(params->cur_key, "output")) {
|
||||||
|
/* We add the ws to the TAILQ of the output, it belongs to */
|
||||||
|
output_name = malloc(sizeof(const unsigned char) * (len + 1));
|
||||||
|
strncpy(output_name, (const char*) val, len);
|
||||||
|
output_name[len] = '\0';
|
||||||
|
params->workspaces_walk->output = get_output_by_name(output_name);
|
||||||
|
|
||||||
|
TAILQ_INSERT_TAIL(params->workspaces_walk->output->workspaces,
|
||||||
|
params->workspaces_walk,
|
||||||
|
tailq);
|
||||||
|
|
||||||
|
FREE(output_name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We hit the start of a json-map (rect or a new output)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static int workspaces_start_map_cb(void *params_) {
|
||||||
|
struct workspaces_json_params *params = (struct workspaces_json_params*) params_;
|
||||||
|
|
||||||
|
i3_ws *new_workspace = NULL;
|
||||||
|
|
||||||
|
if (params->cur_key == NULL) {
|
||||||
|
new_workspace = malloc(sizeof(i3_ws));
|
||||||
|
new_workspace->num = -1;
|
||||||
|
new_workspace->name = NULL;
|
||||||
|
new_workspace->visible = 0;
|
||||||
|
new_workspace->focused = 0;
|
||||||
|
new_workspace->urgent = 0;
|
||||||
|
memset(&new_workspace->rect, 0, sizeof(rect));
|
||||||
|
new_workspace->output = NULL;
|
||||||
|
|
||||||
|
params->workspaces_walk = new_workspace;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse a key.
|
||||||
|
*
|
||||||
|
* Essentially we just save it in the parsing-state
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if YAJL_MAJOR >= 2
|
||||||
|
static int workspaces_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
|
||||||
|
#else
|
||||||
|
static int workspaces_map_key_cb(void *params_, const unsigned char *keyVal, unsigned int keyLen) {
|
||||||
|
#endif
|
||||||
|
struct workspaces_json_params *params = (struct workspaces_json_params*) params_;
|
||||||
|
FREE(params->cur_key);
|
||||||
|
|
||||||
|
params->cur_key = malloc(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);
|
||||||
|
params->cur_key[keyLen] = '\0';
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A datastructure to pass all these callbacks to yajl */
|
||||||
|
yajl_callbacks workspaces_callbacks = {
|
||||||
|
NULL,
|
||||||
|
&workspaces_boolean_cb,
|
||||||
|
&workspaces_integer_cb,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&workspaces_string_cb,
|
||||||
|
&workspaces_start_map_cb,
|
||||||
|
&workspaces_map_key_cb,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start parsing the received json-string
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void parse_workspaces_json(char *json) {
|
||||||
|
/* FIXME: Fasciliate stream-processing, i.e. allow starting to interpret
|
||||||
|
* JSON in chunks */
|
||||||
|
struct workspaces_json_params params;
|
||||||
|
|
||||||
|
free_workspaces();
|
||||||
|
|
||||||
|
params.workspaces_walk = NULL;
|
||||||
|
params.cur_key = NULL;
|
||||||
|
params.json = json;
|
||||||
|
|
||||||
|
yajl_handle handle;
|
||||||
|
yajl_status state;
|
||||||
|
#if YAJL_MAJOR < 2
|
||||||
|
yajl_parser_config parse_conf = { 0, 0 };
|
||||||
|
|
||||||
|
handle = yajl_alloc(&workspaces_callbacks, &parse_conf, NULL, (void*) ¶ms);
|
||||||
|
#else
|
||||||
|
handle = yajl_alloc(&workspaces_callbacks, NULL, (void*) ¶ms);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
state = yajl_parse(handle, (const unsigned char*) json, strlen(json));
|
||||||
|
|
||||||
|
/* FIXME: Propper 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 workspaces-reply!\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
yajl_free(handle);
|
||||||
|
|
||||||
|
FREE(params.cur_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* free() all workspace data-structures. Does not free() the heads of the tailqueues.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void free_workspaces() {
|
||||||
|
i3_output *outputs_walk;
|
||||||
|
if (outputs == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
i3_ws *ws_walk;
|
||||||
|
|
||||||
|
SLIST_FOREACH(outputs_walk, outputs, slist) {
|
||||||
|
if (outputs_walk->workspaces != NULL && !TAILQ_EMPTY(outputs_walk->workspaces)) {
|
||||||
|
TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
|
||||||
|
FREE(ws_walk->name);
|
||||||
|
FREE(ws_walk->ucs2_name);
|
||||||
|
}
|
||||||
|
FREE_TAILQ(outputs_walk->workspaces, i3_ws);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue