Merge i3bar into next
This commit is contained in:
commit
22bac9fd9a
4
i3bar/.gitignore
vendored
Normal file
4
i3bar/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
i3bar
|
||||
*.o
|
||||
core
|
||||
doc/i3bar.1
|
30
i3bar/CHANGELOG
Normal file
30
i3bar/CHANGELOG
Normal file
@ -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
|
27
i3bar/LICENSE
Normal file
27
i3bar/LICENSE
Normal file
@ -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.
|
37
i3bar/Makefile
Normal file
37
i3bar/Makefile
Normal file
@ -0,0 +1,37 @@
|
||||
TOPDIR=$(shell pwd)
|
||||
|
||||
include $(TOPDIR)/common.mk
|
||||
|
||||
FILES:=$(wildcard src/*.c)
|
||||
FILES:=$(FILES:.c=.o)
|
||||
HEADERS:=$(wildcard include/*.h)
|
||||
|
||||
all: i3bar doc
|
||||
|
||||
i3bar: ${FILES}
|
||||
echo "LINK"
|
||||
$(CC) -o i3bar ${FILES} ${LDFLAGS}
|
||||
|
||||
doc:
|
||||
echo ""
|
||||
echo "SUBDIR doc"
|
||||
$(MAKE) -C doc
|
||||
|
||||
src/%.o: src/%.c ${HEADERS}
|
||||
echo "CC $<"
|
||||
$(CC) $(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
|
35
i3bar/common.mk
Normal file
35
i3bar/common.mk
Normal file
@ -0,0 +1,35 @@
|
||||
INSTALL=install
|
||||
ifndef DEBUG
|
||||
DEBUG=1
|
||||
endif
|
||||
PREFIX=/usr
|
||||
|
||||
# The escaping is absurd, but we need to escape for shell, sed, make, define
|
||||
GIT_VERSION:="$(shell git describe --tags --always) ($(shell git log --pretty=format:%cd --date=short -n1), branch $(shell [ -f .git/HEAD ] && sed 's/ref: refs\/heads\/\(.*\)/\\\\\\"\1\\\\\\"/g' .git/HEAD || echo 'unknown'))"
|
||||
|
||||
# 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.
|
||||
CFLAGS += -idirafter yajl-fallback
|
||||
|
||||
CFLAGS += -Wall
|
||||
CFLAGS += -pipe
|
||||
CFLAGS += -g
|
||||
|
||||
CPPFLAGS += -DI3BAR_VERSION=\"${GIT_VERSION}\"
|
||||
CPPFLAGS += -Iinclude
|
||||
|
||||
LDFLAGS += -lev
|
||||
LDFLAGS += -lyajl
|
||||
LDFLAGS += -lxcb
|
||||
LDFLAGS += -lX11
|
||||
LDFLAGS += -L/usr/local/lib
|
||||
|
||||
ifeq ($(DEBUG),1)
|
||||
CFLAGS += -g3
|
||||
else
|
||||
CFLAGS += -O2
|
||||
endif
|
||||
|
||||
.SILENT:
|
||||
|
||||
.PHONY: install clean
|
9
i3bar/doc/Makefile
Normal file
9
i3bar/doc/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
all: i3bar.1
|
||||
|
||||
i3bar.1: i3bar.man
|
||||
echo "A2X i3bar"
|
||||
a2x -f manpage i3bar.man
|
||||
clean:
|
||||
rm -f i3bar.xml i3bar.1 i3bar.html
|
||||
|
||||
distclean: clean
|
90
i3bar/doc/i3bar.man
Normal file
90
i3bar/doc/i3bar.man
Normal file
@ -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
|
41
i3bar/include/child.h
Normal file
41
i3bar/include/child.h
Normal file
@ -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
|
36
i3bar/include/common.h
Normal file
36
i3bar/include/common.h
Normal file
@ -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
|
22
i3bar/include/config.h
Normal file
22
i3bar/include/config.h
Normal file
@ -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
|
40
i3bar/include/ipc.h
Normal file
40
i3bar/include/ipc.h
Normal file
@ -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
|
54
i3bar/include/outputs.h
Normal file
54
i3bar/include/outputs.h
Normal file
@ -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
|
527
i3bar/include/queue.h
Normal file
527
i3bar/include/queue.h
Normal file
@ -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_ */
|
1
i3bar/include/ucs2_to_utf8.h
Normal file
1
i3bar/include/ucs2_to_utf8.h
Normal file
@ -0,0 +1 @@
|
||||
char *convert_utf8_to_ucs2(char *input, int *real_strlen);
|
57
i3bar/include/util.h
Normal file
57
i3bar/include/util.h
Normal file
@ -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)
|
47
i3bar/include/workspaces.h
Normal file
47
i3bar/include/workspaces.h
Normal file
@ -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
|
93
i3bar/include/xcb.h
Normal file
93
i3bar/include/xcb.h
Normal file
@ -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
|
5
i3bar/include/xcb_atoms.def
Normal file
5
i3bar/include/xcb_atoms.def
Normal file
@ -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
|
212
i3bar/src/child.c
Normal file
212
i3bar/src/child.c
Normal file
@ -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);
|
||||
}
|
||||
}
|
342
i3bar/src/ipc.c
Normal file
342
i3bar/src/ipc.c
Normal file
@ -0,0 +1,342 @@
|
||||
/*
|
||||
* 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 <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\" ]");
|
||||
}
|
||||
}
|
315
i3bar/src/main.c
Normal file
315
i3bar/src/main.c
Normal file
@ -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 " I3BAR_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;
|
||||
}
|
293
i3bar/src/outputs.c
Normal file
293
i3bar/src/outputs.c
Normal file
@ -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;
|
||||
}
|
103
i3bar/src/ucs2_to_utf8.c
Normal file
103
i3bar/src/ucs2_to_utf8.c
Normal file
@ -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;
|
||||
}
|
290
i3bar/src/workspaces.c
Normal file
290
i3bar/src/workspaces.c
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
1013
i3bar/src/xcb.c
Normal file
1013
i3bar/src/xcb.c
Normal file
File diff suppressed because it is too large
Load Diff
7
i3bar/yajl-fallback/yajl/yajl_version.h
Normal file
7
i3bar/yajl-fallback/yajl/yajl_version.h
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef YAJL_VERSION_H_
|
||||
#define YAJL_VERSION_H_
|
||||
/* Fallback for libyajl 1 which does not provide yajl_version.h */
|
||||
#define YAJL_MAJOR 1
|
||||
#define YAJL_MINOR 0
|
||||
#define YAJL_MICRO 0
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user