From: Michael Stapelberg Date: Sat, 21 Jan 2012 14:22:29 +0000 (+0000) Subject: Merge branch 'master' into next X-Git-Tag: 4.2~127 X-Git-Url: https://git.sur5r.net/?p=i3%2Fi3;a=commitdiff_plain;h=fc27c19761b47fa449514395f7ea2a21654f515d;hp=cccf078f1e0ce0d8c91795809293c4c259754073 Merge branch 'master' into next Conflicts: testcases/t/005-floating.t --- diff --git a/.gitignore b/.gitignore index cad6ad9a..b641592b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ loglevels.tmp *.gcno testcases/testsuite-* testcases/latest +testcases/Makefile *.output *.tab.* *.yy.c diff --git a/Makefile b/Makefile index c0797839..4e55b2e1 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ AUTOGENERATED:=src/cfgparse.tab.c src/cfgparse.yy.c src/cmdparse.tab.c src/cmdpa FILES:=$(filter-out $(AUTOGENERATED),$(wildcard src/*.c)) FILES:=$(FILES:.c=.o) HEADERS:=$(filter-out include/loglevels.h,$(wildcard include/*.h)) +CMDPARSE_HEADERS:=include/GENERATED_call.h include/GENERATED_enums.h include/GENERATED_tokens.h # Recursively generate loglevels.h by explicitly calling make # We need this step because we need to ensure that loglevels.h will be @@ -18,7 +19,7 @@ else UNUSED:=$(shell $(MAKE) loglevels.h) endif -SUBDIRS:=i3-msg i3-input i3-nagbar i3-config-wizard i3bar +SUBDIRS:=i3-msg i3-input i3-nagbar i3-config-wizard i3bar i3-dump-log # Depend on the specific file (.c for each .o) and on all headers src/%.o: src/%.c ${HEADERS} @@ -53,25 +54,41 @@ loglevels.h: done; \ echo "};") > include/loglevels.h; +# The GENERATED_* files are actually all created from a single pass, so all +# files just depend on the first one. +include/GENERATED_call.h: generate-command-parser.pl parser-specs/commands.spec + echo "[i3] Generating command parser" + (cd include; ../generate-command-parser.pl) +include/GENERATED_enums.h: include/GENERATED_call.h +include/GENERATED_tokens.h: include/GENERATED_call.h + +# This target compiles the command parser twice: +# Once with -DTEST_PARSER, creating a stand-alone executable used for tests, +# and once as an object file for i3. +src/commands_parser.o: src/commands_parser.c ${HEADERS} ${CMDPARSE_HEADERS} + echo "[i3] CC $<" + $(CC) $(CPPFLAGS) $(CFLAGS) -DTEST_PARSER -DLOGLEVEL="((uint64_t)1 << $(shell awk '/$(shell basename $< .c)/ { print NR; exit 0; }' loglevels.tmp))" -o test.commands_parser $< + $(CC) $(CPPFLAGS) $(CFLAGS) -DLOGLEVEL="((uint64_t)1 << $(shell awk '/$(shell basename $< .c)/ { print NR; exit 0; }' loglevels.tmp))" -c -o $@ $< + src/cfgparse.yy.o: src/cfgparse.l src/cfgparse.y.o ${HEADERS} echo "[i3] LEX $<" - flex -i -o$(@:.o=.c) $< + $(FLEX) -i -o$(@:.o=.c) $< $(CC) $(CPPFLAGS) $(CFLAGS) -DLOGLEVEL="(1 << $(shell awk '/cfgparse.l/ { print NR }' loglevels.tmp))" -c -o $@ $(@:.o=.c) src/cmdparse.yy.o: src/cmdparse.l src/cmdparse.y.o ${HEADERS} echo "[i3] LEX $<" - flex -Pcmdyy -i -o$(@:.o=.c) $< + $(FLEX) -Pcmdyy -i -o$(@:.o=.c) $< $(CC) $(CPPFLAGS) $(CFLAGS) -DLOGLEVEL="(1 << $(shell awk '/cmdparse.l/ { print NR }' loglevels.tmp))" -c -o $@ $(@:.o=.c) src/cfgparse.y.o: src/cfgparse.y ${HEADERS} echo "[i3] YACC $<" - bison --debug --verbose -b $(basename $< .y) -d $< + $(BISON) --debug --verbose -b $(basename $< .y) -d $< $(CC) $(CPPFLAGS) $(CFLAGS) -DLOGLEVEL="(1 << $(shell awk '/cfgparse.y/ { print NR }' loglevels.tmp))" -c -o $@ $(<:.y=.tab.c) src/cmdparse.y.o: src/cmdparse.y ${HEADERS} echo "[i3] YACC $<" - bison -p cmdyy --debug --verbose -b $(basename $< .y) -d $< + $(BISON) -p cmdyy --debug --verbose -b $(basename $< .y) -d $< $(CC) $(CPPFLAGS) $(CFLAGS) -DLOGLEVEL="(1 << $(shell awk '/cmdparse.y/ { print NR }' loglevels.tmp))" -c -o $@ $(<:.y=.tab.c) @@ -99,8 +116,8 @@ dist: distclean [ ! -d i3-${VERSION} ] || rm -rf i3-${VERSION} [ ! -e i3-${VERSION}.tar.bz2 ] || rm i3-${VERSION}.tar.bz2 mkdir i3-${VERSION} - cp i3-migrate-config-to-v4 i3-sensible-* i3.config.keycodes DEPENDS GOALS LICENSE PACKAGE-MAINTAINER RELEASE-NOTES-${VERSION} i3.config i3.desktop i3.welcome pseudo-doc.doxygen i3-wsbar Makefile i3-${VERSION} - cp -r src libi3 i3-msg i3-nagbar i3-config-wizard i3bar yajl-fallback include man i3-${VERSION} + cp i3-migrate-config-to-v4 generate-command-parser.pl i3-sensible-* i3.config.keycodes DEPENDS GOALS LICENSE PACKAGE-MAINTAINER RELEASE-NOTES-${VERSION} i3.config i3.desktop i3.welcome pseudo-doc.doxygen i3-wsbar Makefile i3-${VERSION} + cp -r src libi3 i3-msg i3-nagbar i3-config-wizard i3bar i3-dump-log yajl-fallback include man parser-specs i3-${VERSION} # Only copy toplevel documentation (important stuff) mkdir i3-${VERSION}/docs # Pre-generate documentation @@ -121,7 +138,7 @@ dist: distclean rm -rf i3-${VERSION} clean: - rm -f src/*.o src/*.gcno src/cfgparse.tab.{c,h} src/cfgparse.yy.c src/cfgparse.{output,dot} src/cmdparse.tab.{c,h} src/cmdparse.yy.c src/cmdparse.{output,dot} loglevels.tmp include/loglevels.h + rm -f src/*.o src/*.gcno src/cfgparse.tab.{c,h} src/cfgparse.yy.c src/cfgparse.{output,dot} src/cmdparse.tab.{c,h} src/cmdparse.yy.c src/cmdparse.{output,dot} loglevels.tmp include/loglevels.h include/GENERATED_* (which lcov >/dev/null 2>&1 && lcov -d . --zerocounters) || true $(MAKE) -C libi3 clean $(MAKE) -C docs clean diff --git a/common.mk b/common.mk index a9d16618..537d4dda 100644 --- a/common.mk +++ b/common.mk @@ -2,6 +2,8 @@ UNAME=$(shell uname) DEBUG=1 COVERAGE=0 INSTALL=install +FLEX=flex +BISON=bison ifndef PREFIX PREFIX=/usr endif @@ -66,6 +68,7 @@ CPPFLAGS += -DPCRE_HAS_UCP=1 endif LIBS += -lm +LIBS += -lrt LIBS += -L $(TOPDIR)/libi3 -li3 LIBS += $(call ldflags_for_lib, xcb-event,xcb-event) LIBS += $(call ldflags_for_lib, xcb-keysyms,xcb-keysyms) diff --git a/debian/changelog b/debian/changelog index 70897a74..c9fe5268 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +i3-wm (4.1.2-0) unstable; urgency=low + + * NOT YET RELEASED + + -- Michael Stapelberg Sat, 24 Dec 2011 16:29:16 +0100 + i3-wm (4.1.1-1) unstable; urgency=low * Re-add build-arch/build-indep targets to debian/rules (Closes: #648613) diff --git a/debian/control b/debian/control index e3786cfb..1119d69d 100644 --- a/debian/control +++ b/debian/control @@ -10,8 +10,8 @@ Homepage: http://i3wm.org/ Package: i3 Architecture: any Section: x11 -Depends: i3-wm, ${misc:Depends} -Recommends: i3lock, suckless-tools, i3status +Depends: i3-wm (=${binary:Version}), ${misc:Depends} +Recommends: i3lock (>= 2.2), suckless-tools, i3status (>= 2.3) Description: metapackage (i3 window manager, screen locker, menu, statusbar) This metapackage installs the i3 window manager (i3-wm), the i3lock screen locker, i3status (for system information) and suckless-tools (for dmenu). diff --git a/debian/i3-wm.docs b/debian/i3-wm.docs index e5896855..8d2662aa 100644 --- a/debian/i3-wm.docs +++ b/debian/i3-wm.docs @@ -17,3 +17,9 @@ docs/keyboard-layer2.png docs/testsuite.html docs/i3-sync-working.png docs/i3-sync.png +docs/tree-layout1.png +docs/tree-layout2.png +docs/tree-shot1.png +docs/tree-shot2.png +docs/tree-shot3.png +docs/tree-shot4.png diff --git a/debian/patches/manpage-x-terminal-emulator.patch b/debian/patches/manpage-x-terminal-emulator.patch index 61ff0160..ed6cb465 100644 --- a/debian/patches/manpage-x-terminal-emulator.patch +++ b/debian/patches/manpage-x-terminal-emulator.patch @@ -1,14 +1,12 @@ -## Description: Document Debian-specific x-terminal-emulator in the manpage. -## Origin/Author: Michael Stapelberg -Index: i3-4.1/man/i3-sensible-terminal.man +Index: i3-4.1.1/man/i3-sensible-terminal.man =================================================================== ---- i3-4.1.orig/man/i3-sensible-terminal.man 2011-11-11 22:38:06.508025537 +0000 -+++ i3-4.1/man/i3-sensible-terminal.man 2011-11-11 22:38:04.752994892 +0000 +--- i3-4.1.1.orig/man/i3-sensible-terminal.man 2011-12-28 23:56:55.487581000 +0100 ++++ i3-4.1.1/man/i3-sensible-terminal.man 2011-12-28 23:57:06.725802633 +0100 @@ -22,6 +22,7 @@ It tries to start one of the following (in that order): * $TERMINAL (this is a non-standard variable) -+* x-terminal-emulator (only on Debian) - * xterm ++* x-terminal-emulator * urxvt * rxvt + * terminator diff --git a/debian/patches/use-x-terminal-emulator.patch b/debian/patches/use-x-terminal-emulator.patch index 28e9200d..0d71f7c4 100644 --- a/debian/patches/use-x-terminal-emulator.patch +++ b/debian/patches/use-x-terminal-emulator.patch @@ -1,21 +1,17 @@ -## Description: Use Debian-specific x-terminal-emulator in i3-sensible-terminal -## Origin/Author: Michael Stapelberg ---- a/i3-sensible-terminal.O 2011-11-11 22:03:52.414218386 +0000 -+++ b/i3-sensible-terminal 2011-11-11 22:04:38.372020210 +0000 -@@ -1,13 +1,11 @@ - #!/bin/sh +Index: i3-4.1.1/i3-sensible-terminal +=================================================================== +--- i3-4.1.1.orig/i3-sensible-terminal 2011-12-28 23:51:52.455610236 +0100 ++++ i3-4.1.1/i3-sensible-terminal 2011-12-28 23:52:00.826775027 +0100 +@@ -4,11 +4,7 @@ + # # This script tries to exec a terminal emulator by trying some known terminal # emulators. -# -# Distributions/packagers should enhance this script with a -# distribution-specific mechanism to find the preferred terminal emulator. On -# Debian, there is the x-terminal-emulator symlink for example. --# Please don't touch the first line, though: - [ -n "$TERMINAL" ] && which $TERMINAL >/dev/null && exec $TERMINAL "$@" - -+# Debian-specific: use x-terminal-emulator -+which x-terminal-emulator >/dev/null && exec x-terminal-emulator "$@" -+ - # Hopefully one of these is installed: - which xterm >/dev/null && exec xterm "$@" - which urxvt >/dev/null && exec urxvt "$@" +-for terminal in $TERMINAL urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm; do ++for terminal in $TERMINAL x-terminal-emulator urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm; do + if which $terminal > /dev/null 2>&1; then + exec $terminal "$@" + fi diff --git a/docs/hacking-howto b/docs/hacking-howto index 9a7ec9d4..836d5701 100644 --- a/docs/hacking-howto +++ b/docs/hacking-howto @@ -63,57 +63,22 @@ to a specific file type, a window manager should not limit itself to a certain layout (like dwm, awesome, …) but provide mechanisms for you to easily create the layout you need at the moment. -=== The layout table +=== The layout tree -********************************************************************************* -This section has not been updated for v4.0 yet, sorry! We wanted to release on -time, but we will update this soon. Please talk to us on IRC if you need to -know stuff *NOW* :). -********************************************************************************* - -///////////////////////////////////////////////////////////////////////////////// -To accomplish flexible layouts, we decided to simply use a table. The table -grows and shrinks as you need it. Each cell holds a container which then holds -windows (see picture below). You can use different layouts for each container -(default layout and stacking layout). +The data structure which i3 uses to keep track of your windows is a tree. Every +node in the tree is a container (type +Con+). Some containers represent actual +windows (every container with a +window != NULL+), some represent split +containers and a few have special purposes: they represent workspaces, outputs +(like VGA1, LVDS1, …) or the X11 root window. So, when you open a terminal and immediately open another one, they reside in -the same container, in default layout. The layout table has exactly one column, -one row and therefore one cell. When you move one of the terminals to the -right, the table needs to grow. It will be expanded to two columns and one row. -This enables you to have different layouts for each container. The table then -looks like this: - -[width="15%",cols="^,^"] -|======== -| T1 | T2 -|======== - -When moving terminal 2 to the bottom, the table will be expanded again. - -[width="15%",cols="^,^"] -|======== -| T1 | -| | T2 -|======== - -You can really think of the layout table like a traditional HTML table, if -you’ve ever designed one. Especially col- and rowspan work similarly. Below, -you see an example of colspan=2 for the first container (which has T1 as -window). +the same split container, which uses the default layout. In case of an empty +workspace, the split container we are talking about is the workspace. -[width="15%",cols="^asciidoc"] -|======== -| T1 -| -[cols="^,^",frame="none"] -!======== -! T2 ! T3 -!======== -|======== - -Furthermore, you can freely resize table cells. -///////////////////////////////////////////////////////////////////////////////// +To get an impression of how different layouts are represented, just play around +and look at the data structures -- they are exposed as a JSON hash. See +http://i3wm.org/docs/ipc.html#_get_tree_reply for documentation on that and an +example. == Files @@ -160,7 +125,7 @@ src/debug.c:: Contains debugging functions to print unhandled X events. src/ewmh.c:: -iFunctions to get/set certain EWMH properties easily. +Functions to get/set certain EWMH properties easily. src/floating.c:: Contains functions for floating mode (mostly resizing/dragging). @@ -243,17 +208,13 @@ Legacy support for Xinerama. See +src/randr.c+ for the preferred API. == Data structures -********************************************************************************* -This section has not been updated for v4.0 yet, sorry! We wanted to release on -time, but we will update this soon. Please talk to us on IRC if you need to -know stuff *NOW* :). -********************************************************************************* - -///////////////////////////////////////////////////////////////////////////////// See include/data.h for documented data structures. The most important ones are explained right here. +///////////////////////////////////////////////////////////////////////////////// +// TODO: update image + image:bigpicture.png[The Big Picture] ///////////////////////////////////////////////////////////////////////////////// @@ -261,7 +222,7 @@ image:bigpicture.png[The Big Picture] So, the hierarchy is: . *X11 root window*, the root container -. *Virtual screens* (Screen 0 in this example) +. *Output container* (LVDS1 in this example) . *Content container* (there are also containers for dock windows) . *Workspaces* (Workspace 1 in this example, with horizontal orientation) . *Split container* (vertically split) @@ -269,68 +230,57 @@ So, the hierarchy is: The data type is +Con+, in all cases. -=== Virtual screens +=== X11 root window -A virtual screen (type `i3Screen`) is generated from the connected outputs -obtained through RandR. The difference to the raw RandR outputs as seen -when using +xrandr(1)+ is that it falls back to the lowest common resolution of -the actual enabled outputs. +The X11 root window is a single window per X11 display (a display is identified +by +:0+ or +:1+ etc.). The root window is what you draw your background image +on. It spans all the available outputs, e.g. +VGA1+ is a specific part of the +root window and +LVDS1+ is a specific part of the root window. + +=== Output container + +Every active output obtained through RandR is represented by one output +container. Outputs are considered active when a mode is configured (meaning +something is actually displayed on the output) and the output is not a clone. For example, if your notebook has a screen resolution of 1280x800 px and you connect a video projector with a resolution of 1024x768 px, set it up in clone -mode (+xrandr \--output VGA1 \--mode 1024x768 \--same-as LVDS1+), i3 will have -one virtual screen. +mode (+xrandr \--output VGA1 \--mode 1024x768 \--same-as LVDS1+), i3 will +reduce the resolution to the lowest common resolution and disable one of the +cloned outputs afterwards. However, if you configure it using +xrandr \--output VGA1 \--mode 1024x768 -\--right-of LVDS1+, i3 will generate two virtual screens. For each virtual -screen, a new workspace will be assigned. New workspaces are created on the -screen you are currently on. +\--right-of LVDS1+, i3 will set both outputs active. For each output, a new +workspace will be assigned. New workspaces are created on the output you are +currently on. + +=== Content container + +Each output has multiple children. Two of them are dock containers which hold +dock clients. The other one is the content container, which holds the actual +content (workspaces) of this output. === Workspace A workspace is identified by its name. Basically, you could think of workspaces as different desks in your office, if you like the desktop -methaphor. They just contain different sets of windows and are completely +metaphor. They just contain different sets of windows and are completely separate of each other. Other window managers also call this ``Virtual desktops''. -=== The layout table - -********************************************************************************* -This section has not been updated for v4.0 yet, sorry! We wanted to release on -time, but we will update this soon. Please talk to us on IRC if you need to -know stuff *NOW* :). -********************************************************************************* - -///////////////////////////////////////////////////////////////////////////////// - -Each workspace has a table, which is just a two-dimensional dynamic array -containing Containers (see below). This table grows and shrinks as you need it -(by moving windows to the right you can create a new column in the table, by -moving them to the bottom you create a new row). - -///////////////////////////////////////////////////////////////////////////////// - -=== Container - -********************************************************************************* -This section has not been updated for v4.0 yet, sorry! We wanted to release on -time, but we will update this soon. Please talk to us on IRC if you need to -know stuff *NOW* :). -********************************************************************************* +=== Split container -///////////////////////////////////////////////////////////////////////////////// - -A container is the content of a table’s cell. It holds an arbitrary amount of -windows and has a specific layout (default layout, stack layout or tabbed -layout). Containers can consume multiple table cells by modifying their -colspan/rowspan attribute. +A split container is a container which holds an arbitrary amount of split +containers or X11 window containers. It has an orientation (horizontal or +vertical) and a layout. -///////////////////////////////////////////////////////////////////////////////// +Split containers (and X11 window containers, which are a subtype of split +containers) can have different border styles. -=== Client +=== X11 window container -A client is x11-speak for a window. +An X11 window container holds exactly one X11 window. These are the leaf nodes +of the layout tree, they cannot have any children. == List/queue macros @@ -421,8 +371,9 @@ After reparenting, the window type (`_NET_WM_WINDOW_TYPE`) is checked to see whether this window is a dock (`_NET_WM_WINDOW_TYPE_DOCK`), like dzen2 for example. Docks are handled differently, they don’t have decorations and are not assigned to a specific container. Instead, they are positioned at the bottom -of the screen. To get the height which needs to be reserved for the window, -the `_NET_WM_STRUT_PARTIAL` property is used. +or top of the screen (in the appropriate dock area containers). To get the +height which needs to be reserved for the window, the `_NET_WM_STRUT_PARTIAL` +property is used. Furthermore, the list of assignments (to other workspaces, which may be on other screens) is checked. If the window matches one of the user’s criteria, @@ -484,64 +435,218 @@ src/layout.c, function resize_client(). == Rendering (src/layout.c, render_layout() and render_container()) -********************************************************************************* -This section has not been updated for v4.0 yet, sorry! We wanted to release on -time, but we will update this soon. Please talk to us on IRC if you need to -know stuff *NOW* :). -********************************************************************************* - -///////////////////////////////////////////////////////////////////////////////// - - -There are several entry points to rendering: `render_layout()`, -`render_workspace()` and `render_container()`. The former one calls -`render_workspace()` for every screen, which in turn will call -`render_container()` for every container inside its layout table. Therefore, if -you need to render only a single container, for example because a window was -removed, added or changed its title, you should directly call -render_container(). - -Rendering consists of two steps: In the first one, in `render_workspace()`, each -container gets its position (screen offset + offset in the table) and size -(container's width times colspan/rowspan). Then, `render_container()` is called, -which takes different approaches, depending on the mode the container is in: - -=== Common parts - -On the frame (the window which was created around the client’s window for the -decorations), a black rectangle is drawn as a background for windows like -MPlayer, which do not completely fit into the frame. - -=== Default mode - -Each clients gets the container’s width and an equal amount of height. - -=== Stack mode - -In stack mode, a window containing the decorations of all windows inside the -container is placed at the top. The currently focused window is then given the -whole remaining space. - -=== Tabbed mode - -Tabbed mode is like stack mode, except that the window decorations are drawn -in one single line at the top of the container. - -=== Window decorations +Rendering in i3 version 4 is the step which assigns the correct sizes for +borders, decoration windows, child windows and the stacking order of all +windows. In a separate step (+x_push_changes()+), these changes are pushed to +X11. + +Keep in mind that all these properties (+rect+, +window_rect+ and +deco_rect+) +are temporary, meaning they will be overwritten by calling +render_con+. +Persistent position/size information is kept in +geometry+. + +The entry point for every rendering operation (except for the case of moving +floating windows around) currently is +tree_render()+ which will re-render +everything that’s necessary (for every output, only the currently displayed +workspace is rendered). This behavior is expected to change in the future, +since for a lot of updates, re-rendering everything is not actually necessary. +Focus was on getting it working correct, not getting it work very fast. + +What +tree_render()+ actually does is calling +render_con()+ on the root +container and then pushing the changes to X11. The following sections talk +about the different rendering steps, in the order of "top of the tree" (root +container) to the bottom. + +=== Rendering the root container + +The i3 root container (`con->type == CT_ROOT`) represents the X11 root window. +It contains one child container for every output (like LVDS1, VGA1, …), which +is available on your computer. + +Rendering the root will first render all tiling windows and then all floating +windows. This is necessary because a floating window can be positioned in such +a way that it is visible on two different outputs. Therefore, by first +rendering all the tiling windows (of all outputs), we make sure that floating +windows can never be obscured by tiling windows. + +Essentially, though, this code path will just call +render_con()+ for every +output and +x_raise_con(); render_con()+ for every floating window. + +In the special case of having a "global fullscreen" window (fullscreen mode +spanning all outputs), a shortcut is taken and +x_raise_con(); render_con()+ is +only called for the global fullscreen window. + +=== Rendering an output + +Output containers (`con->layout == L_OUTPUT`) represent a hardware output like +LVDS1, VGA1, etc. An output container has three children (at the moment): One +content container (having workspaces as children) and the top/bottom dock area +containers. + +The rendering happens in the function +render_l_output()+ in the following +steps: + +1. Find the content container (`con->type == CT_CON`) +2. Get the currently visible workspace (+con_get_fullscreen_con(content, + CF_OUTPUT)+). +3. If there is a fullscreened window on that workspace, directly render it and + return, thus ignoring the dock areas. +4. Sum up the space used by all the dock windows (they have a variable height + only). +5. Set the workspace rects (x/y/width/height) based on the position of the + output (stored in `con->rect`) and the usable space + (`con->rect.{width,height}` without the space used for dock windows). +6. Recursively raise and render the output’s child containers (meaning dock + area containers and the content container). + +=== Rendering a workspace or split container + +From here on, there really is no difference anymore. All containers are of +`con->type == CT_CON` (whether workspace or split container) and some of them +have a `con->window`, meaning they represent an actual window instead of a +split container. + +==== Default layout + +In default layout, containers are placed horizontally or vertically next to +each other (depending on the `con->orientation`). If a child is a leaf node (as +opposed to a split container) and has border style "normal", appropriate space +will be reserved for its window decoration. + +==== Stacked layout + +In stacked layout, only the focused window is actually shown (this is achieved +by calling +x_raise_con()+ in reverse focus order at the end of +render_con()+). + +The available space for the focused window is the size of the container minus +the height of the window decoration for all windows inside this stacked +container. -The window decorations consist of a rectangle in the appropriate color (depends -on whether this window is the currently focused one, the last focused one in a -not focused container or not focused at all) forming the background. -Afterwards, two lighter lines are drawn and the last step is drawing the -window’s title (see WM_NAME) onto it. +If border style is "1pixel" or "none", no window decoration height will be +reserved (or displayed later on), unless there is more than one window inside +the stacked container. + +==== Tabbed layout + +Tabbed layout works precisely like stacked layout, but the window decoration +position/size is different: They are placed next to each other on a single line +(fixed height). + +==== Dock area layout + +This is a special case. Users cannot chose the dock area layout, but it will be +set for the dock area containers. In the dockarea layout (at the moment!), +windows will be placed above each other. + +=== Rendering a window + +A window’s size and position will be determined in the following way: + +1. Subtract the border if border style is not "none" (but "normal" or "1pixel"). +2. Subtract the X11 border, if the window has an X11 border > 0. +3. Obey the aspect ratio of the window (think MPlayer). +4. Obey the height- and width-increments of the window (think terminal emulator + which can only be resized in one-line or one-character steps). + +== Pushing updates to X11 / Drawing + +A big problem with i3 before version 4 was that we just sent requests to X11 +anywhere in the source code. This was bad because nobody could understand the +entirety of our interaction with X11, it lead to subtle bugs and a lot of edge +cases which we had to consider all over again. + +Therefore, since version 4, we have a single file, +src/x.c+, which is +responsible for repeatedly transferring parts of our tree datastructure to X11. + ++src/x.c+ consists of multiple parts: + +1. The state pushing: +x_push_changes()+, which calls +x_push_node()+. +2. State modification functions: +x_con_init+, +x_reinit+, + +x_reparent_child+, +x_move_win+, +x_con_kill+, +x_raise_con+, +x_set_name+ + and +x_set_warp_to+. +3. Expose event handling (drawing decorations): +x_deco_recurse()+ and + +x_draw_decoration()+. + +=== Pushing state to X11 + +In general, the function +x_push_changes+ should be called to push state +changes. Only when the scope of the state change is clearly defined (for +example only the title of a window) and its impact is known beforehand, one can +optimize this and call +x_push_node+ on the appropriate con directly. + ++x_push_changes+ works in the following steps: + +1. Clear the eventmask for all mapped windows. This leads to not getting + useless ConfigureNotify or EnterNotify events which are caused by our + requests. In general, we only want to handle user input. +2. Stack windows above each other, in reverse stack order (starting with the + most obscured/bottom window). This is relevant for floating windows which + can overlap each other, but also for tiling windows in stacked or tabbed + containers. We also update the +_NET_CLIENT_LIST_STACKING+ hint which is + necessary for tab drag and drop in Chromium. +3. +x_push_node+ will be called for the root container, recursively calling + itself for the container’s children. This function actually pushes the + state, see the next paragraph. +4. If the pointer needs to be warped to a different position (for example when + changing focus to a differnt output), it will be warped now. +5. The eventmask is restored for all mapped windows. +6. Window decorations will be rendered by calling +x_deco_recurse+ on the root + container, which then recursively calls itself for the children. +7. If the input focus needs to be changed (because the user focused a different + window), it will be updated now. +8. +x_push_node_unmaps+ will be called for the root container. This function + only pushes UnmapWindow requests. Separating the state pushing is necessary + to handle fullscreen windows (and workspace switches) in a smooth fashion: + The newly visible windows should be visible before the old windows are + unmapped. + ++x_push_node+ works in the following steps: + +1. Update the window’s +WM_NAME+, if changed (the +WM_NAME+ is set on i3 + containers mainly for debugging purposes). +2. Reparents a child window into the i3 container if the container was created + for a specific managed window. +3. If the size/position of the i3 container changed (due to opening a new + window or switching layouts for example), the window will be reconfigured. + Also, the pixmap which is used to draw the window decoration/border on is + reconfigured (pixmaps are size-dependent). +4. Size/position for the child window is adjusted. +5. The i3 container is mapped if it should be visible and was not yet mapped. + When mapping, +WM_STATE+ is set to +WM_STATE_NORMAL+. Also, the eventmask of + the child window is updated and the i3 container’s contents are copied from + the pixmap. +6. +x_push_node+ is called recursively for all children of the current + container. + ++x_push_node_unmaps+ handles the remaining case of an i3 container being +unmapped if it should not be visible anymore. +WM_STATE+ will be set to ++WM_STATE_WITHDRAWN+. + + +=== Drawing window decorations/borders/backgrounds + ++x_draw_decoration+ draws window decorations. It is run for every leaf +container (representing an actual X11 window) and for every non-leaf container +which is in a stacked/tabbed container (because stacked/tabbed containers +display a window decoration for split containers, which at the moment just says +"another container"). + +Then, parameters are collected to be able to determine whether this decoration +drawing is actually necessary or was already done. This saves a substantial +number of redraws (depending on your workload, but far over 50%). + +Assuming that we need to draw this decoration, we start by filling the empty +space around the child window (think of MPlayer with a specific aspect ratio) +in the user-configured client background color. + +Afterwards, we draw the appropriate border (in case of border styles "normal" +and "1pixel") and the top bar (in case of border style "normal"). + +The last step is drawing the window title on the top bar. -=== Fullscreen windows -For fullscreen windows, the `rect` (x, y, width, height) is not changed to -allow the client to easily go back to its previous position. Instead, -fullscreen windows are skipped when rendering. +///////////////////////////////////////////////////////////////////////////////// -=== Resizing containers +== Resizing containers By clicking and dragging the border of a container, you can resize the whole column (respectively row) which this container is in. This is necessary to keep @@ -567,41 +672,123 @@ floating windows: ///////////////////////////////////////////////////////////////////////////////// -== User commands / commandmode (src/cmdparse.{l,y}) - -********************************************************************************* -This section has not been updated for v4.0 yet, sorry! We wanted to release on -time, but we will update this soon. Please talk to us on IRC if you need to -know stuff *NOW* :). -********************************************************************************* - -///////////////////////////////////////////////////////////////////////////////// - - -Like in vim, you can control i3 using commands. They are intended to be a -powerful alternative to lots of shortcuts, because they can be combined. There -are a few special commands, which are the following: - -exec :: -Starts the given command by passing it to `/bin/sh`. - -restart:: -Restarts i3 by executing `argv[0]` (the path with which you started i3) without -forking. - -w:: -"With". This is used to select a bunch of windows. Currently, only selecting -the whole container in which the window is in, is supported by specifying "w". - -f, s, d:: -Toggle fullscreen, stacking, default mode for the current window/container. - -The other commands are to be combined with a direction. The directions are h, -j, k and l, like in vim (h = left, j = down, k = up, l = right). When you just -specify the direction keys, i3 will move the focus in that direction. You can -provide "m" or "s" before the direction to move a window respectively or snap. - -///////////////////////////////////////////////////////////////////////////////// +== User commands (parser-specs/commands.spec) + +In the configuration file and when using i3 interactively (with +i3-msg+, for +example), you use commands to make i3 do things, like focus a different window, +set a window to fullscreen, and so on. An example command is +floating enable+, +which enables floating mode for the currently focused window. See the +appropriate section in the link:userguide.html[User’s Guide] for a reference of +all commands. + +In earlier versions of i3, interpreting these commands was done using lex and +yacc, but experience has shown that lex and yacc are not well suited for our +command language. Therefore, starting from version 4.2, we use a custom parser +for user commands (not yet for the configuration file). +The input specification for this parser can be found in the file ++parser-specs/commands.spec+. Should you happen to use Vim as an editor, use +:source parser-specs/highlighting.vim to get syntax highlighting for this file +(highlighting files for other editors are welcome). + +.Excerpt from commands.spec +----------------------------------------------------------------------- +state INITIAL: + '[' -> call cmd_criteria_init(); CRITERIA + 'move' -> MOVE + 'exec' -> EXEC + 'workspace' -> WORKSPACE + 'exit' -> call cmd_exit() + 'restart' -> call cmd_restart() + 'reload' -> call cmd_reload() +----------------------------------------------------------------------- + +The input specification is written in an extremely simple format. The +specification is then converted into C code by the Perl script +generate-commands-parser.pl (the output file names begin with GENERATED and the +files are stored in the +include+ directory). The parser implementation ++src/commands_parser.c+ includes the generated C code at compile-time. + +The above excerpt from commands.spec illustrates nearly all features of our +specification format: You describe different states and what can happen within +each state. State names are all-caps; the state in the above excerpt is called +INITIAL. A list of tokens and their actions (separated by an ASCII arrow) +follows. In the excerpt, all tokens are literals, that is, simple text strings +which will be compared with the input. An action is either the name of a state +in which the parser will transition into, or the keyword 'call', followed by +the name of a function (and optionally a state). + +=== Example: The WORKSPACE state + +Let’s have a look at the WORKSPACE state, which is a good example of all +features. This is its definition: + +.WORKSPACE state (commands.spec) +---------------------------------------------------------------- +# workspace next|prev|next_on_output|prev_on_output +# workspace back_and_forth +# workspace +state WORKSPACE: + direction = 'next_on_output', 'prev_on_output', 'next', 'prev' + -> call cmd_workspace($direction) + 'back_and_forth' + -> call cmd_workspace_back_and_forth() + workspace = string + -> call cmd_workspace_name($workspace) +---------------------------------------------------------------- + +As you can see from the commands, there are multiple different valid variants +of the workspace command: + +workspace :: + The word 'workspace' can be followed by any of the tokens 'next', + 'prev', 'next_on_output' or 'prev_on_output'. This command will + switch to the next or previous workspace (optionally on the same + output). + + There is one function called +cmd_workspace+, which is defined + in +src/commands.c+. It will handle this kind of command. To know which + direction was specified, the direction token is stored on the stack + with the name "direction", which is what the "direction = " means in + the beginning. + + +NOTE: Note that you can specify multiple literals in the same line. This has + exactly the same effect as if you specified `direction = + 'next_on_output' -> call cmd_workspace($direction)` and so forth. + + +NOTE: Also note that the order of literals is important here: If 'next' were + ordered before 'next_on_output', then 'next_on_output' would never + match. + +workspace back_and_forth:: + This is a very simple case: When the literal 'back_and_forth' is found + in the input, the function +cmd_workspace_back_and_forth+ will be + called without parameters and the parser will return to the INITIAL + state (since no other state was specified). +workspace :: + In this case, the workspace command is followed by an arbitrary string, + possibly in quotes, for example "workspace 3" or "workspace bleh". + + This is the first time that the token is actually not a literal (not in + single quotes), but just called string. Other possible tokens are word + (the same as string, but stops matching at a whitespace) and end + (matches the end of the input). + +=== Introducing a new command + +The following steps have to be taken in order to properly introduce a new +command (or possibly extend an existing command): + +1. Define a function beginning with +cmd_+ in the file +src/commands.c+. Copy + the prototype of an existing function. +2. After adding a comment on what the function does, copy the comment and + function definition to +include/commands.h+. Make the comment in the header + file use double asterisks to make doxygen pick it up. +3. Write a test case (or extend an existing test case) for your feature, see + link:testsuite.html[i3 testsuite]. For now, it is sufficient to simply call + your command in all the various possible ways. +4. Extend the parser specification in +parser-specs/commands.spec+. Run the + testsuite and see if your new function gets called with the appropriate + arguments for the appropriate input. +5. Actually implement the feature. +6. Document the feature in the link:userguide.html[User’s Guide]. == Moving containers @@ -780,3 +967,73 @@ git format-patch origin ----------------------- Just send us the generated file via email. + +== Thought experiments + +In this section, we collect thought experiments, so that we don’t forget our +thoughts about specific topics. They are not necessary to get into hacking i3, +but if you are interested in one of the topics they cover, you should read them +before asking us why things are the way they are or why we don’t implement +things. + +=== Using cgroups per workspace + +cgroups (control groups) are a linux-only feature which provides the ability to +group multiple processes. For each group, you can individually set resource +limits, like allowed memory usage. Furthermore, and more importantly for our +purposes, they serve as a namespace, a label which you can attach to processes +and their children. + +One interesting use for cgroups is having one cgroup per workspace (or +container, doesn’t really matter). That way, you could set different priorities +and have a workspace for important stuff (say, writing a LaTeX document or +programming) and a workspace for unimportant background stuff (say, +JDownloader). Both tasks can obviously consume a lot of I/O resources, but in +this example it doesn’t really matter if JDownloader unpacks the download a +minute earlier or not. However, your compiler should work as fast as possible. +Having one cgroup per workspace, you would assign more resources to the +programming workspace. + +Another interesting feature is that an inherent problem of the workspace +concept could be solved by using cgroups: When starting an application on +workspace 1, then switching to workspace 2, you will get the application’s +window(s) on workspace 2 instead of the one you started it on. This is because +the window manager does not have any mapping between the process it starts (or +gets started in any way) and the window(s) which appear. + +Imagine for example using dmenu: The user starts dmenu by pressing Mod+d, dmenu +gets started with PID 3390. The user then decides to launch Firefox, which +takes a long time. So he enters firefox into dmenu and presses enter. Firefox +gets started with PID 4001. When it finally finishes loading, it creates an X11 +window and uses MapWindow to make it visible. This is the first time i3 +actually gets in touch with Firefox. It decides to map the window, but it has +no way of knowing that this window (even though it has the _NET_WM_PID property +set to 4001) belongs to the dmenu the user started before. + +How do cgroups help with this? Well, when pressing Mod+d to launch dmenu, i3 +would create a new cgroup, let’s call it i3-3390-1. It launches dmenu in that +cgroup, which gets PID 3390. As before, the user enters firefox and Firefox +gets launched with PID 4001. This time, though, the Firefox process with PID +4001 is *also* member of the cgroup i3-3390-1 (because fork()ing in a cgroup +retains the cgroup property). Therefore, when mapping the window, i3 can look +up in which cgroup the process is and can establish a mapping between the +workspace and the window. + +There are multiple problems with this approach: + +. Every application has to properly set +_NET_WM_PID+. This is acceptable and + patches can be written for the few applications which don’t set the hint yet. +. It does only work on Linux, since cgroups are a Linux-only feature. Again, + this is acceptable. +. The main problem is that some applications create X11 windows completely + independent of UNIX processes. An example for this is Chromium (or + gnome-terminal), which, when being started a second time, communicates with + the first process and lets the first process open a new window. Therefore, if + you have a Chromium window on workspace 2 and you are currently working on + workspace 3, starting +chromium+ does not lead to the desired result (the + window will open on workspace 2). + +Therefore, my conclusion is that the only proper way of fixing the "window gets +opened on the wrong workspace" problem is in the application itself. Most +modern applications support freedesktop startup-notifications which can be +used for this. diff --git a/docs/ipc b/docs/ipc index 61a85c0a..acb05321 100644 --- a/docs/ipc +++ b/docs/ipc @@ -1,7 +1,7 @@ IPC interface (interprocess communication) ========================================== Michael Stapelberg -October 2011 +December 2011 This document describes how to interface with i3 from a separate process. This is useful for example to remote-control i3 (to write test cases for example) or @@ -48,7 +48,7 @@ Currently implemented message types are the following: COMMAND (0):: The payload of the message is a command for i3 (like the commands you can bind to keys in the configuration file) and will be executed - directly after receiving it. There is no reply to this message. + directly after receiving it. GET_WORKSPACES (1):: Gets the current workspaces. The reply will be a JSON-encoded list of workspaces (see the reply section). @@ -70,6 +70,10 @@ GET_BAR_CONFIG (6):: Gets the configuration (as JSON map) of the workspace bar with the given ID. If no ID is provided, an array with all configured bar IDs is returned instead. +GET_LOG_MARKERS (7):: + Gets the SHM log markers for the current position, the last wrap, the + SHM segment name and segment size. This is necessary for tools like + i3-dump-log which want to display the SHM log. So, a typical message could look like this: -------------------------------------------------- @@ -113,18 +117,20 @@ The following reply types are implemented: COMMAND (0):: Confirmation/Error code for the COMMAND message. -GET_WORKSPACES (1):: +WORKSPACES (1):: Reply to the GET_WORKSPACES message. SUBSCRIBE (2):: Confirmation/Error code for the SUBSCRIBE message. -GET_OUTPUTS (3):: +OUTPUTS (3):: Reply to the GET_OUTPUTS message. -GET_TREE (4):: +TREE (4):: Reply to the GET_TREE message. -GET_MARKS (5):: +MARKS (5):: Reply to the GET_MARKS message. -GET_BAR_CONFIG (6):: +BAR_CONFIG (6):: Reply to the GET_BAR_CONFIG message. +LOG_MARKERS (7):: + Reply to the GET_LOG_MARKERS message. === COMMAND reply @@ -136,7 +142,7 @@ property is +success (bool)+, but this will be expanded in future versions. { "success": true } ------------------- -=== GET_WORKSPACES reply +=== WORKSPACES reply The reply consists of a serialized list of workspaces. Each workspace has the following properties: @@ -250,7 +256,7 @@ rect (map):: ] ------------------- -=== GET_TREE reply +=== TREE reply The reply consists of a serialized tree. Each node in the tree (representing one container) has at least the properties listed below. While the nodes might @@ -433,7 +439,7 @@ JSON dump: } ------------------------ -=== GET_MARKS reply +=== MARKS reply The reply consists of a single array of strings for each container that has a mark. The order of that array is undefined. If more than one container has the @@ -442,7 +448,7 @@ contents are not unique). If no window has a mark the response will be the empty array []. -=== GET_BAR_CONFIG reply +=== BAR_CONFIG reply This can be used by third-party workspace bars (especially i3bar, but others are free to implement compatible alternatives) to get the +bar+ block @@ -526,6 +532,40 @@ urgent_workspace_text/urgent_workspace_bar:: } -------------- +=== LOG_MARKERS reply + +Gets the SHM log markers for the current position, the last wrap, the +SHM segment name and segment size. This is necessary for tools like +i3-dump-log which want to display the SHM log. + +The reply is a JSON map with the following entries: + +shmname (string):: + The name of the SHM segment, will be of the format +/i3-log-+. +size (integer):: + The size (in bytes) of the SHM segment. If this is 0, SHM logging is + disabled. +offset_next_write (integer):: + The offset in the SHM segment at which the next write will happen. + Tools should start printing lines from here, since the bytes following + this offset are the oldest log lines. However, the first line might be + garbled, so it makes sense to skip all bytes until the first \0. +offset_last_wrap (integer):: + The offset in the SHM segment at which the last wrap occured. i3 only + stores entire messages in the SHM log, so it might waste a few bytes at + the end to be more efficient. Tools should not print content after the + offset_last_wrap. + +*Example*: +----------------------------- +{ + "offset_next_write":132839, + "offset_last_wrap":26214400, + "shmname":"/i3-log-3392", + "size":26214400 +} +----------------------------- + == Events [[events]] diff --git a/docs/userguide b/docs/userguide index e75f1ca1..270627ab 100644 --- a/docs/userguide +++ b/docs/userguide @@ -1,7 +1,7 @@ i3 User’s Guide =============== Michael Stapelberg -October 2011 +January 2012 This document contains all the information you need to configure and use the i3 window manager. If it does not, please contact us on IRC (preferred) or post your @@ -799,6 +799,29 @@ bar { } --------------------------- +=== i3bar command + +By default i3 will just pass +i3bar+ and let your shell handle the execution, +searching your +$PATH+ for a correct version. +If you have a different +i3bar+ somewhere or the binary is not in your +$PATH+ you can +tell i3 what to execute. + +The specified command will be passed to +sh -c+, so you can use globbing and +have to have correct quoting etc. + +*Syntax*: +---------------------- +i3bar_command command +---------------------- + +*Example*: +------------------------------------------------- +bar { + i3bar_command /home/user/bin/i3bar +} +------------------------------------------------- + +[[status_command]] === Statusline command i3bar can run a program and display every line of its +stdout+ output on the @@ -815,31 +838,41 @@ status_command command *Example*: ------------------------------------------------- -status_command i3status --config ~/.i3status.conf +bar { + status_command i3status --config ~/.i3status.conf +} ------------------------------------------------- === Display mode You can have i3bar either be visible permanently at one edge of the screen (+dock+ mode) or make it show up when you press your modifier key (+hide+ -mode). +mode). The modifier key can be configured using the +modifier+ option. The hide mode maximizes screen space that can be used for actual windows. Also, i3bar sends the +SIGSTOP+ and +SIGCONT+ signals to the statusline process to save battery power. -The default is dock mode. +The default is dock mode; in hide mode, the default modifier is Mod4 (usually +the windows key). *Syntax*: ---------------- mode +modifier ---------------- *Example*: ---------------- -mode hide +bar { + mode hide + modifier Mod1 +} ---------------- +Available modifiers are Mod1-Mod5, Shift, Control (see +xmodmap(1)+). + +[[i3bar_position]] === Position This option determines in which edge of the screen i3bar should show up. @@ -853,7 +886,9 @@ position *Example*: --------------------- -position top +bar { + position top +} --------------------- === Output(s) @@ -862,6 +897,9 @@ You can restrict i3bar to one or more outputs (monitors). The default is to handle all outputs. Restricting the outputs is useful for using different options for different outputs by using multiple 'bar' blocks. +To make a particular i3bar instance handle multiple outputs, specify the output +directive multiple times. + *Syntax*: --------------- output @@ -871,18 +909,20 @@ output ------------------------------- # big monitor: everything bar { - output HDMI2 - status_command i3status + # The display is connected either via HDMI or via DisplayPort + output HDMI2 + output DP2 + status_command i3status } # laptop monitor: bright colors and i3status with less modules. bar { - output LVDS1 - status_command i3status --config ~/.i3status-small.conf - colors { - background #000000 - statusline #ffffff - } + output LVDS1 + status_command i3status --config ~/.i3status-small.conf + colors { + background #000000 + statusline #ffffff + } } ------------------------------- @@ -902,10 +942,14 @@ tray_output *Example*: ------------------------- # disable system tray -tray_output none +bar { + tray_output none +} # show tray icons on the big monitor -tray_output HDMI2 +bar { + tray_output HDMI2 +} ------------------------- === Font @@ -920,7 +964,9 @@ font *Example*: -------------------------------------------------------------- -font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 +bar { + font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 +} -------------------------------------------------------------- === Workspace buttons @@ -937,7 +983,9 @@ workspace_buttons *Example*: -------------------- -workspace_buttons no +bar { + workspace_buttons no +} -------------------- === Colors @@ -950,19 +998,19 @@ background:: statusline:: Text color to be used for the statusline. focused_workspace:: - Text color/background color for a workspace button when the workspace + Border, background and text color for a workspace button when the workspace has focus. active_workspace:: - Text color/background color for a workspace button when the workspace + Border, background and text color for a workspace button when the workspace is active (visible) on some output, but the focus is on another one. You can only tell this apart from the focused workspace when you are using multiple monitors. inactive_workspace:: - Text color/background color for a workspace button when the workspace + Border, background and text color for a workspace button when the workspace does not have focus and is not active (visible) on any output. This will be the case for most workspaces. urgent_workspace:: - Text color/background color for workspaces which contain at least one + Border, background and text color for a workspace button when the workspace window with the urgency hint set. *Syntax*: @@ -971,20 +1019,22 @@ colors { background statusline - colorclass + colorclass } ---------------------------------------- -*Example*: +*Example (default colors)*: -------------------------------------- -colors { - background #000000 - statusline #ffffff - - focused_workspace #ffffff #285577 - active_workspace #ffffff #333333 - inactive_workspace #888888 #222222 - urgent_workspace #ffffff #900000 +bar { + colors { + background #000000 + statusline #ffffff + + focused_workspace #4c7899 #285577 #ffffff + active_workspace #333333 #5f676a #ffffff + inactive_workspace #333333 #222222 #888888 + urgent_workspace #2f343a #900000 #ffffff + } } -------------------------------------- @@ -1005,9 +1055,9 @@ specific workspace and immediately switch to that workspace, you can configure the following keybinding: *Example*: -------------------------------------------- -bindsym mod+x move workspace 3; workspace 3 -------------------------------------------- +-------------------------------------------------------- +bindsym mod+x move container to workspace 3; workspace 3 +-------------------------------------------------------- [[command_criteria]] @@ -1066,7 +1116,7 @@ exec [--no-startup-id] command bindsym mod+g exec gimp # Start the terminal emulator urxvt which is not yet startup-notification-aware -bindsym mod+enter exec --no-startup-id urxvt +bindsym mod+Return exec --no-startup-id urxvt ------------------------------ The +--no-startup-id+ parameter disables startup-notification support for this @@ -1123,7 +1173,8 @@ bindsym mod+t floating toggle === Focusing/Moving containers -To change the focus, use the focus command: +focus left+, +focus right+, +focus down+ and +focus up+. +To change the focus, use the focus command: +focus left+, +focus right+, +focus +down+ and +focus up+. There are a few special parameters you can use for the focus command: @@ -1138,6 +1189,9 @@ tiling:: Sets focus to the last focused tiling container. mode_toggle:: Toggles between floating/tiling containers. +output:: + Followed by a direction or an output name, this will focus the + corresponding output. For moving, use +move left+, +move right+, +move down+ and +move up+. @@ -1145,6 +1199,7 @@ For moving, use +move left+, +move right+, +move down+ and +move up+. ----------------------------------- focus focus +focus output <|output> move [ px] ----------------------------------- @@ -1165,6 +1220,12 @@ bindsym mod+u focus parent # Focus last floating/tiling container bindsym mod+g focus mode_toggle +# Focus the output right to the current one +bindsym mod+x focus output right + +# Focus the big output +bindsym mod+x focus output HDMI-2 + # Move container to the left, bottom, top, right: bindsym mod+j move left bindsym mod+k move down @@ -1180,22 +1241,29 @@ bindsym mod+j move left 20 px To change to a specific workspace, use the +workspace+ command, followed by the number or name of the workspace. To move containers to specific workspaces, use -+move workspace+. ++move container to workspace+. You can also switch to the next and previous workspace with the commands +workspace next+ and +workspace prev+, which is handy, for example, if you have workspace 1, 3, 4 and 9 and you want to cycle through them with a single key -combination. Similarly, you can use +move workspace next+ and +move workspace -prev+ to move a container to the next/previous workspace. +combination. To restrict those to the current output, use +workspace +next_on_output+ and +workspace prev_on_output+. Similarly, you can use +move +container to workspace next+ and +move container to workspace prev+ to move a +container to the next/previous workspace. [[back_and_forth]] To switch back to the previously focused workspace, use +workspace back_and_forth+. To move a container to another xrandr output such as +LVDS1+ or +VGA1+, you can -use the +move output+ command followed by the name of the target output. You -may also use +left+, +right+, +up+, +down+ instead of the xrandr output name to -move to the next output in the specified direction. +use the +move container to output+ command followed by the name of the target +output. You may also use +left+, +right+, +up+, +down+ instead of the xrandr +output name to move to the next output in the specified direction. + +To move a whole workspace to another xrandr output such as +LVDS1+ or +VGA1+, +you can use the +move workspace to output+ command followed by the name of the +target output. You may also use +left+, +right+, +up+, +down+ instead of the +xrandr output name to move to the next output in the specified direction. *Examples*: ------------------------- @@ -1203,12 +1271,15 @@ bindsym mod+1 workspace 1 bindsym mod+2 workspace 2 ... -bindsym mod+Shift+1 move workspace 1 -bindsym mod+Shift+2 move workspace 2 +bindsym mod+Shift+1 move container to workspace 1 +bindsym mod+Shift+2 move container to workspace 2 ... # switch between the current and the previously focused one bindsym mod+b workspace back_and_forth + +# move the whole workspace to the next output +bindsym mod+x move workspace to output right ------------------------- ==== Named workspaces @@ -1413,6 +1484,40 @@ bindsym mod+Shift+w reload bindsym mod+Shift+e exit ---------------------------- +=== Scratchpad + +There are two commands to use any existing window as scratchpad window. +move +scratchpad+ will move a window to the scratchpad workspace. This will make it +invisible until you show it again. There is no way to open that workspace. +Instead, when using +scratchpad show+, the window will be shown again, as a +floating window, centered on your current workspace (using +scratchpad show+ on +a visible scratchpad window will make it hidden again, so you can have a +keybinding to toggle). + +As the name indicates, this is useful for having a window with your favorite +editor always at hand. However, you can also use this for other permanently +running applications which you don’t want to see all the time: Your music +player, alsamixer, maybe even your mail client…? + +*Syntax*: +--------------- +move scratchpad + +scratchpad show +--------------- + +*Examples*: +------------------------------------------------ +# Make the currently focused window a scratchpad +bindsym mod+Shift+minus move scratchpad + +# Show the first scratchpad window +bindsym mod+minus scratchpad show + +# Show the sup-mail scratchpad window, if any. +bindsym mod4+s [title="^Sup ::"] scratchpad show +------------------------------------------------ + [[multi_monitor]] == Multiple monitors @@ -1534,14 +1639,13 @@ If you don’t already have your favorite way of generating such a status line this task. It was written in C with the goal of using as few syscalls as possible to reduce the time your CPU is woken up from sleep states. Because i3status only spits out text, you need to combine it with some other tool, like -i3bar. Use a pipe to connect them: +i3status | i3bar -d+. +i3bar. See <> for how to display i3status in i3bar. Regardless of which application you use to display the status line, you want to make sure that it registers as a dock window using EWMH hints. i3 will position the window either at the top or at the bottom of the screen, depending -on which hint the application sets. With i3bar, you can use +-d+ or +-dbottom+ -for positioning it at the bottom and +-dtop+ to position it at the top of the -screen. +on which hint the application sets. With i3bar, you can configure its position, +see <>. === Giving presentations (multi-monitor) diff --git a/generate-command-parser.pl b/generate-command-parser.pl new file mode 100755 index 00000000..175d7101 --- /dev/null +++ b/generate-command-parser.pl @@ -0,0 +1,204 @@ +#!/usr/bin/env perl +# vim:ts=4:sw=4:expandtab +# +# i3 - an improved dynamic tiling window manager +# © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE) +# +# generate-command-parser.pl: script to generate parts of the command parser +# from its specification file parser-specs/commands.spec. +# +# Requires only perl >= 5.10, no modules. + +use strict; +use warnings; +use Data::Dumper; +use v5.10; + +# reads in a whole file +sub slurp { + open my $fh, '<', shift; + local $/; + <$fh>; +} + +# Stores the different states. +my %states; + +# XXX: don’t hardcode input and output +my $input = '../parser-specs/commands.spec'; +my @raw_lines = split("\n", slurp($input)); +my @lines; + +# XXX: In the future, we might switch to a different way of parsing this. The +# parser is in many ways not good — one obvious one is that it is hand-crafted +# without a good reason, also it preprocesses lines and forgets about line +# numbers. Luckily, this is just an implementation detail and the specification +# for the i3 command parser is in-tree (not user input). +# -- michael, 2012-01-12 + +# First step of preprocessing: +# Join token definitions which are spread over multiple lines. +for my $line (@raw_lines) { + next if $line =~ /^\s*#/ || $line =~ /^\s*$/; + + if ($line =~ /^\s+->/) { + # This is a continued token definition, append this line to the + # previous one. + $lines[$#lines] = $lines[$#lines] . $line; + } else { + push @lines, $line; + next; + } +} + +# First step: We build up the data structure containing all states and their +# token rules. + +my $current_state; + +for my $line (@lines) { + if (my ($state) = ($line =~ /^state ([A-Z_]+):$/)) { + #say "got a new state: $state"; + $current_state = $state; + } else { + # Must be a token definition: + # [identifier = ] -> + #say "token definition: $line"; + + my ($identifier, $tokens, $action) = + ($line =~ / + ^\s* # skip leading whitespace + ([a-z_]+ \s* = \s*|) # optional identifier + (.*?) -> \s* # token + (.*) # optional action + /x); + + # Cleanup the identifier (if any). + $identifier =~ s/^\s*(\S+)\s*=\s*$/$1/g; + + # Cleanup the tokens (remove whitespace). + $tokens =~ s/\s*//g; + + # The default action is to stay in the current state. + $action = $current_state if length($action) == 0; + + #say "identifier = *$identifier*, token = *$tokens*, action = *$action*"; + for my $token (split(',', $tokens)) { + my $store_token = { + token => $token, + identifier => $identifier, + next_state => $action, + }; + if (exists $states{$current_state}) { + push @{$states{$current_state}}, $store_token; + } else { + $states{$current_state} = [ $store_token ]; + } + } + } +} + +# Second step: Generate the enum values for all states. + +# It is important to keep the order the same, so we store the keys once. +my @keys = keys %states; + +open(my $enumfh, '>', 'GENERATED_enums.h'); + +# XXX: we might want to have a way to do this without a trailing comma, but gcc +# seems to eat it. +say $enumfh 'typedef enum {'; +my $cnt = 0; +for my $state (@keys, '__CALL') { + say $enumfh " $state = $cnt,"; + $cnt++; +} +say $enumfh '} cmdp_state;'; +close($enumfh); + +# Third step: Generate the call function. +open(my $callfh, '>', 'GENERATED_call.h'); +say $callfh 'static char *GENERATED_call(const int call_identifier) {'; +say $callfh ' char *output = NULL;'; +say $callfh ' switch (call_identifier) {'; +my $call_id = 0; +for my $state (@keys) { + my $tokens = $states{$state}; + for my $token (@$tokens) { + next unless $token->{next_state} =~ /^call /; + my ($cmd) = ($token->{next_state} =~ /^call (.*)/); + my ($next_state) = ($cmd =~ /; ([A-Z_]+)$/); + $cmd =~ s/; ([A-Z_]+)$//; + # Go back to the INITIAL state unless told otherwise. + $next_state ||= 'INITIAL'; + my $fmt = $cmd; + # Replace the references to identified literals (like $workspace) with + # calls to get_string(). + $cmd =~ s/\$([a-z_]+)/get_string("$1")/g; + # Used only for debugging/testing. + $fmt =~ s/\$([a-z_]+)/%s/g; + $fmt =~ s/"([a-z0-9_]+)"/%s/g; + + say $callfh " case $call_id:"; + say $callfh '#ifndef TEST_PARSER'; + my $real_cmd = $cmd; + if ($real_cmd =~ /\(\)/) { + $real_cmd =~ s/\(/(¤t_match/; + } else { + $real_cmd =~ s/\(/(¤t_match, /; + } + say $callfh " output = $real_cmd;"; + say $callfh '#else'; + # debug + $cmd =~ s/[^(]+\(//; + $cmd =~ s/\)$//; + $cmd = ", $cmd" if length($cmd) > 0; + say $callfh qq| printf("$fmt\\n"$cmd);|; + say $callfh '#endif'; + say $callfh " state = $next_state;"; + say $callfh " break;"; + $token->{next_state} = "call $call_id"; + $call_id++; + } +} +say $callfh ' default:'; +say $callfh ' printf("BUG in the parser. state = %d\n", call_identifier);'; +say $callfh ' }'; +say $callfh ' return output;'; +say $callfh '}'; +close($callfh); + +# Fourth step: Generate the token datastructures. + +open(my $tokfh, '>', 'GENERATED_tokens.h'); + +for my $state (@keys) { + my $tokens = $states{$state}; + say $tokfh 'cmdp_token tokens_' . $state . '[' . scalar @$tokens . '] = {'; + for my $token (@$tokens) { + my $call_identifier = 0; + my $token_name = $token->{token}; + if ($token_name =~ /^'/) { + # To make the C code simpler, we leave out the trailing single + # quote of the literal. We can do strdup(literal + 1); then :). + $token_name =~ s/'$//; + } + my $next_state = $token->{next_state}; + if ($next_state =~ /^call /) { + ($call_identifier) = ($next_state =~ /^call ([0-9]+)$/); + $next_state = '__CALL'; + } + my $identifier = $token->{identifier}; + say $tokfh qq| { "$token_name", "$identifier", $next_state, { $call_identifier } }, |; + } + say $tokfh '};'; +} + +say $tokfh 'cmdp_token_ptr tokens[' . scalar @keys . '] = {'; +for my $state (@keys) { + my $tokens = $states{$state}; + say $tokfh ' { tokens_' . $state . ', ' . scalar @$tokens . ' },'; +} +say $tokfh '};'; + +close($tokfh); diff --git a/i3-config-wizard/Makefile b/i3-config-wizard/Makefile index 27d5bf54..75d4684f 100644 --- a/i3-config-wizard/Makefile +++ b/i3-config-wizard/Makefile @@ -26,12 +26,12 @@ $(TOPDIR)/libi3/%.a: $(TOPDIR)/libi3/*.c cfgparse.yy.o: cfgparse.l cfgparse.y.o ${HEADERS} echo "[i3-config-wizard] LEX $<" - flex -i -o$(@:.o=.c) $< + $(FLEX) -i -o$(@:.o=.c) $< $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $(@:.o=.c) cfgparse.y.o: cfgparse.y ${HEADERS} echo "[i3-config-wizard] YACC $<" - bison --debug --verbose -b $(basename $< .y) -d $< + $(BISON) --debug --verbose -b $(basename $< .y) -d $< $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $(<:.y=.tab.c) diff --git a/i3-config-wizard/main.c b/i3-config-wizard/main.c index cdce0653..ac596024 100644 --- a/i3-config-wizard/main.c +++ b/i3-config-wizard/main.c @@ -112,13 +112,15 @@ static int handle_expose() { xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){ get_colorpixel("#000000") }); xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &border); - xcb_change_gc(conn, pixmap_gc, XCB_GC_FONT, (uint32_t[]){ font.id }); + set_font(&font); -#define txt(x, row, text) xcb_image_text_8(conn, strlen(text), pixmap, pixmap_gc, x, (row * font.height) + 2, text) +#define txt(x, row, text) \ + draw_text(text, strlen(text), false, pixmap, pixmap_gc,\ + x, (row - 1) * font.height + 4, 300 - x * 2) if (current_step == STEP_WELCOME) { /* restore font color */ - xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){ get_colorpixel("#FFFFFF") }); + set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000")); txt(10, 2, "You have not configured i3 yet."); txt(10, 3, "Do you want me to generate ~/.i3/config?"); @@ -126,16 +128,16 @@ static int handle_expose() { txt(85, 7, "No, I will use the defaults"); /* green */ - xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){ get_colorpixel("#00FF00") }); + set_font_colors(pixmap_gc, get_colorpixel("#00FF00"), get_colorpixel("#000000")); txt(25, 5, ""); /* red */ - xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){ get_colorpixel("#FF0000") }); + set_font_colors(pixmap_gc, get_colorpixel("#FF0000"), get_colorpixel("#000000")); txt(31, 7, ""); } if (current_step == STEP_GENERATE) { - xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){ get_colorpixel("#FFFFFF") }); + set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000")); txt(10, 2, "Please choose either:"); txt(85, 4, "Win as default modifier"); @@ -150,19 +152,19 @@ static int handle_expose() { else txt(31, 4, ""); /* the selected modifier */ - xcb_change_gc(conn, pixmap_gc, XCB_GC_FONT, (uint32_t[]){ bold_font.id }); + set_font(&bold_font); + set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000")); if (modifier == MOD_Mod4) txt(31, 4, ""); else txt(31, 5, ""); /* green */ - xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_FONT, - (uint32_t[]) { get_colorpixel("#00FF00"), font.id }); - + set_font(&font); + set_font_colors(pixmap_gc, get_colorpixel("#00FF00"), get_colorpixel("#000000")); txt(25, 9, ""); /* red */ - xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){ get_colorpixel("#FF0000") }); + set_font_colors(pixmap_gc, get_colorpixel("#FF0000"), get_colorpixel("#000000")); txt(31, 10, ""); } @@ -283,7 +285,7 @@ static void finish() { char *line = NULL; size_t len = 0; -#if !defined(__APPLE__) +#if !defined(__APPLE__) && (!defined(__FreeBSD__) || __FreeBSD_version >= 800000) ssize_t read; #endif bool head_of_file = true; @@ -296,7 +298,7 @@ static void finish() { fputs("# this file and re-run i3-config-wizard(1).\n", ks_config); fputs("#\n", ks_config); -#if defined(__APPLE__) +#if defined(__APPLE__) || (defined(__FreeBSD__) && __FreeBSD_version < 800000) while ((line = fgetln(kc_config, &len)) != NULL) { #else while ((read = getline(&line, &len, kc_config)) != -1) { @@ -408,7 +410,7 @@ int main(int argc, char *argv[]) { unlink(config_path); if (socket_path == NULL) - socket_path = socket_path_from_x11(); + socket_path = root_atom_contents("I3_SOCKET_PATH"); if (socket_path == NULL) socket_path = "/tmp/i3-ipc.sock"; diff --git a/i3-dump-log/Makefile b/i3-dump-log/Makefile new file mode 100644 index 00000000..18076e51 --- /dev/null +++ b/i3-dump-log/Makefile @@ -0,0 +1,32 @@ +# Default value so one can compile i3-dump-log standalone +TOPDIR=.. + +include $(TOPDIR)/common.mk + +CFLAGS += -I$(TOPDIR)/include + +# Depend on the object files of all source-files in src/*.c and on all header files +FILES=$(patsubst %.c,%.o,$(wildcard *.c)) +HEADERS=$(wildcard *.h) + +# Depend on the specific file (.c for each .o) and on all headers +%.o: %.c ${HEADERS} + echo "[i3-dump-log] CC $<" + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +all: i3-dump-log + +i3-dump-log: ${FILES} + echo "[i3-dump-log] LINK i3-dump-log" + $(CC) $(LDFLAGS) -o i3-dump-log ${FILES} $(LIBS) + +install: all + echo "[i3-dump-log] INSTALL" + $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin + $(INSTALL) -m 0755 i3-dump-log $(DESTDIR)$(PREFIX)/bin/ + +clean: + rm -f *.o + +distclean: clean + rm -f i3-dump-log diff --git a/i3-dump-log/main.c b/i3-dump-log/main.c new file mode 100644 index 00000000..30dd514f --- /dev/null +++ b/i3-dump-log/main.c @@ -0,0 +1,108 @@ +/* + * vim:ts=4:sw=4:expandtab + * + * i3 - an improved dynamic tiling window manager + * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE) + * + * i3-dump-log/main.c: Dumps the i3 SHM log to stdout. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libi3.h" +#include "shmlog.h" +#include + +int main(int argc, char *argv[]) { + int o, option_index = 0; + bool verbose = false; + + static struct option long_options[] = { + {"version", no_argument, 0, 'v'}, + {"verbose", no_argument, 0, 'V'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0} + }; + + char *options_string = "s:vVh"; + + while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) { + if (o == 'v') { + printf("i3-dump-log " I3_VERSION "\n"); + return 0; + } else if (o == 'V') { + verbose = true; + } else if (o == 'h') { + printf("i3-dump-log " I3_VERSION "\n"); + printf("i3-dump-log [-s ]\n"); + return 0; + } + } + + char *shmname = root_atom_contents("I3_SHMLOG_PATH"); + if (shmname == NULL) + errx(EXIT_FAILURE, "Cannot get I3_SHMLOG_PATH atom contents. Is i3 running on this display?"); + + if (*shmname == '\0') + errx(EXIT_FAILURE, "Cannot dump log: SHM logging is disabled in i3."); + + struct stat statbuf; + + int logbuffer_shm = shm_open(shmname, O_RDONLY, 0); + if (logbuffer_shm == -1) + err(EXIT_FAILURE, "Could not shm_open SHM segment for the i3 log (%s)", shmname); + + if (fstat(logbuffer_shm, &statbuf) != 0) + err(EXIT_FAILURE, "stat(%s)", shmname); + + char *logbuffer = mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, logbuffer_shm, 0); + if (logbuffer == MAP_FAILED) + err(EXIT_FAILURE, "Could not mmap SHM segment for the i3 log"); + + i3_shmlog_header *header = (i3_shmlog_header*)logbuffer; + + if (verbose) + printf("next_write = %d, last_wrap = %d, logbuffer_size = %d, shmname = %s\n", + header->offset_next_write, header->offset_last_wrap, header->size, shmname); + int chars; + char *walk = logbuffer + header->offset_next_write; + /* Skip the first line, it very likely is mangled. Not a problem, though, + * the log is chatty enough to have plenty lines left. */ + while (*walk != '\0') + walk++; + + /* Print the oldest log lines. We use printf("%s") to stop on \0. */ + while (walk < (logbuffer + header->offset_last_wrap)) { + chars = printf("%s", walk); + /* Shortcut: If there are two consecutive \0 bytes, this part of the + * buffer was never touched. To not call printf() for every byte of the + * buffer, we directly exit the loop. */ + if (*walk == '\0' && *(walk+1) == '\0') + break; + walk += (chars > 0 ? chars : 1); + } + + /* Then start from the beginning and print the newer lines */ + walk = logbuffer + sizeof(i3_shmlog_header); + while (walk < (logbuffer + header->offset_next_write)) { + chars = printf("%s", walk); + walk += (chars > 0 ? chars : 1); + } + + return 0; +} diff --git a/i3-input/i3-input.h b/i3-input/i3-input.h index d97807d1..f494cbd5 100644 --- a/i3-input/i3-input.h +++ b/i3-input/i3-input.h @@ -14,7 +14,4 @@ while (0) extern xcb_window_t root; -char *convert_ucs_to_utf8(char *input); -char *convert_utf8_to_ucs2(char *input, int *real_strlen); - #endif diff --git a/i3-input/main.c b/i3-input/main.c index def68481..b5709523 100644 --- a/i3-input/main.c +++ b/i3-input/main.c @@ -50,7 +50,7 @@ static char *glyphs_utf8[512]; static int input_position; static i3Font font; static char *prompt; -static int prompt_len; +static size_t prompt_len; static int limit; xcb_window_t root; xcb_connection_t *conn; @@ -94,7 +94,9 @@ static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &inner); /* restore font color */ - xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){ get_colorpixel("#FFFFFF") }); + set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000")); + + /* draw the text */ uint8_t *con = concat_strings(glyphs_ucs, input_position); char *full_text = (char*)con; if (prompt != NULL) { @@ -104,8 +106,8 @@ static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t memcpy(full_text, prompt, prompt_len * 2); memcpy(full_text + (prompt_len * 2), con, input_position * 2); } - xcb_image_text_16(conn, input_position + prompt_len, pixmap, pixmap_gc, 4 /* X */, - font.height + 2 /* Y = baseline of font */, (xcb_char2b_t*)full_text); + if (input_position + prompt_len != 0) + draw_text(full_text, input_position + prompt_len, true, pixmap, pixmap_gc, 4, 4, 492); /* Copy the contents of the pixmap to the real window */ xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, /* */ 500, font.height + 8); @@ -260,14 +262,14 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press printf("inp[0] = %02x, inp[1] = %02x, inp[2] = %02x\n", inp[0], inp[1], inp[2]); /* convert it to UTF-8 */ - char *out = convert_ucs_to_utf8((char*)inp); + char *out = convert_ucs2_to_utf8((xcb_char2b_t*)inp, 1); printf("converted to %s\n", out); glyphs_ucs[input_position] = malloc(3 * sizeof(uint8_t)); if (glyphs_ucs[input_position] == NULL) err(EXIT_FAILURE, "malloc() failed\n"); memcpy(glyphs_ucs[input_position], inp, 3); - glyphs_utf8[input_position] = strdup(out); + glyphs_utf8[input_position] = out; input_position++; if (input_position == limit) @@ -340,7 +342,7 @@ int main(int argc, char *argv[]) { printf("using format \"%s\"\n", format); if (socket_path == NULL) - socket_path = socket_path_from_x11(); + socket_path = root_atom_contents("I3_SOCKET_PATH"); if (socket_path == NULL) socket_path = "/tmp/i3-ipc.sock"; @@ -348,7 +350,7 @@ int main(int argc, char *argv[]) { sockfd = ipc_connect(socket_path); if (prompt != NULL) - prompt = convert_utf8_to_ucs2(prompt, &prompt_len); + prompt = (char*)convert_utf8_to_ucs2(prompt, &prompt_len); int screens; conn = xcb_connect(NULL, &screens); @@ -361,6 +363,7 @@ int main(int argc, char *argv[]) { symbols = xcb_key_symbols_alloc(conn); font = load_font(pattern, true); + set_font(&font); /* Open an input window */ win = xcb_generate_id(conn); @@ -393,9 +396,6 @@ int main(int argc, char *argv[]) { * this for us) */ xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, win, XCB_CURRENT_TIME); - /* Create graphics context */ - xcb_change_gc(conn, pixmap_gc, XCB_GC_FONT, (uint32_t[]){ font.id }); - /* Grab the keyboard to get all input */ xcb_flush(conn); diff --git a/i3-input/ucs2_to_utf8.c b/i3-input/ucs2_to_utf8.c deleted file mode 100644 index df112eef..00000000 --- a/i3-input/ucs2_to_utf8.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * vim:ts=4:sw=4:expandtab - * - * i3 - an improved dynamic tiling window manager - * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE) - * - * ucs2_to_utf8.c: Converts between UCS-2 and UTF-8, both of which are used in - * different contexts in X11. - * - */ -#include -#include -#include -#include -#include - -#include "libi3.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 = scalloc(buffer_size); - 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) - errx(EXIT_FAILURE, "Error opening the conversion context"); - } - - /* 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) { - free(buffer); - 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 = smalloc(buffer_size); - 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) - errx(EXIT_FAILURE, "Error opening the conversion context"); - } - - /* 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"); - free(buffer); - if (real_strlen != NULL) - *real_strlen = 0; - return NULL; - } - - if (real_strlen != NULL) - *real_strlen = ((buffer_size - output_size) / 2) - 1; - - return buffer; -} - diff --git a/i3-migrate-config-to-v4 b/i3-migrate-config-to-v4 index 4f4d0134..c8ff41c9 100755 --- a/i3-migrate-config-to-v4 +++ b/i3-migrate-config-to-v4 @@ -316,10 +316,10 @@ sub convert_command { if ($command =~ /^m[0-9]+/) { my ($number) = ($command =~ /^m([0-9]+)/); if (exists $workspace_names{$number}) { - print qq|$statement $key move workspace $workspace_names{$number}\n|; + print qq|$statement $key move container to workspace $workspace_names{$number}\n|; return; } else { - print qq|$statement $key move workspace $number\n|; + print qq|$statement $key move container to workspace $number\n|; return; } } diff --git a/i3-msg/main.c b/i3-msg/main.c index 6a3b29d3..ccf6e10f 100644 --- a/i3-msg/main.c +++ b/i3-msg/main.c @@ -91,7 +91,7 @@ int main(int argc, char *argv[]) { } if (socket_path == NULL) - socket_path = socket_path_from_x11(); + socket_path = root_atom_contents("I3_SOCKET_PATH"); /* Fall back to the default socket path */ if (socket_path == NULL) diff --git a/i3-nagbar/main.c b/i3-nagbar/main.c index 4d4e253a..1dbd7736 100644 --- a/i3-nagbar/main.c +++ b/i3-nagbar/main.c @@ -131,17 +131,15 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) { xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &rect); /* restore font color */ - uint32_t values[3]; - values[0] = color_text; - values[1] = color_background; - xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND, values); - xcb_image_text_8(conn, strlen(prompt), pixmap, pixmap_gc, 4 + 4/* X */, - font.height + 2 + 4 /* Y = baseline of font */, prompt); + set_font_colors(pixmap_gc, color_text, color_background); + draw_text(prompt, strlen(prompt), false, pixmap, pixmap_gc, + 4 + 4, 4 + 4, rect.width - 4 - 4); /* render close button */ int line_width = 4; int w = 20; int y = rect.width; + uint32_t values[3]; values[0] = color_button_background; values[1] = line_width; xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_LINE_WIDTH, values); @@ -159,12 +157,10 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) { }; xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, pixmap, pixmap_gc, 5, points); - values[0] = color_text; - values[1] = color_button_background; - values[2] = 1; - xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_LINE_WIDTH, values); - xcb_image_text_8(conn, strlen("x"), pixmap, pixmap_gc, y - w - line_width + (w / 2) - 4/* X */, - font.height + 2 + 4 - 1/* Y = baseline of font */, "X"); + values[0] = 1; + set_font_colors(pixmap_gc, color_text, color_button_background); + draw_text("X", 1, false, pixmap, pixmap_gc, y - w - line_width + w / 2 - 4, + 4 + 4 - 1, rect.width - y + w + line_width - w / 2 + 4); y -= w; y -= 20; @@ -193,9 +189,9 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) { values[0] = color_text; values[1] = color_button_background; - xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND, values); - xcb_image_text_8(conn, strlen(buttons[c].label), pixmap, pixmap_gc, y - w - line_width + 6/* X */, - font.height + 2 + 3/* Y = baseline of font */, buttons[c].label); + set_font_colors(pixmap_gc, color_text, color_button_background); + draw_text(buttons[c].label, strlen(buttons[c].label), false, pixmap, pixmap_gc, + y - w - line_width + 6, 4 + 3, rect.width - y + w + line_width - 6); y -= w; } @@ -256,7 +252,7 @@ int main(int argc, char *argv[]) { break; case 'h': printf("i3-nagbar " I3_VERSION "\n"); - printf("i3-nagbar [-m ] [-b