#include "util.h"
#include "workspaces.h"
#include "xcb.h"
+#include "ucs2_to_utf8.h"
#endif
struct outputs_head *outputs;
void parse_outputs_json(char* json);
+void init_outputs();
void free_outputs();
i3_output* get_output_by_name(char* name);
--- /dev/null
+char *convert_utf8_to_ucs2(char *input, int *real_strlen);
#ifndef WORKSPACES_H_
#define WORKSPACES_H_
+#include <xcb/xproto.h>
+
#include "common.h"
typedef struct i3_ws i3_ws;
struct i3_ws {
int num;
char *name;
+ xcb_char2b_t *ucs2_name;
+ int name_glyphs;
int name_width;
bool visible;
bool focused;
void clean_xcb();
void get_atoms();
void destroy_windows();
-void create_windows();
+void reconfig_windows();
void draw_bars();
-int get_string_width(char *string);
+int get_string_width(xcb_char2b_t *string, int glyph_len);
#endif
strip_dzen_formats(buffer);
FREE(statusline);
statusline = buffer;
- printf("%s", buffer);
+ printf("%s\n", buffer);
draw_bars();
}
}
void start_child(char *command) {
- int fd[2];
- pipe(fd);
- child_pid = fork();
- switch (child_pid) {
- case -1:
- printf("ERROR: Couldn't fork()");
- exit(EXIT_FAILURE);
- case 0:
- close(fd[0]);
+ child_pid = 0;
+ if (command != NULL) {
+ int fd[2];
+ pipe(fd);
+ child_pid = fork();
+ switch (child_pid) {
+ case -1:
+ printf("ERROR: Couldn't fork()");
+ exit(EXIT_FAILURE);
+ case 0:
+ close(fd[0]);
+
+ dup2(fd[1], STDOUT_FILENO);
- dup2(fd[1], STDOUT_FILENO);
+ static const char *shell = NULL;
- static const char *shell = NULL;
+ if ((shell = getenv("SHELL")) == NULL)
+ shell = "/bin/sh";
- if ((shell = getenv("SHELL")) == NULL)
- shell = "/bin/sh";
+ execl(shell, shell, "-c", command, (char*) NULL);
+ return;
+ default:
+ close(fd[1]);
- execl(shell, shell, "-c", command, (char*) NULL);
- break;
- default:
- close(fd[1]);
+ dup2(fd[0], STDIN_FILENO);
+
+ break;
+ }
+ }
- dup2(fd[0], STDIN_FILENO);
- fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
+ fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
- child_io = malloc(sizeof(ev_io));
- ev_io_init(child_io, &child_io_cb, STDIN_FILENO, EV_READ);
- ev_io_start(main_loop, child_io);
+ child_io = malloc(sizeof(ev_io));
+ ev_io_init(child_io, &child_io_cb, STDIN_FILENO, EV_READ);
+ ev_io_start(main_loop, child_io);
- /* We must cleanup, if the child unexpectedly terminates */
- child_sig = malloc(sizeof(ev_io));
- ev_child_init(child_sig, &child_sig_cb, child_pid, 0);
- ev_child_start(main_loop, child_sig);
+ /* We must cleanup, if the child unexpectedly terminates */
+ child_sig = malloc(sizeof(ev_io));
+ ev_child_init(child_sig, &child_sig_cb, child_pid, 0);
+ ev_child_start(main_loop, child_sig);
- break;
- }
}
void kill_child() {
- kill(child_pid, SIGQUIT);
+ if (child_pid != 0) {
+ kill(child_pid, SIGQUIT);
+ }
cleanup();
}
}
void got_output_reply(char *reply) {
- printf("Got Outputs-Data!\nDestroying Windows...\n");
- destroy_windows();
- printf("Parsing JSON...\n");
+ printf("Parsing Outputs-JSON...\n");
parse_outputs_json(reply);
- printf("Creating_Windows...\n");
- create_windows();
+ printf("Reconfiguring Windows...\n");
+ reconfig_windows();
}
handler_t reply_handlers[] = {
#include <stdlib.h>
#include <errno.h>
#include <ev.h>
+#include <getopt.h>
#include "common.h"
+char *i3_default_sock_path = "/home/mero/.i3/ipc.sock";
+
int main(int argc, char **argv) {
+ int opt;
+ int option_index = 0;
+ char *socket_path = NULL;
+ char *command = NULL;
+ char *fontname = NULL;
+
+ static struct option long_opt[] = {
+ { "socket", required_argument, 0, 's' },
+ { "command", required_argument, 0, 'c' },
+ { "font", required_argument, 0, 'f' },
+ { "help", no_argument, 0, 'h' },
+ { NULL, 0, 0, 0}
+ };
+
+ while ((opt = getopt_long(argc, argv, "s:c:f:h", long_opt, &option_index)) != -1) {
+ switch (opt) {
+ case 's':
+ socket_path = malloc(strlen(optarg));
+ strcpy(socket_path, optarg);
+ break;
+ case 'c':
+ command = malloc(strlen(optarg));
+ strcpy(command, optarg);
+ break;
+ case 'f':
+ fontname = malloc(strlen(optarg));
+ strcpy(socket_path, optarg);
+ break;
+ default:
+ printf("Usage: %s [-s socket_path] [-c command] [-f font] [-h]\n", argv[0]);
+ printf("-s <socket_path>: Connect to i3 via <socket_path>\n");
+ printf("-c <command>: Execute <command> to get sdtin\n");
+ printf("-f <font>: Use X-Core-Font <font> for display\n");
+ printf("-h: Display this help-message and exit\n");
+ exit(EXIT_SUCCESS);
+ break;
+ }
+ }
+
+ if (fontname == NULL) {
+ fontname = "-misc-fixed-medium-r-semicondensed--12-110-75-75-c-60-iso10646-1";
+ }
+
+ if (socket_path == NULL) {
+ printf("No Socket Path Specified, default to %s\n", i3_default_sock_path);
+ socket_path = i3_default_sock_path;
+ }
+
main_loop = ev_default_loop(0);
- init_xcb();
- init_connection("/home/mero/.i3/ipc.sock");
+ init_xcb(fontname);
+ init_outputs();
+ init_connection(socket_path);
subscribe_events();
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
- start_child("i3status");
+ start_child(command);
ev_loop(main_loop, 0);
i3_output *outputs_walk;
char *cur_key;
char *json;
+ bool init;
};
static int outputs_null_cb(void *params_) {
return 0;
}
- params->outputs_walk->name = malloc(sizeof(const unsigned char) * (len + 1));
- strncpy(params->outputs_walk->name, (const char*) val, len);
- params->outputs_walk->name[len] = '\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);
new_output->workspaces = malloc(sizeof(struct ws_head));
TAILQ_INIT(new_output->workspaces);
- SLIST_INSERT_HEAD(params->outputs, new_output, slist);
-
- params->outputs_walk = SLIST_FIRST(params->outputs);
+ params->outputs_walk = new_output;
return 1;
}
return 1;
}
+static int outputs_end_map_cb(void *params_) {
+ struct outputs_json_params *params = (struct outputs_json_params*) params_;
+
+ i3_output *target = get_output_by_name(params->outputs_walk->name);
+
+ if (target == NULL) {
+ SLIST_INSERT_HEAD(outputs, params->outputs_walk, slist);
+ } else {
+ target->ws = params->outputs_walk->ws;
+ target->rect = params->outputs_walk->rect;
+ }
+ return 1;
+}
+
static int outputs_map_key_cb(void *params_, const unsigned char *keyVal, unsigned int keyLen) {
struct outputs_json_params *params = (struct outputs_json_params*) params_;
FREE(params->cur_key);
&outputs_string_cb,
&outputs_start_map_cb,
&outputs_map_key_cb,
- NULL,
+ &outputs_end_map_cb,
NULL,
NULL
};
+void init_outputs() {
+ outputs = malloc(sizeof(struct outputs_head));
+ SLIST_INIT(outputs);
+}
+
void parse_outputs_json(char *json) {
- /* FIXME: Fasciliate stream-processing, i.e. allow starting to interpret
- * JSON in chunks */
struct outputs_json_params params;
- printf(json);
- params.outputs = malloc(sizeof(struct outputs_head));
- SLIST_INIT(params.outputs);
params.outputs_walk = NULL;
params.cur_key = NULL;
}
yajl_free(handle);
-
- if (outputs != NULL) {
- FREE_SLIST(outputs, i3_output);
- }
-
- outputs = params.outputs;
}
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;
--- /dev/null
+/*
+ * 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;
+}
char *output_name;
if (!strcmp(params->cur_key, "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';
-
- params->workspaces_walk->name_width = get_string_width(params->workspaces_walk->name);
-
- printf("Got Workspace %s, name_width: %d\n",
- params->workspaces_walk->name,
- params->workspaces_walk->name_width);
- FREE(params->cur_key);
-
- return 1;
+ 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';
+
+ 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 = get_string_width(params->workspaces_walk->ucs2_name,
+ params->workspaces_walk->name_glyphs);
+
+ printf("Got Workspace %s, name_width: %d, glyphs: %d\n",
+ params->workspaces_walk->name,
+ params->workspaces_walk->name_width,
+ params->workspaces_walk->name_glyphs);
+ FREE(params->cur_key);
+
+ return 1;
}
if (!strcmp(params->cur_key, "output")) {
- 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);
+ 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);
+ TAILQ_INSERT_TAIL(params->workspaces_walk->output->workspaces,
+ params->workspaces_walk,
+ tailq);
- free(output_name);
- return 1;
+ FREE(output_name);
+ return 1;
}
return 0;
static int workspaces_start_map_cb(void *params_) {
struct workspaces_json_params *params = (struct workspaces_json_params*) params_;
- i3_ws *new_workspace = NULL;
+ i3_ws *new_workspace = NULL;
if (params->cur_key == NULL) {
new_workspace = malloc(sizeof(i3_ws));
#include <xcb/xcb.h>
+#include <xcb/xproto.h>
+#include <xcb/xcb_event.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
ev_check *xcb_chk;
ev_io *xcb_io;
+xcb_event_handlers_t xcb_event_handlers;
+
uint32_t get_colorpixel(const char *s) {
char strings[3][3] = { { s[0], s[1], '\0'} ,
{ s[2], s[3], '\0'} ,
* Prepare- and Check-Watchers */
}
-int get_string_width(char *string) {
+int get_string_width(xcb_char2b_t *string, int glyph_len) {
xcb_query_text_extents_cookie_t cookie;
xcb_query_text_extents_reply_t *reply;
xcb_generic_error_t *error;
int width;
- cookie = xcb_query_text_extents(xcb_connection, xcb_font, strlen(string), (xcb_char2b_t*) string);
- if ((reply= xcb_query_text_extents_reply(xcb_connection, cookie, &error)) == NULL) {
+ cookie = xcb_query_text_extents(xcb_connection, xcb_font, glyph_len, string);
+ if ((reply = xcb_query_text_extents_reply(xcb_connection, cookie, &error)) == NULL) {
printf("ERROR: Could not get text extents!");
return 7;
}
return width;
}
-void init_xcb() {
+void init_xcb(char *fontname) {
/* FIXME: xcb_connect leaks Memory */
xcb_connection = xcb_connect(NULL, NULL);
if (xcb_connection_has_error(xcb_connection)) {
xcb_root = xcb_screens->root;
xcb_font = xcb_generate_id(xcb_connection);
- char *fontname = "-misc-fixed-medium-r-semicondensed--12-110-75-75-c-60-iso10646-1";
xcb_open_font(xcb_connection,
xcb_font,
strlen(fontname),
printf("Got Atoms\n");
}
-void destroy_windows() {
- i3_output *walk;
- if (outputs == NULL) {
+void destroy_window(i3_output *output) {
+ if (output == NULL) {
return;
}
- SLIST_FOREACH(walk, outputs, slist) {
- if (walk->bar == XCB_NONE) {
- continue;
- }
- xcb_destroy_window(xcb_connection, walk->bar);
- walk->bar = XCB_NONE;
+ if (output->bar == XCB_NONE) {
+ return;
}
+ xcb_destroy_window(xcb_connection, output->bar);
+ output->bar = XCB_NONE;
}
-void create_windows() {
+void reconfig_windows() {
uint32_t mask;
- uint32_t values[2];
+ uint32_t values[4];
i3_output *walk;
SLIST_FOREACH(walk, outputs, slist) {
if (!walk->active) {
+ destroy_window(walk);
continue;
}
- printf("Creating Window for output %s\n", walk->name);
-
- walk->bar = xcb_generate_id(xcb_connection);
- mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
- values[0] = xcb_screens->black_pixel;
- values[1] = XCB_EVENT_MASK_EXPOSURE |
- XCB_EVENT_MASK_BUTTON_PRESS;
- xcb_create_window(xcb_connection,
- xcb_screens->root_depth,
+ if (walk->bar == XCB_NONE) {
+ printf("Creating Window for output %s\n", walk->name);
+
+ walk->bar = xcb_generate_id(xcb_connection);
+ mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
+ values[0] = xcb_screens->black_pixel;
+ values[1] = XCB_EVENT_MASK_EXPOSURE |
+ XCB_EVENT_MASK_BUTTON_PRESS;
+ xcb_create_window(xcb_connection,
+ xcb_screens->root_depth,
+ walk->bar,
+ xcb_root,
+ walk->rect.x, walk->rect.y,
+ walk->rect.w, font_height + 6,
+ 1,
+ XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ xcb_screens->root_visual,
+ mask,
+ values);
+
+ xcb_change_property(xcb_connection,
+ XCB_PROP_MODE_REPLACE,
+ walk->bar,
+ atoms[_NET_WM_WINDOW_TYPE],
+ atoms[ATOM],
+ 32,
+ 1,
+ (unsigned char*) &atoms[_NET_WM_WINDOW_TYPE_DOCK]);
+
+ walk->bargc = xcb_generate_id(xcb_connection);
+ mask = XCB_GC_FONT;
+ values[0] = xcb_font;
+ xcb_create_gc(xcb_connection,
+ walk->bargc,
walk->bar,
- xcb_root,
- walk->rect.x, walk->rect.y,
- walk->rect.w, font_height + 6,
- 1,
- XCB_WINDOW_CLASS_INPUT_OUTPUT,
- xcb_screens->root_visual,
mask,
values);
- xcb_change_property(xcb_connection,
- XCB_PROP_MODE_REPLACE,
- walk->bar,
- atoms[_NET_WM_WINDOW_TYPE],
- atoms[ATOM],
- 32,
- 1,
- (unsigned char*) &atoms[_NET_WM_WINDOW_TYPE_DOCK]);
-
- walk->bargc = xcb_generate_id(xcb_connection);
- mask = XCB_GC_FONT;
- values[0] = xcb_font;
- xcb_create_gc(xcb_connection,
- walk->bargc,
- walk->bar,
- mask,
- values);
-
- xcb_map_window(xcb_connection, walk->bar);
+ xcb_map_window(xcb_connection, walk->bar);
+ } else {
+ mask = XCB_CONFIG_WINDOW_X |
+ XCB_CONFIG_WINDOW_Y |
+ XCB_CONFIG_WINDOW_WIDTH |
+ XCB_CONFIG_WINDOW_HEIGHT;
+ values[0] = walk->rect.x;
+ values[1] = walk->rect.y;
+ values[2] = walk->rect.w;
+ values[3] = walk->rect.h;
+ xcb_configure_window(xcb_connection,
+ walk->bar,
+ mask,
+ values);
+ }
}
xcb_flush(xcb_connection);
}
continue;
}
if (outputs_walk->bar == XCB_NONE) {
- create_windows();
+ reconfig_windows();
}
uint32_t color = get_colorpixel("000000");
xcb_change_gc(xcb_connection,
XCB_GC_FOREGROUND,
&color);
- xcb_image_text_8(xcb_connection,
- strlen(statusline),
- outputs_walk->bar,
- outputs_walk->bargc,
- outputs_walk->rect.w - get_string_width(statusline) - 4,
- font_height + 1,
- statusline);
+ int glyph_count;
+ xcb_char2b_t *text = (xcb_char2b_t*) convert_utf8_to_ucs2(statusline, &glyph_count);
+
+ xcb_void_cookie_t cookie;
+ cookie = xcb_image_text_16(xcb_connection,
+ glyph_count,
+ outputs_walk->bar,
+ outputs_walk->bargc,
+ outputs_walk->rect.w - get_string_width(text, glyph_count) - 4,
+ font_height + 1,
+ (xcb_char2b_t*) text);
+
+ xcb_generic_error_t *err = xcb_request_check(xcb_connection, cookie);
+
+ if (err != NULL) {
+ printf("XCB-Error: %d\n", err->error_code);
+ }
}
i3_ws *ws_walk;
TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
outputs_walk->bargc,
XCB_GC_FOREGROUND,
&color);
- xcb_image_text_8(xcb_connection,
- strlen(ws_walk->name),
- outputs_walk->bar,
- outputs_walk->bargc,
- i + 5, font_height + 1,
- ws_walk->name);
+ xcb_image_text_16(xcb_connection,
+ ws_walk->name_glyphs,
+ outputs_walk->bar,
+ outputs_walk->bargc,
+ i + 5, font_height + 1,
+ ws_walk->ucs2_name);
i += 10 + ws_walk->name_width;
}