]> git.sur5r.net Git - i3/i3/commitdiff
Merge branch 'master' into next
authorMichael Stapelberg <michael@stapelberg.de>
Wed, 30 Nov 2011 20:10:45 +0000 (20:10 +0000)
committerMichael Stapelberg <michael@stapelberg.de>
Wed, 30 Nov 2011 20:10:45 +0000 (20:10 +0000)
109 files changed:
.gitignore
Makefile
common.mk
debian/changelog
debian/control
debian/i3-wm.docs
debian/patches/use-x-terminal-emulator.patch
docs/hacking-howto
docs/userguide
i3-config-wizard/Makefile
i3-config-wizard/main.c
i3-input/i3-input.h
i3-input/main.c
i3-input/ucs2_to_utf8.c [deleted file]
i3-nagbar/main.c
i3-sensible-editor
i3-sensible-pager
i3-sensible-terminal
i3bar/include/common.h
i3bar/include/ucs2_to_utf8.h [deleted file]
i3bar/include/xcb.h
i3bar/src/ucs2_to_utf8.c [deleted file]
i3bar/src/workspaces.c
i3bar/src/xcb.c
include/config.h
include/data.h
include/libi3.h
include/util.h
include/xcb.h
libi3/font.c [new file with mode: 0644]
libi3/load_font.c [deleted file]
libi3/ucs2_conversion.c [new file with mode: 0644]
man/i3.man
src/cfgparse.l
src/cfgparse.y
src/config.c
src/main.c
src/render.c
src/sighandler.c
src/util.c
src/window.c
src/x.c
src/xcb.c
testcases/Makefile.PL
testcases/complete-run.pl
testcases/lib/SocketActivation.pm
testcases/lib/i3test.pm
testcases/t/001-tile.t
testcases/t/002-i3-sync.t
testcases/t/003-ipc.t
testcases/t/004-unmanaged.t
testcases/t/005-floating.t
testcases/t/100-fullscreen.t
testcases/t/101-focus.t
testcases/t/102-dock.t
testcases/t/103-move.t
testcases/t/104-focus-stack.t
testcases/t/105-stacking.t
testcases/t/111-goto.t
testcases/t/112-floating-resize.t
testcases/t/113-urgent.t
testcases/t/114-client-leader.t
testcases/t/116-nestedcons.t
testcases/t/117-workspace.t
testcases/t/119-match.t
testcases/t/122-split.t
testcases/t/124-move.t
testcases/t/127-regress-floating-parent.t
testcases/t/128-open-order.t
testcases/t/129-focus-after-close.t
testcases/t/130-close-empty-split.t
testcases/t/132-move-workspace.t
testcases/t/133-size-hints.t
testcases/t/135-floating-focus.t
testcases/t/136-floating-ws-empty.t
testcases/t/137-floating-unmap.t
testcases/t/138-floating-attach.t
testcases/t/139-ws-numbers.t
testcases/t/140-focus-lost.t
testcases/t/141-resize.t
testcases/t/144-regress-floating-resize.t
testcases/t/145-flattening.t
testcases/t/146-floating-reinsert.t
testcases/t/147-regress-floatingmove.t
testcases/t/148-regress-floatingmovews.t
testcases/t/150-regress-dock-restart.t
testcases/t/153-floating-originalsize.t
testcases/t/154-regress-multiple-dock.t
testcases/t/155-floating-split-size.t
testcases/t/156-fullscreen-focus.t
testcases/t/157-regress-fullscreen-level-up.t
testcases/t/158-wm_take_focus.t
testcases/t/161-regress-borders-restart.t
testcases/t/162-regress-dock-urgent.t
testcases/t/163-wm-state.t
testcases/t/164-kill-win-vs-client.t
testcases/t/165-for_window.t
testcases/t/166-assign.t
testcases/t/167-workspace_layout.t
testcases/t/168-regress-fullscreen-restart.t
testcases/t/170-force_focus_wrapping.t
testcases/t/172-start-on-named-ws.t
testcases/t/173-get-marks.t
testcases/t/173-regress-focus-assign.t
testcases/t/174-border-config.t
testcases/t/175-startup-notification.t
testcases/t/176-workspace-baf.t
testcases/t/180-fd-leaks.t [new file with mode: 0644]
testcases/t/181-regress-float-border.t [new file with mode: 0644]

index cad6ad9a2120daffdde2a58c3b705fa253c0a77a..b641592b9069e8222382d3bd2ca23d512753cf52 100644 (file)
@@ -7,6 +7,7 @@ loglevels.tmp
 *.gcno
 testcases/testsuite-*
 testcases/latest
+testcases/Makefile
 *.output
 *.tab.*
 *.yy.c
index c0797839c97e9ffe5cd61ad98908b0caac8f0447..f8ace071fd704dfeb84a653b55818eb5c69b27cb 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -55,23 +55,23 @@ loglevels.h:
 
 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)
 
 
index a9d1661879f5c9f55d8e396f6bb71a5624988ee1..4394bc41958b7436341fabf212b62affdfbaba76 100644 (file)
--- 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
index be459af507f92a103d9a2c1b42f616358210e5f3..349911075937184436ce0e2cca77fae7afd44b48 100644 (file)
@@ -1,3 +1,9 @@
+i3-wm (4.1.1-0) unstable; urgency=low
+
+  * NOT YET RELEASED
+
+ -- Michael Stapelberg <michael@stapelberg.de>  Fri, 11 Nov 2011 23:00:04 +0000
+
 i3-wm (4.1-1) unstable; urgency=low
 
   * Switch to dpkg-source 3.0 (quilt) and compat level 7
index e3786cfb4f19854133381b873192cdd4b91f7cd1..1119d69d3bfa9722f555de79b7212986edc461b9 100644 (file)
@@ -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).
index e5896855a0c396d0e49fc3741669a71d0da233d3..8d2662aaadb311c5f57bf9444f987d3c4106e87f 100644 (file)
@@ -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
index fd515c168d84a4f6c7e30271622a428d776ceeba..28e9200df49c85caf2fc785e62ffdef694f79cb8 100644 (file)
@@ -11,7 +11,7 @@
 -# 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:
- which $TERMINAL >/dev/null && exec $TERMINAL "$@"
[ -n "$TERMINAL" ] && which $TERMINAL >/dev/null && exec $TERMINAL "$@"
  
 +# Debian-specific: use x-terminal-emulator
 +which x-terminal-emulator >/dev/null && exec x-terminal-emulator "$@"
index 9a7ec9d4547fd1ce421bd81e14b1bcbc5c3ff74d..1377d87c8c4f50086df323e7775b7d715458738b 100644 (file)
@@ -160,7 +160,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 +243,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 +257,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 +265,57 @@ So, the hierarchy is:
 
 The data type is +Con+, in all cases.
 
-=== Virtual screens
+=== X11 root window
+
+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.
 
-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.
+=== 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
+=== Split 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* :).
-*********************************************************************************
-
-/////////////////////////////////////////////////////////////////////////////////
-
-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).
+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.
 
-=== Container
+=== X11 window 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* :).
-*********************************************************************************
-
-/////////////////////////////////////////////////////////////////////////////////
-
-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.
-
-/////////////////////////////////////////////////////////////////////////////////
-
-=== Client
-
-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 +406,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 +470,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
@@ -780,3 +920,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.
index 3d78d16f83020e1f838df9cd119dd7d24eb7bdd1..26c12b77ce62434e753e9b921241f833c2facdd7 100644 (file)
@@ -174,7 +174,7 @@ Floating windows are always on top of tiling windows.
 i3 stores all information about the X11 outputs, workspaces and layout of the
 windows on them in a tree. The root node is the X11 root window, followed by
 the X11 outputs, then dock areas and a content container, then workspaces and
-finally the windows themselve. In previous versions of i3 we had multiple lists
+finally the windows themselves. In previous versions of i3 we had multiple lists
 (of outputs, workspaces) and a table for each workspace. That approach turned
 out to be complicated to use (snapping), understand and implement.
 
@@ -796,6 +796,28 @@ 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
+}
+-------------------------------------------------
+
 === Statusline command
 
 i3bar can run a program and display every line of its +stdout+ output on the
@@ -812,7 +834,9 @@ status_command command
 
 *Example*:
 -------------------------------------------------
-status_command i3status --config ~/.i3status.conf
+bar {
+    status_command i3status --config ~/.i3status.conf
+}
 -------------------------------------------------
 
 === Display mode
@@ -834,7 +858,9 @@ mode <dock|hide>
 
 *Example*:
 ----------------
-mode hide
+bar {
+    mode hide
+}
 ----------------
 
 === Position
@@ -850,7 +876,9 @@ position <top|bottom>
 
 *Example*:
 ---------------------
-position top
+bar {
+    position top
+}
 ---------------------
 
 === Output(s)
@@ -859,6 +887,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 <output>
@@ -868,18 +899,20 @@ output <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
+    }
 }
 -------------------------------
 
@@ -899,10 +932,14 @@ tray_output <none|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
@@ -917,7 +954,9 @@ font <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
@@ -934,7 +973,9 @@ workspace_buttons <yes|no>
 
 *Example*:
 --------------------
-workspace_buttons no
+bar {
+    workspace_buttons no
+}
 --------------------
 
 === Colors
@@ -974,14 +1015,16 @@ colors {
 
 *Example*:
 --------------------------------------
-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  #ffffff #285577
+        active_workspace   #ffffff #333333
+        inactive_workspace #888888 #222222
+        urgent_workspace   #ffffff #900000
+    }
 }
 --------------------------------------
 
@@ -1063,7 +1106,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
index 27d5bf543c5e339e58bae65ffa8754e11205dcbf..75d4684f8bcd56f919a3957f54b23646ed398706 100644 (file)
@@ -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)
 
 
index cdce0653be25f34053274615d5b68666a27406e3..84a7f77ee22f3f76b688c89aed0e8d0c7cfa9a1b 100644 (file)
@@ -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, "<Enter>");
 
         /* 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, "<ESC>");
     }
 
     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, "<Win>");
 
         /* 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, "<Win>");
         else txt(31, 5, "<Alt>");
 
         /* 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, "<Enter>");
 
         /* 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, "<ESC>");
     }
 
index d97807d1749decec42982bf935e50fb37d6028f3..f494cbd56704c260c4806e486a17fd301948c4b8 100644 (file)
@@ -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
index def6848177464d7a86eec2aa8b91ef3ad4b7f2f7..25ccceaac7208ea41ed85e6c79ca649f16b7bf51 100644 (file)
@@ -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)
@@ -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 (file)
index df112ee..0000000
+++ /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 <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <err.h>
-#include <iconv.h>
-
-#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;
-}
-
index 4d4e253a496a0cdabdd1a18a7cb3077bdfe98ccf..742039e2693e95611c7dadaae8273c81b0b95441 100644 (file)
@@ -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;
     }
@@ -304,6 +300,7 @@ int main(int argc, char *argv[]) {
     }
 
     font = load_font(pattern, true);
+    set_font(&font);
 
     /* Open an input window */
     win = xcb_generate_id(conn);
@@ -387,9 +384,6 @@ int main(int argc, char *argv[]) {
     xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, 500, font.height + 8);
     xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0);
 
-    /* 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);
 
@@ -431,9 +425,6 @@ int main(int argc, char *argv[]) {
 
                 xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, rect.width, rect.height);
                 xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0);
-
-                /* Create graphics context */
-                xcb_change_gc(conn, pixmap_gc, XCB_GC_FONT, (uint32_t[]){ font.id });
                 break;
             }
         }
index dffe00d59360eaffb04b7ba3b51671d8b54a8745..d71a440bb1fe0c5c408ca8e18883f8ed114bb592 100755 (executable)
@@ -4,8 +4,8 @@
 #
 # Distributions/packagers can enhance this script with a
 # distribution-specific mechanism to find the preferred pager.
-which $VISUAL >/dev/null && exec $VISUAL "$@"
-which $EDITOR >/dev/null && exec $EDITOR "$@"
+[ -n "$VISUAL" ] && which $VISUAL >/dev/null && exec $VISUAL "$@"
+[ -n "$EDITOR" ] && which $EDITOR >/dev/null && exec $EDITOR "$@"
 
 # Hopefully one of these is installed (no flamewars about preference please!):
 which nano >/dev/null && exec nano "$@"
index 5af8d6b4e56be752aeac38a6ecbf005b37fb3f58..32f30aff895e670d39a3755e07870bf287e5358f 100755 (executable)
@@ -4,7 +4,7 @@
 #
 # Distributions/packagers can enhance this script with a
 # distribution-specific mechanism to find the preferred pager.
-which $PAGER >/dev/null && exec $PAGER "$@"
+[ -n "$PAGER" ] && which $PAGER >/dev/null && exec $PAGER "$@"
 
 # Hopefully one of these is installed:
 which most >/dev/null && exec most "$@"
index 28e60623a2f29f2d4db5c40ae0cb728463cf56e1..e5bf2718be1c3afa8623f6d3b577a6373bffad7b 100755 (executable)
@@ -6,7 +6,7 @@
 # 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:
-which $TERMINAL >/dev/null && exec $TERMINAL "$@"
+[ -n "$TERMINAL" ] && which $TERMINAL >/dev/null && exec $TERMINAL "$@"
 
 # Hopefully one of these is installed:
 which xterm >/dev/null && exec xterm "$@"
index 3b6967faf347cdace0c353c9b30680ac032eb82f..bce31a4df22cd5c267d3bf9626b97025d424f2cb 100644 (file)
@@ -31,7 +31,6 @@ struct rect_t {
 #include "workspaces.h"
 #include "trayclients.h"
 #include "xcb.h"
-#include "ucs2_to_utf8.h"
 #include "config.h"
 #include "libi3.h"
 
diff --git a/i3bar/include/ucs2_to_utf8.h b/i3bar/include/ucs2_to_utf8.h
deleted file mode 100644 (file)
index a77ed20..0000000
+++ /dev/null
@@ -1,15 +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.
- */
-#ifndef _UCS2_TO_UTF8
-#define _UCS2_TO_UTF8
-
-char *convert_utf8_to_ucs2(char *input, int *real_strlen);
-
-#endif
index 8067a19382919a24805a0018c5ac2ec35d741ec9..f82c71159d7a10abb6023e54374ebe1cdddffe4c 100644 (file)
@@ -103,12 +103,4 @@ void draw_bars();
  */
 void redraw_bars();
 
-/*
- * Predicts the length of text based on cached data.
- * The string has to be encoded in ucs2 and glyph_len has to be the length
- * of the string (in glyphs).
- *
- */
-uint32_t predict_text_extents(xcb_char2b_t *text, uint32_t length);
-
 #endif
diff --git a/i3bar/src/ucs2_to_utf8.c b/i3bar/src/ucs2_to_utf8.c
deleted file mode 100644 (file)
index 642a72f..0000000
+++ /dev/null
@@ -1,102 +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 <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <err.h>
-#include <iconv.h>
-
-#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) {
-            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");
-        free(buffer);
-        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) {
-            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");
-        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;
-}
index 7cfbeffd4e3e4ba912250611392bc7b5260021af..5df1899f33a408f4a8429a52acaa3a2b060c8f7a 100644 (file)
@@ -119,13 +119,13 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, unsigne
             params->workspaces_walk->name[len] = '\0';
 
             /* Convert the name to ucs2, save its length in glyphs and calculate its rendered width */
-            int ucs2_len;
+            size_t ucs2_len;
             xcb_char2b_t *ucs2_name = (xcb_char2b_t*) convert_utf8_to_ucs2(params->workspaces_walk->name, &ucs2_len);
             params->workspaces_walk->ucs2_name = ucs2_name;
             params->workspaces_walk->name_glyphs = ucs2_len;
             params->workspaces_walk->name_width =
-                predict_text_extents(params->workspaces_walk->ucs2_name,
-                params->workspaces_walk->name_glyphs);
+                predict_text_width((char *)params->workspaces_walk->ucs2_name,
+                params->workspaces_walk->name_glyphs, true);
 
             DLOG("Got Workspace %s, name_width: %d, glyphs: %d\n",
                  params->workspaces_walk->name,
index 29ffe1c41cfe96e0d9fa7a28ebee2f81f00ac00c..4a5ff69a9d4a291638fa6682a34c8d21ed289102 100644 (file)
@@ -48,12 +48,12 @@ xcb_connection_t *xcb_connection;
 int              screen;
 xcb_screen_t     *xcb_screen;
 xcb_window_t     xcb_root;
-xcb_font_t       xcb_font;
 
-/* We need to cache some data to speed up text-width-prediction */
-xcb_query_font_reply_t *font_info;
-int                    font_height;
-xcb_charinfo_t         *font_table;
+/* This is needed for integration with libi3 */
+xcb_connection_t *conn;
+
+/* The font we'll use */
+static i3Font font;
 
 /* These are only relevant for XKB, which we only need for grabbing modifiers */
 Display          *xkb_dpy;
@@ -99,98 +99,31 @@ int _xcb_request_failed(xcb_void_cookie_t cookie, char *err_msg, int line) {
     return 0;
 }
 
-/*
- * Predicts the length of text based on cached data.
- * The string has to be encoded in ucs2 and glyph_len has to be the length
- * of the string (in glyphs).
- *
- */
-uint32_t predict_text_extents(xcb_char2b_t *text, uint32_t length) {
-    /* If we don't have per-character data, return the maximum width */
-    if (font_table == NULL) {
-        return (font_info->max_bounds.character_width * length);
-    }
-
-    uint32_t width = 0;
-    uint32_t i;
-
-    for (i = 0; i < length; i++) {
-        xcb_charinfo_t *info;
-        int row = text[i].byte1;
-        int col = text[i].byte2;
-
-        if (row < font_info->min_byte1 || row > font_info->max_byte1 ||
-            col < font_info->min_char_or_byte2 || col > font_info->max_char_or_byte2) {
-            continue;
-        }
-
-        /* Don't you ask me, how this one works… */
-        info = &font_table[((row - font_info->min_byte1) *
-                            (font_info->max_char_or_byte2 - font_info->min_char_or_byte2 + 1)) +
-                           (col - font_info->min_char_or_byte2)];
-
-        if (info->character_width != 0 ||
-            (info->right_side_bearing |
-             info->left_side_bearing |
-             info->ascent |
-             info->descent) != 0) {
-            width += info->character_width;
-        }
-    }
-
-    return width;
-}
-
-/*
- * Draws text given in UCS-2-encoding to a given drawable and position
- *
- */
-void draw_text(xcb_drawable_t drawable, xcb_gcontext_t ctx, int16_t x, int16_t y,
-               xcb_char2b_t *text, uint32_t glyph_count) {
-    int offset = 0;
-    int16_t pos_x = x;
-    int16_t font_ascent = font_info->font_ascent;
-
-    while (glyph_count > 0) {
-        uint8_t chunk_size = MIN(255, glyph_count);
-        uint32_t chunk_width = predict_text_extents(text + offset, chunk_size);
-
-        xcb_image_text_16(xcb_connection,
-                          chunk_size,
-                          drawable,
-                          ctx,
-                          pos_x, y + font_ascent,
-                          text + offset);
-
-        offset += chunk_size;
-        pos_x += chunk_width;
-        glyph_count -= chunk_size;
-    }
-}
-
 /*
  * Redraws the statusline to the buffer
  *
  */
 void refresh_statusline() {
-    int glyph_count;
+    size_t glyph_count;
 
     if (statusline == NULL) {
         return;
     }
 
-    xcb_char2b_t *text = (xcb_char2b_t*) convert_utf8_to_ucs2(statusline, &glyph_count);
+    xcb_char2b_t *text = (xcb_char2b_t*)convert_utf8_to_ucs2(statusline, &glyph_count);
     uint32_t old_statusline_width = statusline_width;
-    statusline_width = predict_text_extents(text, glyph_count);
+    statusline_width = predict_text_width((char*)text, glyph_count, true);
     /* If the statusline is bigger than our screen we need to make sure that
      * the pixmap provides enough space, so re-allocate if the width grew */
     if (statusline_width > xcb_screen->width_in_pixels &&
         statusline_width > old_statusline_width)
         realloc_sl_buffer();
 
-    xcb_rectangle_t rect = { 0, 0, xcb_screen->width_in_pixels, font_height };
+    xcb_rectangle_t rect = { 0, 0, xcb_screen->width_in_pixels, font.height };
     xcb_poly_fill_rectangle(xcb_connection, statusline_pm, statusline_clear, 1, &rect);
-    draw_text(statusline_pm, statusline_ctx, 0, 0, text, glyph_count);
+    set_font_colors(statusline_ctx, colors.bar_fg, colors.bar_bg);
+    draw_text((char*)text, glyph_count, true, statusline_pm, statusline_ctx,
+            0, 0, xcb_screen->width_in_pixels);
 
     FREE(text);
 }
@@ -242,9 +175,9 @@ void unhide_bars() {
         values[0] = walk->rect.x;
         if (config.position == POS_TOP)
             values[1] = walk->rect.y;
-        else values[1] = walk->rect.y + walk->rect.h - font_height - 6;
+        else values[1] = walk->rect.y + walk->rect.h - font.height - 6;
         values[2] = walk->rect.w;
-        values[3] = font_height + 6;
+        values[3] = font.height + 6;
         values[4] = XCB_STACK_MODE_ABOVE;
         DLOG("Reconfiguring Window for output %s to %d,%d\n", walk->name, values[0], values[1]);
         cookie = xcb_configure_window_checked(xcb_connection,
@@ -378,8 +311,8 @@ static void configure_trayclients() {
             clients++;
 
             DLOG("Configuring tray window %08x to x=%d\n",
-                 trayclient->win, output->rect.w - (clients * (font_height + 2)));
-            uint32_t x = output->rect.w - (clients * (font_height + 2));
+                 trayclient->win, output->rect.w - (clients * (font.height + 2)));
+            uint32_t x = output->rect.w - (clients * (font.height + 2));
             xcb_configure_window(xcb_connection,
                                  trayclient->win,
                                  XCB_CONFIG_WINDOW_X,
@@ -465,7 +398,7 @@ static void handle_client_message(xcb_client_message_event_t* event) {
             xcb_reparent_window(xcb_connection,
                                 client,
                                 output->bar,
-                                output->rect.w - font_height - 2,
+                                output->rect.w - font.height - 2,
                                 2);
             /* We reconfigure the window to use a reasonable size. The systray
              * specification explicitly says:
@@ -473,8 +406,8 @@ static void handle_client_message(xcb_client_message_event_t* event) {
              *   should do their best to cope with any size effectively
              */
             mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
-            values[0] = font_height;
-            values[1] = font_height;
+            values[0] = font.height;
+            values[1] = font.height;
             xcb_configure_window(xcb_connection,
                                  client,
                                  mask,
@@ -649,10 +582,10 @@ static void handle_configure_request(xcb_configure_request_event_t *event) {
                 continue;
 
             xcb_rectangle_t rect;
-            rect.x = output->rect.w - (clients * (font_height + 2));
+            rect.x = output->rect.w - (clients * (font.height + 2));
             rect.y = 2;
-            rect.width = font_height;
-            rect.height = font_height;
+            rect.width = font.height;
+            rect.height = font.height;
 
             DLOG("This is a tray window. x = %d\n", rect.x);
             fake_configure_notify(xcb_connection, rect, event->window, 0);
@@ -778,6 +711,7 @@ char *init_xcb_early() {
         ELOG("Cannot open display\n");
         exit(EXIT_FAILURE);
     }
+    conn = xcb_connection;
     DLOG("Connected to xcb\n");
 
     /* We have to request the atoms we need */
@@ -799,14 +733,12 @@ char *init_xcb_early() {
                                                                mask,
                                                                vals);
 
-    mask |= XCB_GC_BACKGROUND;
-    vals[0] = colors.bar_fg;
     statusline_ctx = xcb_generate_id(xcb_connection);
     xcb_void_cookie_t sl_ctx_cookie = xcb_create_gc_checked(xcb_connection,
                                                             statusline_ctx,
                                                             xcb_root,
-                                                            mask,
-                                                            vals);
+                                                            0,
+                                                            NULL);
 
     statusline_pm = xcb_generate_id(xcb_connection);
     xcb_void_cookie_t sl_pm_cookie = xcb_create_pixmap_checked(xcb_connection,
@@ -869,29 +801,13 @@ char *init_xcb_early() {
  *
  */
 void init_xcb_late(char *fontname) {
-    if (fontname == NULL) {
-        /* XXX: font fallback to 'misc' like i3 does it would be good. */
+    if (fontname == NULL)
         fontname = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1";
-    }
 
-    /* We load and allocate the font */
-    xcb_font = xcb_generate_id(xcb_connection);
-    xcb_void_cookie_t open_font_cookie;
-    open_font_cookie = xcb_open_font_checked(xcb_connection,
-                                             xcb_font,
-                                             strlen(fontname),
-                                             fontname);
-
-    /* We need to save info about the font, because we need the font's height and
-     * information about the width of characters */
-    xcb_query_font_cookie_t query_font_cookie;
-    query_font_cookie = xcb_query_font(xcb_connection,
-                                       xcb_font);
-
-    xcb_change_gc(xcb_connection,
-                  statusline_ctx,
-                  XCB_GC_FONT,
-                  (uint32_t[]){ xcb_font });
+    /* Load the font */
+    font = load_font(fontname, true);
+    set_font(&font);
+    DLOG("Calculated Font-height: %d\n", font.height);
 
     xcb_flush(xcb_connection);
 
@@ -936,25 +852,6 @@ void init_xcb_late(char *fontname) {
         ev_io_start(main_loop, xkb_io);
         XFlush(xkb_dpy);
     }
-
-    /* Now we save the font-infos */
-    font_info = xcb_query_font_reply(xcb_connection,
-                                     query_font_cookie,
-                                     NULL);
-
-    if (xcb_request_failed(open_font_cookie, "Could not open font")) {
-        exit(EXIT_FAILURE);
-    }
-
-    font_height = font_info->font_ascent + font_info->font_descent;
-
-    if (xcb_query_font_char_infos_length(font_info) == 0) {
-        font_table = NULL;
-    } else {
-        font_table = xcb_query_font_char_infos(font_info);
-    }
-
-    DLOG("Calculated Font-height: %d\n", font_height);
 }
 
 /*
@@ -1084,7 +981,6 @@ void clean_xcb() {
     FREE(xcb_chk);
     FREE(xcb_prep);
     FREE(xcb_io);
-    FREE(font_info);
 }
 
 /*
@@ -1137,7 +1033,7 @@ void realloc_sl_buffer() {
                                                                xcb_screen->height_in_pixels);
 
     uint32_t mask = XCB_GC_FOREGROUND;
-    uint32_t vals[3] = { colors.bar_bg, colors.bar_bg, xcb_font };
+    uint32_t vals[2] = { colors.bar_bg, colors.bar_bg };
     xcb_free_gc(xcb_connection, statusline_clear);
     statusline_clear = xcb_generate_id(xcb_connection);
     xcb_void_cookie_t clear_ctx_cookie = xcb_create_gc_checked(xcb_connection,
@@ -1146,7 +1042,7 @@ void realloc_sl_buffer() {
                                                                mask,
                                                                vals);
 
-    mask |= XCB_GC_BACKGROUND | XCB_GC_FONT;
+    mask |= XCB_GC_BACKGROUND;
     vals[0] = colors.bar_fg;
     statusline_ctx = xcb_generate_id(xcb_connection);
     xcb_free_gc(xcb_connection, statusline_ctx);
@@ -1207,8 +1103,8 @@ void reconfig_windows() {
                                                                      xcb_screen->root_depth,
                                                                      walk->bar,
                                                                      xcb_root,
-                                                                     walk->rect.x, walk->rect.y + walk->rect.h - font_height - 6,
-                                                                     walk->rect.w, font_height + 6,
+                                                                     walk->rect.x, walk->rect.y + walk->rect.h - font.height - 6,
+                                                                     walk->rect.w, font.height + 6,
                                                                      1,
                                                                      XCB_WINDOW_CLASS_INPUT_OUTPUT,
                                                                      xcb_screen->root_visual,
@@ -1282,12 +1178,12 @@ void reconfig_windows() {
                 case POS_NONE:
                     break;
                 case POS_TOP:
-                    strut_partial.top = font_height + 6;
+                    strut_partial.top = font.height + 6;
                     strut_partial.top_start_x = walk->rect.x;
                     strut_partial.top_end_x = walk->rect.x + walk->rect.w;
                     break;
                 case POS_BOT:
-                    strut_partial.bottom = font_height + 6;
+                    strut_partial.bottom = font.height + 6;
                     strut_partial.bottom_start_x = walk->rect.x;
                     strut_partial.bottom_end_x = walk->rect.x + walk->rect.w;
                     break;
@@ -1304,13 +1200,11 @@ void reconfig_windows() {
             /* We also want a graphics-context for the bars (it defines the properties
              * with which we draw to them) */
             walk->bargc = xcb_generate_id(xcb_connection);
-            mask = XCB_GC_FONT;
-            values[0] = xcb_font;
             xcb_void_cookie_t gc_cookie = xcb_create_gc_checked(xcb_connection,
                                                                 walk->bargc,
                                                                 walk->bar,
-                                                                mask,
-                                                                values);
+                                                                0,
+                                                                NULL);
 
             /* We finally map the bar (display it on screen), unless the modifier-switch is on */
             xcb_void_cookie_t map_cookie;
@@ -1343,9 +1237,9 @@ void reconfig_windows() {
                    XCB_CONFIG_WINDOW_HEIGHT |
                    XCB_CONFIG_WINDOW_STACK_MODE;
             values[0] = walk->rect.x;
-            values[1] = walk->rect.y + walk->rect.h - font_height - 6;
+            values[1] = walk->rect.y + walk->rect.h - font.height - 6;
             values[2] = walk->rect.w;
-            values[3] = font_height + 6;
+            values[3] = font.height + 6;
             values[4] = XCB_STACK_MODE_ABOVE;
 
             DLOG("Destroying buffer for output %s", walk->name);
@@ -1401,7 +1295,7 @@ void draw_bars() {
                       outputs_walk->bargc,
                       XCB_GC_FOREGROUND,
                       &color);
-        xcb_rectangle_t rect = { 0, 0, outputs_walk->rect.w, font_height + 6 };
+        xcb_rectangle_t rect = { 0, 0, outputs_walk->rect.w, font.height + 6 };
         xcb_poly_fill_rectangle(xcb_connection,
                                 outputs_walk->buffer,
                                 outputs_walk->bargc,
@@ -1422,7 +1316,7 @@ void draw_bars() {
                 /* We assume the tray icons are quadratic (we use the font
                  * *height* as *width* of the icons) because we configured them
                  * like this. */
-                traypx += font_height + 2;
+                traypx += font.height + 2;
             }
             /* Add 2px of padding if there are any tray icons */
             if (traypx > 0)
@@ -1433,7 +1327,7 @@ void draw_bars() {
                           outputs_walk->bargc,
                           MAX(0, (int16_t)(statusline_width - outputs_walk->rect.w + 4)), 0,
                           MAX(0, (int16_t)(outputs_walk->rect.w - statusline_width - traypx - 4)), 3,
-                          MIN(outputs_walk->rect.w - traypx - 4, statusline_width), font_height);
+                          MIN(outputs_walk->rect.w - traypx - 4, statusline_width), font.height);
         }
 
         if (config.disable_ws) {
@@ -1467,22 +1361,15 @@ void draw_bars() {
                           outputs_walk->bargc,
                           mask,
                           vals);
-            xcb_rectangle_t rect = { i + 1, 1, ws_walk->name_width + 8, font_height + 4 };
+            xcb_rectangle_t rect = { i + 1, 1, ws_walk->name_width + 8, font.height + 4 };
             xcb_poly_fill_rectangle(xcb_connection,
                                     outputs_walk->buffer,
                                     outputs_walk->bargc,
                                     1,
                                     &rect);
-            xcb_change_gc(xcb_connection,
-                          outputs_walk->bargc,
-                          XCB_GC_FOREGROUND,
-                          &fg_color);
-            xcb_image_text_16(xcb_connection,
-                              ws_walk->name_glyphs,
-                              outputs_walk->buffer,
-                              outputs_walk->bargc,
-                              i + 5, font_info->font_ascent + 2,
-                              ws_walk->ucs2_name);
+            set_font_colors(outputs_walk->bargc, fg_color, bg_color);
+            draw_text((char*)ws_walk->ucs2_name, ws_walk->name_glyphs, true,
+                    outputs_walk->buffer, outputs_walk->bargc, i + 5, 2, ws_walk->name_width);
             i += 10 + ws_walk->name_width;
         }
 
index b4128cafc6a1ca4381d9814e9b3e7543ede5114e..ea1885cc9c1dd0dd56502c3f3bf4921e9fb61721 100644 (file)
@@ -201,6 +201,11 @@ struct Barconfig {
     /** Bar position (bottom by default). */
     enum { P_BOTTOM = 0, P_TOP = 1 } position;
 
+    /** Command that should be run to execute i3bar, give a full path if i3bar is not
+     * in your $PATH.
+     * By default just 'i3bar' is executed. */
+    char *i3bar_command;
+
     /** Command that should be run to get a statusline, for example 'i3status'.
      * Will be passed to the shell. */
     char *status_command;
index 740278aecd12eb4bae72c0848d5ec25c474bf28d..40fffbfc4cebf8c71d2a4d18f38003968083357f 100644 (file)
@@ -119,7 +119,6 @@ struct deco_render_params {
     Rect con_deco_rect;
     uint32_t background;
     bool con_is_leaf;
-    xcb_font_t font;
 };
 
 /**
@@ -285,7 +284,7 @@ struct Window {
     char *name_json;
 
     /** The length of the name in glyphs (not bytes) */
-    int name_len;
+    size_t name_len;
 
     /** Whether the application used _NET_WM_NAME */
     bool uses_net_wm_name;
index 71fba764540f41ab5c4ff8e2c1e6e50a48373ba0..b8ed6c31c875f299929341e5e19590b36b7b318c 100644 (file)
@@ -27,10 +27,17 @@ typedef struct Font i3Font;
  *
  */
 struct Font {
-    /** The height of the font, built from font_ascent + font_descent */
-    int height;
     /** The xcb-id for the font */
     xcb_font_t id;
+
+    /** Font information gathered from the server */
+    xcb_query_font_reply_t *info;
+
+    /** Font table for this font (may be NULL) */
+    xcb_charinfo_t *table;
+
+    /** The height of the font, built from font_ascent + font_descent */
+    int height;
 };
 
 /* Since this file also gets included by utilities which don’t use the i3 log
@@ -177,6 +184,56 @@ uint32_t get_mod_mask_for(uint32_t keysym,
  * the fonts 'fixed' or '-misc-*' will be loaded instead of exiting.
  *
  */
-i3Font load_font(const char *pattern, bool fallback);
+i3Font load_font(const char *pattern, const bool fallback);
+
+/**
+ * Defines the font to be used for the forthcoming calls.
+ *
+ */
+void set_font(i3Font *font);
+
+/**
+ * Frees the resources taken by the current font.
+ *
+ */
+void free_font();
+
+/**
+ * Converts the given string to UTF-8 from UCS-2 big endian. The return value
+ * must be freed after use.
+ *
+ */
+char *convert_ucs2_to_utf8(xcb_char2b_t *text, size_t num_glyphs);
+
+/**
+ * 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.
+ *
+ */
+xcb_char2b_t *convert_utf8_to_ucs2(char *input, size_t *real_strlen);
+
+/**
+ * Defines the colors to be used for the forthcoming draw_text calls.
+ *
+ */
+void set_font_colors(xcb_gcontext_t gc, uint32_t foreground, uint32_t background);
+
+/**
+ * Draws text onto the specified X drawable (normally a pixmap) at the
+ * specified coordinates (from the top left corner of the leftmost, uppermost
+ * glyph) and using the provided gc. Text can be specified as UCS-2 or UTF-8.
+ *
+ */
+void draw_text(char *text, size_t text_len, bool is_ucs2, xcb_drawable_t drawable,
+        xcb_gcontext_t gc, int x, int y, int max_width);
+
+/**
+ * Predict the text width in pixels for the given text. Text can be specified
+ * as UCS-2 or UTF-8.
+ *
+ */
+int predict_text_width(char *text, size_t text_len, bool is_ucs2);
 
 #endif
index 4a5920d2fbfa692627f17a09bc34a3fe5e8d1b97..cd88863c282a90b976bd9ba02fb6b4c3c58ef3ca 100644 (file)
@@ -91,15 +91,6 @@ void exec_i3_utility(char *name, char *argv[]);
 void check_error(xcb_connection_t *conn, xcb_void_cookie_t cookie,
                  char *err_message);
 
-/**
- * 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);
-
 /**
  * This function resolves ~ in pathnames.
  * It may resolve wildcards in the first part of the path, but if no match
index 01e2b66728ec28b022e6eaa7b5308b78b9dd2f5e..8c7d5422b79c083ccbd312c05176c00b2f45db30 100644 (file)
@@ -94,13 +94,6 @@ void send_take_focus(xcb_window_t window);
  */
 void xcb_raise_window(xcb_connection_t *conn, xcb_window_t window);
 
-/**
- * Calculate the width of the given text (16-bit characters, UCS) with given
- * real length (amount of glyphs) using the given font.
- *
- */
-int predict_text_width(char *text, int length);
-
 /**
  * Configures the given window to have the size/position specified by given rect
  *
diff --git a/libi3/font.c b/libi3/font.c
new file mode 100644 (file)
index 0000000..3a68cb7
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ * Â© 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ */
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <err.h>
+
+#include "libi3.h"
+
+extern xcb_connection_t *conn;
+static const i3Font *savedFont = NULL;
+
+/*
+ * Loads a font for usage, also getting its metrics. If fallback is true,
+ * the fonts 'fixed' or '-misc-*' will be loaded instead of exiting.
+ *
+ */
+i3Font load_font(const char *pattern, const bool fallback) {
+    i3Font font;
+
+    /* Send all our requests first */
+    font.id = xcb_generate_id(conn);
+    xcb_void_cookie_t font_cookie = xcb_open_font_checked(conn, font.id,
+            strlen(pattern), pattern);
+    xcb_query_font_cookie_t info_cookie = xcb_query_font(conn, font.id);
+
+    /* Check for errors. If errors, fall back to default font. */
+    xcb_generic_error_t *error;
+    error = xcb_request_check(conn, font_cookie);
+
+    /* If we fail to open font, fall back to 'fixed' */
+    if (fallback && error != NULL) {
+        ELOG("Could not open font %s (X error %d). Trying fallback to 'fixed'.\n",
+             pattern, error->error_code);
+        pattern = "fixed";
+        font_cookie = xcb_open_font_checked(conn, font.id, strlen(pattern), pattern);
+        info_cookie = xcb_query_font(conn, font.id);
+
+        /* Check if we managed to open 'fixed' */
+        error = xcb_request_check(conn, font_cookie);
+
+        /* Fall back to '-misc-*' if opening 'fixed' fails. */
+        if (error != NULL) {
+            ELOG("Could not open fallback font 'fixed', trying with '-misc-*'.\n");
+            pattern = "-misc-*";
+            font_cookie = xcb_open_font_checked(conn, font.id, strlen(pattern), pattern);
+            info_cookie = xcb_query_font(conn, font.id);
+
+            if ((error = xcb_request_check(conn, font_cookie)) != NULL)
+                errx(EXIT_FAILURE, "Could open neither requested font nor fallbacks "
+                     "(fixed or -misc-*): X11 error %d", error->error_code);
+        }
+    }
+
+    /* Get information (height/name) for this font */
+    if (!(font.info = xcb_query_font_reply(conn, info_cookie, NULL)))
+        errx(EXIT_FAILURE, "Could not load font \"%s\"", pattern);
+
+    /* Get the font table, if possible */
+    if (xcb_query_font_char_infos_length(font.info) == 0)
+        font.table = NULL;
+    else
+        font.table = xcb_query_font_char_infos(font.info);
+
+    /* Calculate the font height */
+    font.height = font.info->font_ascent + font.info->font_descent;
+
+    return font;
+}
+
+/*
+ * Defines the font to be used for the forthcoming calls.
+ *
+ */
+void set_font(i3Font *font) {
+    savedFont = font;
+}
+
+/*
+ * Frees the resources taken by the current font.
+ *
+ */
+void free_font() {
+    /* Close the font and free the info */
+    xcb_close_font(conn, savedFont->id);
+    if (savedFont->info)
+        free(savedFont->info);
+}
+
+/*
+ * Defines the colors to be used for the forthcoming draw_text calls.
+ *
+ */
+void set_font_colors(xcb_gcontext_t gc, uint32_t foreground, uint32_t background) {
+    assert(savedFont != NULL);
+    uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
+    uint32_t values[] = { foreground, background, savedFont->id };
+    xcb_change_gc(conn, gc, mask, values);
+}
+
+/*
+ * Draws text onto the specified X drawable (normally a pixmap) at the
+ * specified coordinates (from the top left corner of the leftmost, uppermost
+ * glyph) and using the provided gc. Text can be specified as UCS-2 or UTF-8.
+ *
+ */
+void draw_text(char *text, size_t text_len, bool is_ucs2, xcb_drawable_t drawable,
+               xcb_gcontext_t gc, int x, int y, int max_width) {
+    assert(savedFont != NULL);
+    assert(text_len != 0);
+
+    /* X11 coordinates for fonts start at the baseline */
+    int pos_y = y + savedFont->info->font_ascent;
+
+    /* As an optimization, check if we can bypass conversion */
+    if (!is_ucs2 && text_len <= 255) {
+        xcb_image_text_8(conn, text_len, drawable, gc, x, pos_y, text);
+        return;
+    }
+
+    /* Convert the text into UCS-2 so we can do basic pointer math */
+    char *input = (is_ucs2 ? text : (char*)convert_utf8_to_ucs2(text, &text_len));
+
+    /* The X11 protocol limits text drawing to 255 chars, so we may need
+     * multiple calls */
+    int pos_x = x;
+    int offset = 0;
+    for (;;) {
+        /* Calculate the size of this chunk */
+        int chunk_size = (text_len > 255 ? 255 : text_len);
+        xcb_char2b_t *chunk = (xcb_char2b_t*)input + offset;
+
+        /* Draw it */
+        xcb_image_text_16(conn, chunk_size, drawable, gc, pos_x, pos_y, chunk);
+
+        /* Advance the offset and length of the text to draw */
+        offset += chunk_size;
+        text_len -= chunk_size;
+
+        /* Check if we're done */
+        if (text_len == 0)
+            break;
+
+        /* Advance pos_x based on the predicted text width */
+        pos_x += predict_text_width((char*)chunk, chunk_size, true);
+    }
+
+    /* If we had to convert, free the converted string */
+    if (!is_ucs2)
+        free(input);
+}
+
+static int xcb_query_text_width(xcb_char2b_t *text, size_t text_len) {
+    /* Make the user know we’re using the slow path, but only once. */
+    static bool first_invocation = true;
+    if (first_invocation) {
+        fprintf(stderr, "Using slow code path for text extents\n");
+        first_invocation = false;
+    }
+
+    /* Query the text width */
+    xcb_generic_error_t *error;
+    xcb_query_text_extents_cookie_t cookie = xcb_query_text_extents(conn,
+            savedFont->id, text_len, (xcb_char2b_t*)text);
+    xcb_query_text_extents_reply_t *reply = xcb_query_text_extents_reply(conn,
+            cookie, &error);
+    if (reply == NULL) {
+        /* We return a safe estimate because a rendering error is better than
+         * a crash. Plus, the user will see the error in his log. */
+        fprintf(stderr, "Could not get text extents (X error code %d)\n",
+                error->error_code);
+        return savedFont->info->max_bounds.character_width * text_len;
+    }
+
+    int width = reply->overall_width;
+    free(reply);
+    return width;
+}
+
+/*
+ * Predict the text width in pixels for the given text. Text can be specified
+ * as UCS-2 or UTF-8.
+ *
+ */
+int predict_text_width(char *text, size_t text_len, bool is_ucs2) {
+    /* Convert the text into UTF-16 so we can do basic pointer math */
+    xcb_char2b_t *input;
+    if (is_ucs2)
+        input = (xcb_char2b_t*)text;
+    else
+        input = convert_utf8_to_ucs2(text, &text_len);
+
+    int width;
+    if (savedFont->table == NULL) {
+        /* If we don't have a font table, fall back to querying the server */
+        width = xcb_query_text_width(input, text_len);
+    } else {
+        /* Save some pointers for convenience */
+        xcb_query_font_reply_t *font_info = savedFont->info;
+        xcb_charinfo_t *font_table = savedFont->table;
+
+        /* Calculate the width using the font table */
+        width = 0;
+        for (size_t i = 0; i < text_len; i++) {
+            xcb_charinfo_t *info;
+            int row = input[i].byte1;
+            int col = input[i].byte2;
+
+            if (row < font_info->min_byte1 ||
+                row > font_info->max_byte1 ||
+                col < font_info->min_char_or_byte2 ||
+                col > font_info->max_char_or_byte2)
+                continue;
+
+            /* Don't you ask me, how this one works… (Merovius) */
+            info = &font_table[((row - font_info->min_byte1) *
+                    (font_info->max_char_or_byte2 - font_info->min_char_or_byte2 + 1)) +
+                (col - font_info->min_char_or_byte2)];
+
+            if (info->character_width != 0 ||
+                    (info->right_side_bearing |
+                     info->left_side_bearing |
+                     info->ascent |
+                     info->descent) != 0) {
+                width += info->character_width;
+            }
+        }
+    }
+
+    /* If we had to convert, free the converted string */
+    if (!is_ucs2)
+        free(input);
+
+    return width;
+}
diff --git a/libi3/load_font.c b/libi3/load_font.c
deleted file mode 100644 (file)
index acb52c0..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * vim:ts=4:sw=4:expandtab
- *
- * i3 - an improved dynamic tiling window manager
- * Â© 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
- *
- */
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
-#include <err.h>
-
-#include "libi3.h"
-
-extern xcb_connection_t *conn;
-
-/*
- * Loads a font for usage, also getting its height. If fallback is true,
- * the fonts 'fixed' or '-misc-*' will be loaded instead of exiting.
- *
- */
-i3Font load_font(const char *pattern, bool fallback) {
-    i3Font font;
-    xcb_void_cookie_t font_cookie;
-    xcb_list_fonts_with_info_cookie_t info_cookie;
-    xcb_list_fonts_with_info_reply_t *info_reply;
-    xcb_generic_error_t *error;
-
-    /* Send all our requests first */
-    font.id = xcb_generate_id(conn);
-    font_cookie = xcb_open_font_checked(conn, font.id, strlen(pattern), pattern);
-    info_cookie = xcb_list_fonts_with_info(conn, 1, strlen(pattern), pattern);
-
-    /* Check for errors. If errors, fall back to default font. */
-    error = xcb_request_check(conn, font_cookie);
-
-    /* If we fail to open font, fall back to 'fixed' */
-    if (fallback && error != NULL) {
-        ELOG("Could not open font %s (X error %d). Trying fallback to 'fixed'.\n",
-             pattern, error->error_code);
-        pattern = "fixed";
-        font_cookie = xcb_open_font_checked(conn, font.id, strlen(pattern), pattern);
-        info_cookie = xcb_list_fonts_with_info(conn, 1, strlen(pattern), pattern);
-
-        /* Check if we managed to open 'fixed' */
-        error = xcb_request_check(conn, font_cookie);
-
-        /* Fall back to '-misc-*' if opening 'fixed' fails. */
-        if (error != NULL) {
-            ELOG("Could not open fallback font 'fixed', trying with '-misc-*'.\n");
-            pattern = "-misc-*";
-            font_cookie = xcb_open_font_checked(conn, font.id, strlen(pattern), pattern);
-            info_cookie = xcb_list_fonts_with_info(conn, 1, strlen(pattern), pattern);
-
-            if ((error = xcb_request_check(conn, font_cookie)) != NULL)
-                errx(EXIT_FAILURE, "Could open neither requested font nor fallbacks "
-                     "(fixed or -misc-*): X11 error %d", error->error_code);
-        }
-    }
-
-    /* Get information (height/name) for this font */
-    if (!(info_reply = xcb_list_fonts_with_info_reply(conn, info_cookie, NULL)))
-        errx(EXIT_FAILURE, "Could not load font \"%s\"", pattern);
-
-    font.height = info_reply->font_ascent + info_reply->font_descent;
-
-    free(info_reply);
-
-    return font;
-}
diff --git a/libi3/ucs2_conversion.c b/libi3/ucs2_conversion.c
new file mode 100644 (file)
index 0000000..6f7cf28
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ * Â© 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ */
+#include <err.h>
+#include <errno.h>
+#include <iconv.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libi3.h"
+
+static iconv_t utf8_conversion_descriptor = (iconv_t)-1;
+static iconv_t ucs2_conversion_descriptor = (iconv_t)-1;
+
+/*
+ * Converts the given string to UTF-8 from UCS-2 big endian. The return value
+ * must be freed after use.
+ *
+ */
+char *convert_ucs2_to_utf8(xcb_char2b_t *text, size_t num_glyphs) {
+    /* Allocate the output buffer (UTF-8 is at most 4 bytes per glyph) */
+    size_t buffer_size = num_glyphs * 4 * sizeof(char) + 1;
+    char *buffer = scalloc(buffer_size * sizeof(char));
+
+    /* We need to use an additional pointer, because iconv() modifies it */
+    char *output = buffer;
+    size_t output_size = buffer_size - 1;
+
+    if (utf8_conversion_descriptor == (iconv_t)-1) {
+        /* Get a new conversion descriptor */
+        utf8_conversion_descriptor = iconv_open("UTF-8", "UCS-2BE");
+        if (utf8_conversion_descriptor == (iconv_t)-1)
+            err(EXIT_FAILURE, "Error opening the conversion context");
+    } else {
+        /* Reset the existing conversion descriptor */
+        iconv(utf8_conversion_descriptor, NULL, NULL, NULL, NULL);
+    }
+
+    /* Do the conversion */
+    size_t input_len = num_glyphs * sizeof(xcb_char2b_t);
+    size_t rc = iconv(utf8_conversion_descriptor, (char**)&text,
+            &input_len, &output, &output_size);
+    if (rc == (size_t)-1) {
+        perror("Converting to UTF-8 failed");
+        free(buffer);
+        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.
+ *
+ */
+xcb_char2b_t *convert_utf8_to_ucs2(char *input, size_t *real_strlen) {
+    /* Calculate the input buffer size (UTF-8 is strlen-safe) */
+    size_t input_size = strlen(input);
+
+    /* Calculate the output buffer size and allocate the buffer */
+    size_t buffer_size = input_size * sizeof(xcb_char2b_t);
+    xcb_char2b_t *buffer = smalloc(buffer_size);
+
+    /* We need to use an additional pointer, because iconv() modifies it */
+    size_t output_size = buffer_size;
+    xcb_char2b_t *output = buffer;
+
+    if (ucs2_conversion_descriptor == (iconv_t)-1) {
+        /* Get a new conversion descriptor */
+        ucs2_conversion_descriptor = iconv_open("UCS-2BE", "UTF-8");
+        if (ucs2_conversion_descriptor == (iconv_t)-1)
+            err(EXIT_FAILURE, "Error opening the conversion context");
+    } else {
+        /* Reset the existing conversion descriptor */
+        iconv(ucs2_conversion_descriptor, NULL, NULL, NULL, NULL);
+    }
+
+    /* Do the conversion */
+    size_t rc = iconv(ucs2_conversion_descriptor, (char**)&input,
+            &input_size, (char**)&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;
+    }
+
+    /* Return the resulting string's length */
+    if (real_strlen != NULL)
+        *real_strlen = (buffer_size - output_size) / sizeof(xcb_char2b_t);
+
+    return buffer;
+}
index 019c9124e54faf6a8ccb05f6f4b9418c957f61ab..28951374a75554f02800b59a69a5178e5fae929a 100644 (file)
@@ -310,7 +310,7 @@ which is why this is not integrated into this manpage), the debugging guide,
 and the "how to hack" guide. If you are building from source, run:
  +make -C docs+
 
-You can also access these documents online at http://i3.zekjur.net/
+You can also access these documents online at http://i3wm.org/
 
 i3-input(1), i3-msg(1), i3-wsbar(1), i3-nagbar(1), i3-config-wizard(1),
 i3-migrate-config-to-v4(1)
index 1566e24f879fabd4a162500cb28057a8b4461344..673724a85f65973e4ec8ce6306073cfad20b55a2 100644 (file)
@@ -102,6 +102,7 @@ EOL     (\r?\n)
 <BAR_POSITION>bottom            { yy_pop_state(); return TOK_BAR_BOTTOM; }
 <BAR_POSITION>top               { yy_pop_state(); return TOK_BAR_TOP; }
 <BAR>status_command             { WS_STRING; return TOK_BAR_STATUS_COMMAND; }
+<BAR>i3bar_command              { WS_STRING; return TOK_BAR_I3BAR_COMMAND; }
 <BAR>font                       { WS_STRING; return TOK_BAR_FONT; }
 <BAR>workspace_buttons          { return TOK_BAR_WORKSPACE_BUTTONS; }
 <BAR>verbose                    { return TOK_BAR_VERBOSE; }
index 7a6a73984731e50901f8b2ff3b2fe9bfcf5e9169..dbe227ddffd1b2bcce745d1c04cd8a2efc878e74 100644 (file)
@@ -710,6 +710,7 @@ void parse_file(const char *f) {
 %token                  TOK_BAR_BOTTOM              "bottom"
 %token                  TOK_BAR_TOP                 "top"
 %token                  TOK_BAR_STATUS_COMMAND      "status_command"
+%token                  TOK_BAR_I3BAR_COMMAND       "i3bar_command"
 %token                  TOK_BAR_FONT                "font (bar)"
 %token                  TOK_BAR_WORKSPACE_BUTTONS   "workspace_buttons"
 %token                  TOK_BAR_VERBOSE             "verbose"
@@ -1034,6 +1035,7 @@ barlines:
 barline:
     comment
     | bar_status_command
+    | bar_i3bar_command
     | bar_output
     | bar_tray_output
     | bar_position
@@ -1060,6 +1062,15 @@ bar_status_command:
     }
     ;
 
+bar_i3bar_command:
+    TOK_BAR_I3BAR_COMMAND STR
+    {
+        DLOG("should add i3bar_command %s\n", $2);
+        FREE(current_bar.i3bar_command);
+        current_bar.i3bar_command = $2;
+    }
+    ;
+
 bar_output:
     TOK_BAR_OUTPUT STR
     {
@@ -1551,6 +1562,7 @@ font:
     TOKFONT STR
     {
         config.font = load_font($2, true);
+        set_font(&config.font);
         printf("font %s\n", $2);
         FREE(font_pattern);
         font_pattern = $2;
index 8efb491ece9657ec22465da7d873323d0ef38092..7b0b34e34eea6d1536c58ea3aefd3ed82c12bb6c 100644 (file)
@@ -299,6 +299,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
             FREE(barconfig->tray_output);
             FREE(barconfig->socket_path);
             FREE(barconfig->status_command);
+            FREE(barconfig->i3bar_command);
             FREE(barconfig->font);
             FREE(barconfig->colors.background);
             FREE(barconfig->colors.statusline);
@@ -320,6 +321,14 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
         TAILQ_FOREACH(ws, workspaces, workspaces)
             workspace_set_name(ws, NULL);
 #endif
+
+        /* Invalidate pixmap caches in case font or colors changed */
+        Con *con;
+        TAILQ_FOREACH(con, &all_cons, all_cons)
+            FREE(con->deco_render_params);
+
+        /* Get rid of the current font */
+        free_font();
     }
 
     SLIST_INIT(&modes);
@@ -371,6 +380,14 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
     if (config.font.id == 0) {
         ELOG("You did not specify required configuration option \"font\"\n");
         config.font = load_font("fixed", true);
+        set_font(&config.font);
+    }
+
+    /* Redraw the currently visible decorations on reload, so that
+     * the possibly new drawing parameters changed. */
+    if (reload) {
+        x_deco_recurse(croot);
+        xcb_flush(conn);
     }
 
 #if 0
index 062a48626d8765d1a99bd803847fe952b8fa424f..38412d53de07d69f0810191d4d59fa5bb6eadf1a 100644 (file)
@@ -615,7 +615,9 @@ int main(int argc, char *argv[]) {
         ev_io_start(main_loop, ipc_io);
     }
 
-    /* Also handle the UNIX domain sockets passed via socket activation */
+    /* Also handle the UNIX domain sockets passed via socket activation. The
+     * parameter 1 means "remove the environment variables", we don’t want to
+     * pass these to child processes. */
     int fds = sd_listen_fds(1);
     if (fds < 0)
         ELOG("socket activation: Error in sd_listen_fds\n");
@@ -687,8 +689,9 @@ int main(int argc, char *argv[]) {
     Barconfig *barconfig;
     TAILQ_FOREACH(barconfig, &barconfigs, configs) {
         char *command = NULL;
-        sasprintf(&command, "i3bar --bar_id=%s --socket=\"%s\"",
-                  barconfig->id, current_socketpath);
+        sasprintf(&command, "%s --bar_id=%s --socket=\"%s\"",
+                barconfig->i3bar_command ? barconfig->i3bar_command : "i3bar",
+                barconfig->id, current_socketpath);
         LOG("Starting bar process: %s\n", command);
         start_application(command, true);
         free(command);
index 2905356c9d29a6f7c846c73123a02f532166ca5c..759c351fefe9364f5e50c719958d080531a6d4b9 100644 (file)
@@ -193,7 +193,9 @@ void render_con(Con *con, bool render_fullscreen) {
     }
 
     /* find the height for the decorations */
-    int deco_height = config.font.height + 5;
+    int deco_height = config.font.height + 4;
+    if (config.font.height & 0x01)
+        ++deco_height;
 
     /* precalculate the sizes to be able to correct rounding errors */
     int sizes[children];
index a029422bf778645f1396773b49122ad585f132ad..ca74813d36e2e0f215a4969e3740724368faf6ab 100644 (file)
@@ -47,15 +47,11 @@ static int sig_draw_window(xcb_window_t win, int width, int height, int font_hei
     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"));
 
     for (int i = 0; i < sizeof(crash_text) / sizeof(char*); i++) {
-        int text_len = strlen(crash_text[i]);
-        char *full_text = convert_utf8_to_ucs2(crash_text[i], &text_len);
-        xcb_image_text_16(conn, text_len, pixmap, pixmap_gc, 8 /* X */,
-                          3 + (i + 1) * font_height /* Y = baseline of font */,
-                          (xcb_char2b_t*)full_text);
-        free(full_text);
+        draw_text(crash_text[i], strlen(crash_text[i]), false, pixmap, pixmap_gc,
+                8, 3 + (i - 1) * font_height, width - 16);
     }
 
     /* Copy the contents of the pixmap to the real window */
@@ -150,9 +146,9 @@ void handle_signal(int sig, siginfo_t *info, void *data) {
     int height = 13 + (crash_text_num * config.font.height);
 
     /* calculate width for longest text */
-    int text_len = strlen(crash_text[crash_text_longest]);
-    char *longest_text = convert_utf8_to_ucs2(crash_text[crash_text_longest], &text_len);
-    int font_width = predict_text_width(longest_text, text_len);
+    size_t text_len = strlen(crash_text[crash_text_longest]);
+    xcb_char2b_t *longest_text = convert_utf8_to_ucs2(crash_text[crash_text_longest], &text_len);
+    int font_width = predict_text_width((char *)longest_text, text_len, true);
     int width = font_width + 20;
 
     /* Open a popup window on each virtual screen */
@@ -169,9 +165,6 @@ void handle_signal(int sig, siginfo_t *info, void *data) {
         xcb_create_pixmap(conn, root_depth, pixmap, win, width, height);
         xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0);
 
-        /* Create graphics context */
-        xcb_change_gc(conn, pixmap_gc, XCB_GC_FONT, (uint32_t[]){ config.font.id });
-
         /* Grab the keyboard to get all input */
         xcb_grab_keyboard(conn, false, win, XCB_CURRENT_TIME, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
 
index 72146bff7aa7c1769a1d1d341255a738f1dfc62f..57bfa89bb77051a53a5964ee4c3f7520079391ff 100644 (file)
@@ -12,7 +12,6 @@
 
 #include <sys/wait.h>
 #include <stdarg.h>
-#include <iconv.h>
 #if defined(__OpenBSD__)
 #include <sys/cdefs.h>
 #endif
@@ -24,8 +23,6 @@
 #define SN_API_NOT_YET_FROZEN 1
 #include <libsn/sn-launcher.h>
 
-static iconv_t conversion_descriptor = 0;
-
 int min(int a, int b) {
     return (a < b ? a : b);
 }
@@ -120,51 +117,6 @@ void check_error(xcb_connection_t *conn, xcb_void_cookie_t cookie, char *err_mes
     }
 }
 
-/*
- * 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_descriptor == 0) {
-        conversion_descriptor = iconv_open("UCS-2BE", "UTF-8");
-        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");
-        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;
-}
-
 /*
  * This function resolves ~ in pathnames.
  * It may resolve wildcards in the first part of the path, but if no match
index 30957a4b81678e3f629d12acb6303b17812fa8f2..270314eabb6cdca2a5d00a1809ee5323a09b5365 100644 (file)
@@ -68,8 +68,8 @@ void window_update_name(i3Window *win, xcb_get_property_reply_t *prop, bool befo
         return;
     }
     /* Convert it to UCS-2 here for not having to convert it later every time we want to pass it to X */
-    int len;
-    char *ucs2_name = convert_utf8_to_ucs2(new_name, &len);
+    size_t len;
+    xcb_char2b_t *ucs2_name = convert_utf8_to_ucs2(new_name, &len);
     if (ucs2_name == NULL) {
         LOG("Could not convert _NET_WM_NAME to UCS-2, ignoring new hint\n");
         FREE(new_name);
@@ -79,7 +79,7 @@ void window_update_name(i3Window *win, xcb_get_property_reply_t *prop, bool befo
     FREE(win->name_x);
     FREE(win->name_json);
     win->name_json = new_name;
-    win->name_x = ucs2_name;
+    win->name_x = (char*)ucs2_name;
     win->name_len = len;
     win->name_x_changed = true;
     LOG("_NET_WM_NAME changed to \"%s\"\n", win->name_json);
diff --git a/src/x.c b/src/x.c
index 61824d58bc57145a644988c007bcd18279b093ff..070fae91b1e74097e9caf910099ad7c4302aec41 100644 (file)
--- a/src/x.c
+++ b/src/x.c
@@ -307,7 +307,6 @@ void x_draw_decoration(Con *con) {
     p->con_deco_rect = con->deco_rect;
     p->background = config.client.background;
     p->con_is_leaf = con_is_leaf(con);
-    p->font = config.font.id;
 
     if (con->deco_render_params != NULL &&
         (con->window == NULL || !con->window->name_x_changed) &&
@@ -408,25 +407,17 @@ void x_draw_decoration(Con *con) {
     xcb_poly_segment(conn, parent->pixmap, parent->pm_gc, 2, segments);
 
     /* 6: draw the title */
-    uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
-    uint32_t values[] = { p->color->text, p->color->background, config.font.id };
-    xcb_change_gc(conn, parent->pm_gc, mask, values);
-    int text_offset_y = config.font.height + (con->deco_rect.height - config.font.height) / 2 - 1;
+    set_font_colors(parent->pm_gc, p->color->text, p->color->background);
+    int text_offset_y = (con->deco_rect.height - config.font.height) / 2;
 
     struct Window *win = con->window;
     if (win == NULL || win->name_x == NULL) {
         /* this is a non-leaf container, we need to make up a good description */
         // TODO: use a good description instead of just "another container"
-        xcb_image_text_8(
-            conn,
-            strlen("another container"),
-            parent->pixmap,
-            parent->pm_gc,
-            con->deco_rect.x + 2,
-            con->deco_rect.y + text_offset_y,
-            "another container"
-        );
-
+        draw_text("another container", strlen("another container"), false,
+                parent->pixmap, parent->pm_gc,
+                con->deco_rect.x + 2, con->deco_rect.y + text_offset_y,
+                con->deco_rect.width - 2);
         goto copy_pixmaps;
     }
 
@@ -447,26 +438,10 @@ void x_draw_decoration(Con *con) {
     //DLOG("indent_level = %d, indent_mult = %d\n", indent_level, indent_mult);
     int indent_px = (indent_level * 5) * indent_mult;
 
-    if (win->uses_net_wm_name)
-        xcb_image_text_16(
-            conn,
-            win->name_len,
-            parent->pixmap,
-            parent->pm_gc,
-            con->deco_rect.x + 2 + indent_px,
-            con->deco_rect.y + text_offset_y,
-            (xcb_char2b_t*)win->name_x
-        );
-    else
-        xcb_image_text_8(
-            conn,
-            win->name_len,
-            parent->pixmap,
-            parent->pm_gc,
-            con->deco_rect.x + 2 + indent_px,
-            con->deco_rect.y + text_offset_y,
-            win->name_x
-        );
+    draw_text(win->name_x, win->name_len, win->uses_net_wm_name,
+            parent->pixmap, parent->pm_gc,
+            con->deco_rect.x + 2 + indent_px, con->deco_rect.y + text_offset_y,
+            con->deco_rect.width - 2 - indent_px);
 
 copy_pixmaps:
     xcb_copy_area(conn, con->pixmap, con->frame, con->pm_gc, 0, 0, 0, 0, con->rect.width, con->rect.height);
index aa761cac74679873f32f0347d3675cce708de934..48906a26148145ffe3cbaa62ad2ef87b97bc20a9 100644 (file)
--- a/src/xcb.c
+++ b/src/xcb.c
@@ -130,32 +130,6 @@ void xcb_raise_window(xcb_connection_t *conn, xcb_window_t window) {
     xcb_configure_window(conn, window, XCB_CONFIG_WINDOW_STACK_MODE, values);
 }
 
-/*
- * Query the width of the given text (16-bit characters, UCS) with given real
- * length (amount of glyphs) using the given font.
- *
- */
-int predict_text_width(char *text, int length) {
-    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(conn, config.font.id, length, (xcb_char2b_t*)text);
-    if ((reply = xcb_query_text_extents_reply(conn, cookie, &error)) == NULL) {
-        ELOG("Could not get text extents (X error code %d)\n",
-             error->error_code);
-        /* We return the rather safe guess of 7 pixels, because a
-         * rendering error is better than a crash. Plus, the user will
-         * see the error in his log. */
-        return 7;
-    }
-
-    width = reply->overall_width;
-    free(reply);
-    return width;
-}
-
 /*
  * Configures the given window to have the size/position specified by given rect
  *
index 11385f763f631aa57e22d9868ce1f0e71230cf93..eb0f73a45dd3076eb0272ae9e182e54488eade00 100755 (executable)
@@ -10,16 +10,21 @@ WriteMakefile(
         'AnyEvent'     => 0,
         'AnyEvent::I3' => '0.09',
         'X11::XCB'     => '0.03',
-        'Test::Most'   => 0,
-        'Test::Deep'   => 0,
-       'EV'           => 0,
-       'Inline'       => 0,
+        'Inline'       => 0,
     },
-    # don't install any files from this directory
-    PM => {},
+    PM => {}, # do not install any files from this directory
     clean => {
-        FILES => 'testsuite-* latest'
+        FILES => 'testsuite-* latest i3-cfg-for-*',
     }
 );
-# and don't run the tests while installing
-sub MY::test { }
+
+package MY;
+sub test { } # do not run the tests while installing
+
+# do not rename the Makefile
+sub clean {
+    my $section = shift->SUPER::clean(@_);
+    $section =~ s/^\t\Q$_\E\n$//m for
+        '- $(MV) $(FIRST_MAKEFILE) $(MAKEFILE_OLD) $(DEV_NULL)';
+    $section;
+}
index c43fbf07b49cc4553a879ef79eee768ee40aec68..c62623f10245ae29ceab38165ed1c01156f62d49 100755 (executable)
@@ -24,10 +24,15 @@ use StartXDummy;
 use StatusLine;
 # the following modules are not shipped with Perl
 use AnyEvent;
+use AnyEvent::Util;
 use AnyEvent::Handle;
 use AnyEvent::I3 qw(:all);
 use X11::XCB;
 
+# Close superfluous file descriptors which were passed by running in a VIM
+# subshell or situations like that.
+AnyEvent::Util::close_all_fds_except(0, 1, 2);
+
 # We actually use AnyEvent to make sure it loads an event loop implementation.
 # Afterwards, we overwrite SIGCHLD:
 my $cv = AnyEvent->condvar;
@@ -51,6 +56,7 @@ sub Log { say $log "@_" }
 
 my $coverage_testing = 0;
 my $valgrind = 0;
+my $strace = 0;
 my $help = 0;
 # Number of tests to run in parallel. Important to know how many Xdummy
 # instances we need to start (unless @displays are given). Defaults to
@@ -62,6 +68,7 @@ my @childpids = ();
 my $result = GetOptions(
     "coverage-testing" => \$coverage_testing,
     "valgrind" => \$valgrind,
+    "strace" => \$strace,
     "display=s" => \@displays,
     "parallel=i" => \$parallel,
     "help|?" => \$help,
@@ -132,7 +139,7 @@ status_init(displays => \@wdisplays, tests => $num);
 
 # We start tests concurrently: For each display, one test gets started. Every
 # test starts another test after completing.
-take_job($_) for @wdisplays;
+for (@wdisplays) { $cv->begin; take_job($_) }
 
 #
 # Takes a test from the beginning of @testfiles and runs it.
@@ -147,8 +154,8 @@ take_job($_) for @wdisplays;
 sub take_job {
     my ($display) = @_;
 
-    my $test = shift @testfiles;
-    return unless $test;
+    my $test = shift @testfiles
+        or return $cv->end;
 
     my $dont_start = (slurp($test) =~ /# !NO_I3_INSTANCE!/);
     my $basename = basename($test);
@@ -171,8 +178,9 @@ sub take_job {
             display => $display,
             configfile => $tmpfile,
             outdir => $outdir,
-            logpath => $logpath,
+            testname => $basename,
             valgrind => $valgrind,
+            strace => $strace,
             cv => $activate_cv
         );
 
@@ -239,7 +247,7 @@ sub take_job {
         my $output;
         open(my $spool, '>', \$output);
         my $parser = TAP::Parser->new({
-            exec => [ 'sh', '-c', qq|DISPLAY=$display LOGPATH="$logpath" OUTDIR="$outdir" VALGRIND=$valgrind /usr/bin/perl -Ilib $test| ],
+            exec => [ 'sh', '-c', qq|DISPLAY=$display TESTNAME="$basename" OUTDIR="$outdir" VALGRIND=$valgrind STRACE=$strace /usr/bin/perl -Ilib $test| ],
             spool => $spool,
             merge => 1,
         });
@@ -280,7 +288,7 @@ sub take_job {
 
                         undef $_ for @watchers;
                         if (@done == $num) {
-                            $cv->send;
+                            $cv->end;
                         } else {
                             take_job($display);
                         }
@@ -354,7 +362,12 @@ complete-run.pl will start (num_cores * 2) Xdummy instances.
 =item B<--valgrind>
 
 Runs i3 under valgrind to find memory problems. The output will be available in
-C<latest/valgrind.log>.
+C<latest/valgrind-for-$test.log>.
+
+=item B<--strace>
+
+Runs i3 under strace to trace system calls. The output will be available in
+C<latest/strace-for-$test.log>.
 
 =item B<--coverage-testing>
 
index e0b08d746fc3bde846b502c159f8e06b94cdd095..da1dda33b3eb2db5a7dfe9e74e9b8d8a6e0af665 100644 (file)
@@ -5,8 +5,9 @@ use strict;
 use warnings;
 use IO::Socket::UNIX; # core
 use Cwd qw(abs_path); # core
-use POSIX (); # core
+use POSIX qw(:fcntl_h); # core
 use AnyEvent::Handle; # not core
+use AnyEvent::Util; # not core
 use Exporter 'import';
 use v5.10;
 
@@ -38,11 +39,6 @@ sub activate_i3 {
     # remove the old unix socket
     unlink($args{unix_socket_path});
 
-    # pass all file descriptors up to three to the children.
-    # we need to set this flag before opening the socket.
-    open(my $fdtest, '<', '/dev/null');
-    $^F = fileno($fdtest);
-    close($fdtest);
     my $socket = IO::Socket::UNIX->new(
         Listen => 1,
         Local => $args{unix_socket_path},
@@ -65,31 +61,57 @@ sub activate_i3 {
             '..',
             $ENV{PATH}
         );
-        # Only pass file descriptors 0 (stdin), 1 (stdout), 2 (stderr) and
-        # 3 (socket) to the child.
-        $^F = 3;
+
+        # We are about to exec, but we did not modify $^F to include $socket
+        # when creating the socket (because the file descriptor could have a
+        # number != 3 which would lead to i3 leaking a file descriptor). This
+        # caused Perl to set the FD_CLOEXEC flag, which would close $socket on
+        # exec(), effectively *NOT* passing $socket to the new process.
+        # Therefore, we explicitly clear FD_CLOEXEC (the only flag right now)
+        # by setting the flags to 0.
+        POSIX::fcntl($socket, F_SETFD, 0) or die "Could not clear fd flags: $!";
 
         # If the socket does not use file descriptor 3 by chance already, we
         # close fd 3 and dup2() the socket to 3.
         if (fileno($socket) != 3) {
             POSIX::close(3);
             POSIX::dup2(fileno($socket), 3);
+            POSIX::close(fileno($socket));
         }
 
+        # Make sure no file descriptors are open. Strangely, I got an open file
+        # descriptor pointing to AnyEvent/Impl/EV.pm when testing.
+        AnyEvent::Util::close_all_fds_except(0, 1, 2, 3);
+
         # Construct the command to launch i3. Use maximum debug level, disable
         # the interactive signalhandler to make it crash immediately instead.
         my $i3cmd = abs_path("../i3") . " -V -d all --disable-signalhandler";
 
+        # For convenience:
+        my $outdir = $args{outdir};
+        my $test = $args{testname};
+
         if ($args{valgrind}) {
             $i3cmd =
-                qq|valgrind -v --log-file="$args{outdir}/valgrind.log" | .
+                qq|valgrind -v --log-file="$outdir/valgrind-for-$test.log" | .
                 qq|--leak-check=full --track-origins=yes --num-callers=20 | .
                 qq|--tool=memcheck -- $i3cmd|;
         }
 
-        # Append to $args{logpath} instead of overwriting because i3 might be
+        my $logfile = "$outdir/i3-log-for-$test";
+        # Append to $logfile instead of overwriting because i3 might be
         # run multiple times in one testcase.
-        my $cmd = "exec $i3cmd -c $args{configfile} >>$args{logpath} 2>&1";
+        my $cmd = "exec $i3cmd -c $args{configfile} >>$logfile 2>&1";
+
+        if ($args{strace}) {
+            my $out = "$outdir/strace-for-$test.log";
+
+            # We overwrite LISTEN_PID with the correct process ID to make
+            # socket activation work (LISTEN_PID has to match getpid(),
+            # otherwise the LISTEN_FDS will be treated as a left-over).
+            $cmd = qq|strace -fF -s2048 -v -o "$out" -- | .
+                     'sh -c "export LISTEN_PID=\$\$; ' . $cmd . '"';
+        }
 
         # We need to use the shell due to using output redirections.
         exec '/bin/sh', '-c', $cmd;
index 7473b61757ae9c86037f2ecb3df70cf8307bc977..8437a534a1971250946119c9d133ccbf50099c54 100644 (file)
@@ -8,10 +8,10 @@ use X11::XCB::Rect;
 use X11::XCB::Window;
 use X11::XCB qw(:all);
 use AnyEvent::I3;
-use EV;
 use List::Util qw(first);
 use Time::HiRes qw(sleep);
 use Cwd qw(abs_path);
+use Scalar::Util qw(blessed);
 use SocketActivation;
 
 use v5.10;
@@ -39,6 +39,7 @@ our @EXPORT = qw(
     wait_for_event
     wait_for_map
     wait_for_unmap
+    $x
 );
 
 my $tester = Test::Builder->new();
@@ -46,6 +47,8 @@ my $_cached_socket_path = undef;
 my $_sync_window = undef;
 my $tmp_socket_path = undef;
 
+our $x;
+
 BEGIN {
     my $window_count = 0;
     sub counter_window {
@@ -56,16 +59,22 @@ BEGIN {
 sub import {
     my $class = shift;
     my $pkg = caller;
-    eval "package $pkg;
-use Test::Most" . (@_ > 0 ? " qw(@_)" : "") . ";
+
+    my $test_more_args = @_ ? "qw(@_)" : "";
+    local $@;
+    eval << "__";
+package $pkg;
+use Test::More $test_more_args;
 use Data::Dumper;
 use AnyEvent::I3;
 use Time::HiRes qw(sleep);
-use Test::Deep qw(eq_deeply cmp_deeply cmp_set cmp_bag cmp_methods useclass noclass set bag subbagof superbagof subsetof supersetof superhashof subhashof bool str arraylength Isa ignore methods regexprefonly regexpmatches num regexponly scalref reftype hashkeysonly blessed array re hash regexpref hash_each shallow array_each code arrayelementsonly arraylengthonly scalarrefonly listmethods any hashkeys isa);
-use v5.10;
-use strict;
-use warnings;
-";
+__
+    $tester->bail_out("$@") if $@;
+    feature->import(":5.10");
+    strict->import;
+    warnings->import;
+
+    $x ||= i3test::X11->new;
     @_ = ($class);
     goto \&Exporter::import;
 }
@@ -80,15 +89,16 @@ use warnings;
 # wait_for_event $x, 0.25, sub { $_[0]->{response_type} == MAP_NOTIFY };
 #
 sub wait_for_event {
-    my ($x, $timeout, $cb) = @_;
+    my ($timeout, $cb) = @_;
 
     my $cv = AE::cv;
 
-    my $prep = EV::prepare sub {
-        $x->flush;
-    };
+    $x->flush;
+
+    # unfortunately, there is no constant for this
+    my $ae_read = 0;
 
-    my $check = EV::check sub {
+    my $guard = AE::io $x->get_file_descriptor, $ae_read, sub {
         while (defined(my $event = $x->poll_for_event)) {
             if ($cb->($event)) {
                 $cv->send(1);
@@ -97,32 +107,35 @@ sub wait_for_event {
         }
     };
 
-    my $watcher = EV::io $x->get_file_descriptor, EV::READ, sub {
-        # do nothing, we only need this watcher so that EV picks up the events
-    };
-
     # Trigger timeout after $timeout seconds (can be fractional)
     my $t = AE::timer $timeout, 0, sub { warn "timeout ($timeout secs)"; $cv->send(0) };
 
     my $result = $cv->recv;
     undef $t;
+    undef $guard;
     return $result;
 }
 
 # thin wrapper around wait_for_event which waits for MAP_NOTIFY
 # make sure to include 'structure_notify' in the window’s event_mask attribute
 sub wait_for_map {
-    my ($x) = @_;
-    wait_for_event $x, 2, sub { $_[0]->{response_type} == MAP_NOTIFY };
+    my ($win) = @_;
+    my $id = (blessed($win) && $win->isa('X11::XCB::Window')) ? $win->id : $win;
+    wait_for_event 2, sub {
+        $_[0]->{response_type} == MAP_NOTIFY and $_[0]->{window} == $id
+    };
 }
 
 # Wrapper around wait_for_event which waits for UNMAP_NOTIFY. Also calls
 # sync_with_i3 to make sure i3 also picked up and processed the UnmapNotify
 # event.
 sub wait_for_unmap {
-    my ($x) = @_;
-    wait_for_event $x, 2, sub { $_[0]->{response_type} == UNMAP_NOTIFY };
-    sync_with_i3($x);
+    my ($win) = @_;
+    # my $id = (blessed($win) && $win->isa('X11::XCB::Window')) ? $win->id : $win;
+    wait_for_event 2, sub {
+        $_[0]->{response_type} == UNMAP_NOTIFY # and $_[0]->{window} == $id
+    };
+    sync_with_i3();
 }
 
 #
@@ -131,6 +144,12 @@ sub wait_for_unmap {
 #
 # set dont_map to a true value to avoid mapping
 #
+# if you want to change aspects of your window before it would be mapped,
+# set before_map to a coderef. $window gets passed as $_ and as first argument.
+#
+# if you set both dont_map and before_map, the coderef will be called nevertheless
+#
+#
 # default values:
 #     class => WINDOW_CLASS_INPUT_OUTPUT
 #     rect => [ 0, 0, 30, 30 ]
@@ -139,10 +158,10 @@ sub wait_for_unmap {
 #     name => 'Window <n>'
 #
 sub open_window {
-    my ($x, $args) = @_;
-    my %args = ($args ? %$args : ());
+    my %args = @_ == 1 ? %{$_[0]} : @_;
 
     my $dont_map = delete $args{dont_map};
+    my $before_map = delete $args{before_map};
 
     $args{class} //= WINDOW_CLASS_INPUT_OUTPUT;
     $args{rect} //= [ 0, 0, 30, 30 ];
@@ -152,24 +171,27 @@ sub open_window {
 
     my $window = $x->root->create_child(%args);
 
+    if ($before_map) {
+        # TODO: investigate why _create is not needed
+        $window->_create;
+        $before_map->($window) for $window;
+    }
+
     return $window if $dont_map;
 
     $window->map;
-    wait_for_map($x);
-    # We sync with i3 here to make sure $x->input_focus is updated.
-    sync_with_i3($x);
+    wait_for_map($window);
     return $window;
 }
 
 # Thin wrapper around open_window which sets window_type to
 # _NET_WM_WINDOW_TYPE_UTILITY to make the window floating.
 sub open_floating_window {
-    my ($x, $args) = @_;
-    my %args = ($args ? %$args : ());
+    my %args = @_ == 1 ? %{$_[0]} : @_;
 
     $args{window_type} = $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY');
 
-    return open_window($x, \%args);
+    return open_window(\%args);
 }
 
 sub open_empty_con {
@@ -308,23 +330,14 @@ sub focused_ws {
 # See also docs/testsuite for a long explanation
 #
 sub sync_with_i3 {
-    my ($x) = @_;
-
     # Since we need a (mapped) window for receiving a ClientMessage, we create
     # one on the first call of sync_with_i3. It will be re-used in all
     # subsequent calls.
     if (!defined($_sync_window)) {
-        $_sync_window = $x->root->create_child(
-            class => WINDOW_CLASS_INPUT_OUTPUT,
-            rect => X11::XCB::Rect->new(x => -15, y => -15, width => 10, height => 10 ),
+        $_sync_window = open_window(
+            rect => [ -15, -15, 10, 10 ],
             override_redirect => 1,
-            background_color => '#ff0000',
-            event_mask => [ 'structure_notify' ],
         );
-
-        $_sync_window->map;
-
-        wait_for_event $x, 2, sub { $_[0]->{response_type} == MAP_NOTIFY };
     }
 
     my $root = $x->get_root_window();
@@ -350,7 +363,7 @@ sub sync_with_i3 {
     $x->send_event(0, $root, EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
 
     # now wait until the reply is here
-    return wait_for_event $x, 2, sub {
+    return wait_for_event 2, sub {
         my ($event) = @_;
         # TODO: const
         return 0 unless $event->{response_type} == 161;
@@ -398,7 +411,6 @@ sub get_socket_path {
         return $_cached_socket_path;
     }
 
-    my $x = X11::XCB::Connection->new;
     my $atom = $x->atom(name => 'I3_SOCKET_PATH');
     my $cookie = $x->get_property(0, $x->get_root_window(), $atom->id, GET_PROPERTY_TYPE_ANY, 0, 256);
     my $reply = $x->get_property_reply($cookie->{sequence});
@@ -423,7 +435,7 @@ sub launch_with_config {
         $tmp_socket_path = File::Temp::tempnam('/tmp', 'i3-test-socket-');
     }
 
-    my ($fh, $tmpfile) = tempfile('i3-test-config-XXXXX', UNLINK => 1);
+    my ($fh, $tmpfile) = tempfile('/tmp/i3-test-config-XXXXX', UNLINK => 1);
     say $fh $config;
     say $fh "ipc-socket $tmp_socket_path" unless $dont_add_socket_path;
     close($fh);
@@ -434,8 +446,9 @@ sub launch_with_config {
         display => $ENV{DISPLAY},
         configfile => $tmpfile,
         outdir => $ENV{OUTDIR},
-        logpath => $ENV{LOGPATH},
+        testname => $ENV{TESTNAME},
         valgrind => $ENV{VALGRIND},
+        strace => $ENV{STRACE},
         cv => $cv,
     );
 
@@ -448,4 +461,14 @@ sub launch_with_config {
     return $pid;
 }
 
+package i3test::X11;
+use parent 'X11::XCB::Connection';
+
+sub input_focus {
+    my $self = shift;
+    i3test::sync_with_i3();
+
+    return $self->SUPER::input_focus(@_);
+}
+
 1
index 0db3b83b3e824cb4bb241c304b02b6690c37a7ed..c13b87c4f57bef37e8e8ddd45f76c3976b78dfd9 100644 (file)
@@ -2,31 +2,18 @@
 # vim:ts=4:sw=4:expandtab
 
 use i3test;
-use X11::XCB qw(:all);
-
-BEGIN {
-    use_ok('X11::XCB::Window');
-}
-
-my $x = X11::XCB::Connection->new;
 
 my $original_rect = X11::XCB::Rect->new(x => 0, y => 0, width => 30, height => 30);
 
-my $window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => $original_rect,
-    background_color => '#C0C0C0',
-);
-
+my $window = open_window(rect => $original_rect, dont_map => 1);
 isa_ok($window, 'X11::XCB::Window');
 
 is_deeply($window->rect, $original_rect, "rect unmodified before mapping");
 
 $window->map;
-
-sleep(0.25);
+wait_for_map $window;
 
 my $new_rect = $window->rect;
-ok(!eq_deeply($new_rect, $original_rect), "Window got repositioned");
+ok(!eq_hash($new_rect, $original_rect), "Window got repositioned");
 
 done_testing;
index 7518c9493b466daf6109a96413a23885b6dd8d62..1377ee942407f8c615273212ea82e563b1134441 100644 (file)
@@ -3,13 +3,9 @@
 #
 # checks if i3 supports I3_SYNC
 #
-use X11::XCB qw(:all);
-use X11::XCB::Connection;
 use i3test;
 
-my $x = X11::XCB::Connection->new;
-
-my $result = sync_with_i3($x);
+my $result = sync_with_i3;
 ok($result, 'syncing was successful');
 
 done_testing;
index 982ece7e9a2c7f232dc530d19b4bfce63edeea7a..34359f20a7e28410378666bbd0f2c3665fa12295 100644 (file)
@@ -3,8 +3,6 @@
 
 use i3test;
 
-my $x = X11::XCB::Connection->new;
-
 fresh_workspace;
 
 #####################################################################
@@ -12,14 +10,14 @@ fresh_workspace;
 #####################################################################
 
 # Create a window so we can get a focus different from NULL
-my $window = open_window($x);
+my $window = open_window;
 
 my $focus = $x->input_focus;
 
 # Switch to another workspace
 fresh_workspace;
 
-sync_with_i3($x);
+sync_with_i3;
 my $new_focus = $x->input_focus;
 isnt($focus, $new_focus, "Focus changed");
 
index 1ef934ee5f99f3f6bdddf18e1a66c2d9050187c7..e998eb46fcddce97ec82634520abff3cad1599a0 100644 (file)
@@ -2,21 +2,13 @@
 # vim:ts=4:sw=4:expandtab
 
 use i3test;
-use X11::XCB qw(:all);
-
-BEGIN {
-    use_ok('X11::XCB::Window');
-}
-
-my $x = X11::XCB::Connection->new;
 
 my $original_rect = X11::XCB::Rect->new(x => 0, y => 0, width => 30, height => 30);
 
-my $window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
+my $window = open_window(
     rect => $original_rect,
     override_redirect => 1,
-    background_color => '#C0C0C0',
+    dont_map => 1,
 );
 
 isa_ok($window, 'X11::XCB::Window');
index d605328d491124ca9c4556da56afcf4f0ce85c0b..9ac45627aa39446f057a0e422f75b8c96eae2c2c 100644 (file)
@@ -2,30 +2,12 @@
 # vim:ts=4:sw=4:expandtab
 
 use i3test;
-use X11::XCB qw(:all);
-
-BEGIN {
-    use_ok('X11::XCB::Window');
-}
-
-my $x = X11::XCB::Connection->new;
 
 # Create a floating window which is smaller than the minimum enforced size of i3
-my $window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30],
-    background_color => '#C0C0C0',
-    # replace the type with 'utility' as soon as the coercion works again in X11::XCB
-    window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'),
-    event_mask => [ 'structure_notify' ],
-);
+my $window = open_floating_window;
 
 isa_ok($window, 'X11::XCB::Window');
 
-$window->map;
-
-wait_for_map $x;
-
 my ($absolute, $top) = $window->rect;
 
 ok($window->mapped, 'Window is mapped');
@@ -36,20 +18,10 @@ ok($absolute->{x} != 0 && $absolute->{y} != 0, 'i3 did not map it to (0x0)');
 
 $window->unmap;
 
-$window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 1, 1, 80, 90],
-    background_color => '#C0C0C0',
-    window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'),
-    event_mask => [ 'structure_notify' ],
-);
+$window = open_floating_window(rect => [ 1, 1, 80, 90 ]);
 
 isa_ok($window, 'X11::XCB::Window');
 
-$window->map;
-
-wait_for_map $x;
-
 ($absolute, $top) = $window->rect;
 
 cmp_ok($absolute->{width}, '==', 80, "i3 let the width at 80");
@@ -67,21 +39,12 @@ $window->unmap;
 # at least the size of its initial geometry
 #####################################################################
 
-$window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 1, 1, 80, 90],
-    background_color => '#C0C0C0',
-    #window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'),
-    event_mask => [ 'structure_notify' ],
-);
+$window = open_window(rect => [ 1, 1, 80, 90 ]);
 
 isa_ok($window, 'X11::XCB::Window');
 
-$window->map;
-
-wait_for_map $x;
-
 cmd 'floating enable';
+sync_with_i3;
 
 ($absolute, $top) = $window->rect;
 
index ae8c63f67de6789e0e366878a7dc4702a155f578..cc893adc3723a1b9e2aca6cfe71bd20286672141 100644 (file)
@@ -2,7 +2,6 @@
 # vim:ts=4:sw=4:expandtab
 
 use i3test;
-use X11::XCB qw(:all);
 use List::Util qw(first);
 
 my $i3 = i3(get_socket_path());
@@ -26,23 +25,15 @@ for my $o (@outputs) {
     }
 }
 
-BEGIN {
-    use_ok('X11::XCB::Window');
-}
-
-my $x = X11::XCB::Connection->new;
-
 ##################################
 # map a window, then fullscreen it
 ##################################
 
 my $original_rect = X11::XCB::Rect->new(x => 0, y => 0, width => 30, height => 30);
 
-my $window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
+my $window = open_window(
     rect => $original_rect,
-    background_color => '#C0C0C0',
-    event_mask => [ 'structure_notify' ],
+    dont_map => 1,
 );
 
 isa_ok($window, 'X11::XCB::Window');
@@ -51,21 +42,21 @@ is_deeply($window->rect, $original_rect, "rect unmodified before mapping");
 
 $window->map;
 
-wait_for_map $x;
+wait_for_map $window;
 
 # open another container to make the window get only half of the screen
 cmd 'open';
 
 my $new_rect = $window->rect;
-ok(!eq_deeply($new_rect, $original_rect), "Window got repositioned");
+ok(!eq_hash($new_rect, $original_rect), "Window got repositioned");
 $original_rect = $new_rect;
 
 $window->fullscreen(1);
 
-sync_with_i3($x);
+sync_with_i3;
 
 $new_rect = $window->rect;
-ok(!eq_deeply($new_rect, $original_rect), "Window got repositioned after fullscreen");
+ok(!eq_hash($new_rect, $original_rect), "Window got repositioned after fullscreen");
 
 my $orect = $output->{rect};
 my $wrect = $new_rect;
@@ -89,11 +80,9 @@ $window->unmap;
 cmd 'open';
 
 $original_rect = X11::XCB::Rect->new(x => 0, y => 0, width => 30, height => 30);
-$window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
+$window = open_window(
     rect => $original_rect,
-    background_color => 61440,
-    event_mask => [ 'structure_notify' ],
+    dont_map => 1,
 );
 
 is_deeply($window->rect, $original_rect, "rect unmodified before mapping");
@@ -101,10 +90,10 @@ is_deeply($window->rect, $original_rect, "rect unmodified before mapping");
 $window->fullscreen(1);
 $window->map;
 
-wait_for_map $x;
+wait_for_map $window;
 
 $new_rect = $window->rect;
-ok(!eq_deeply($new_rect, $original_rect), "Window got repositioned after fullscreen");
+ok(!eq_hash($new_rect, $original_rect), "Window got repositioned after fullscreen");
 ok($window->mapped, "Window is mapped after opening it in fullscreen mode");
 
 $wrect = $new_rect;
@@ -120,29 +109,27 @@ ok(abs($wrect->{height} - $orect->{height}) < $threshold, 'height coordinate ful
 ###############################################################################
 
 $original_rect = X11::XCB::Rect->new(x => 0, y => 0, width => 30, height => 30);
-my $swindow = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
+my $swindow = open_window(
     rect => $original_rect,
-    background_color => '#C0C0C0',
-    event_mask => [ 'structure_notify' ],
+    dont_map => 1,
 );
 
 $swindow->map;
 
-sync_with_i3($x);
+sync_with_i3;
 
 ok(!$swindow->mapped, 'window not mapped while fullscreen window active');
 
 $new_rect = $swindow->rect;
-ok(!eq_deeply($new_rect, $original_rect), "Window got repositioned");
+ok(!eq_hash($new_rect, $original_rect), "Window got repositioned");
 
 $swindow->fullscreen(1);
-sync_with_i3($x);
+sync_with_i3;
 
 is(fullscreen_windows(), 1, 'amount of fullscreen windows');
 
 $window->fullscreen(0);
-sync_with_i3($x);
+sync_with_i3;
 is(fullscreen_windows(), 0, 'amount of fullscreen windows');
 
 ok($swindow->mapped, 'window mapped after other fullscreen ended');
@@ -154,7 +141,7 @@ ok($swindow->mapped, 'window mapped after other fullscreen ended');
 ###########################################################################
 
 $swindow->fullscreen(0);
-sync_with_i3($x);
+sync_with_i3;
 
 is(fullscreen_windows(), 0, 'amount of fullscreen windows after disabling');
 
index 5ded494fbe9fd5e4894c04853d79532cb4a68cd9..8a795c46c0a3d4f8a555fa08f5700c0be00b9cf0 100644 (file)
@@ -3,8 +3,6 @@
 
 use i3test;
 
-my $x = X11::XCB::Connection->new;
-
 my $tmp = fresh_workspace;
 
 #####################################################################
@@ -15,9 +13,9 @@ my $tmp = fresh_workspace;
 cmd 'layout default';
 cmd 'split v';
 
-my $top = open_window($x);
-my $mid = open_window($x);
-my $bottom = open_window($x);
+my $top = open_window;
+my $mid = open_window;
+my $bottom = open_window;
 
 #
 # Returns the input focus after sending the given command to i3 via IPC
@@ -27,11 +25,10 @@ sub focus_after {
     my $msg = shift;
 
     cmd $msg;
-    sync_with_i3 $x;
     return $x->input_focus;
 }
 
-$focus = $x->input_focus;
+my $focus = $x->input_focus;
 is($focus, $bottom->id, "Latest window focused");
 
 $focus = focus_after('focus up');
index cad54c26c81aa17228591437ff735b08aaab7442..20acf49e033ded8426b45b9f54671b06006c618d 100644 (file)
@@ -2,15 +2,9 @@
 # vim:ts=4:sw=4:expandtab
 
 use i3test;
-use X11::XCB qw(:all);
+use X11::XCB 'PROP_MODE_REPLACE';
 use List::Util qw(first);
 
-BEGIN {
-    use_ok('X11::XCB::Connection') or BAIL_OUT('Cannot load X11::XCB::Connection');
-}
-
-my $x = X11::XCB::Connection->new;
-
 #####################################################################
 # verify that there is no dock window yet
 #####################################################################
@@ -29,7 +23,7 @@ my $screens = $x->screens;
 my $primary = first { $_->primary } @{$screens};
 
 # TODO: focus the primary screen before
-my $window = open_window($x, {
+my $window = open_window({
         window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
     });
 
@@ -58,7 +52,7 @@ is($docknode->{rect}->{height}, 30, 'dock node has unchanged height');
 
 $window->rect(X11::XCB::Rect->new(x => 0, y => 0, width => 50, height => 40));
 
-sync_with_i3 $x;
+sync_with_i3;
 
 @docked = get_dock_clients('top');
 is(@docked, 1, 'one dock client found');
@@ -73,7 +67,7 @@ is($docknode->{rect}->{height}, 40, 'dock height changed');
 
 $window->destroy;
 
-wait_for_unmap $x;
+wait_for_unmap $window;
 
 @docked = get_dock_clients();
 is(@docked, 0, 'no more dock clients');
@@ -82,13 +76,13 @@ is(@docked, 0, 'no more dock clients');
 # check if it gets placed on bottom (by coordinates)
 #####################################################################
 
-$window = open_window($x, {
+$window = open_window({
         rect => [ 0, 1000, 30, 30 ],
         background_color => '#FF0000',
         window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
     });
 
-my $rect = $window->rect;
+$rect = $window->rect;
 is($rect->width, $primary->rect->width, 'dock client is as wide as the screen');
 is($rect->height, 30, 'height is unchanged');
 
@@ -97,7 +91,7 @@ is(@docked, 1, 'dock client on bottom');
 
 $window->destroy;
 
-wait_for_unmap $x;
+wait_for_unmap $window;
 
 @docked = get_dock_clients();
 is(@docked, 0, 'no more dock clients');
@@ -106,7 +100,7 @@ is(@docked, 0, 'no more dock clients');
 # check if it gets placed on bottom (by hint)
 #####################################################################
 
-$window = open_window($x, {
+$window = open_window({
         dont_map => 1,
         rect => [ 0, 1000, 30, 30 ],
         background_color => '#FF0000',
@@ -131,19 +125,19 @@ $x->change_property(
 
 $window->map;
 
-wait_for_map $x;
+wait_for_map $window;
 
 @docked = get_dock_clients('top');
 is(@docked, 1, 'dock client on top');
 
 $window->destroy;
 
-wait_for_unmap $x;
+wait_for_unmap $window;
 
 @docked = get_dock_clients();
 is(@docked, 0, 'no more dock clients');
 
-$window = open_window($x, {
+$window = open_window({
         dont_map => 1,
         rect => [ 0, 1000, 30, 30 ],
         background_color => '#FF0000',
@@ -153,8 +147,8 @@ $window = open_window($x, {
 $window->_create();
 
 # Add a _NET_WM_STRUT_PARTIAL hint
-my $atomname = $x->atom(name => '_NET_WM_STRUT_PARTIAL');
-my $atomtype = $x->atom(name => 'CARDINAL');
+$atomname = $x->atom(name => '_NET_WM_STRUT_PARTIAL');
+$atomtype = $x->atom(name => 'CARDINAL');
 
 $x->change_property(
     PROP_MODE_REPLACE,
@@ -168,7 +162,7 @@ $x->change_property(
 
 $window->map;
 
-wait_for_map $x;
+wait_for_map $window;
 
 @docked = get_dock_clients('bottom');
 is(@docked, 1, 'dock client on bottom');
@@ -180,7 +174,7 @@ $window->destroy;
 # regression test: transient dock client
 #####################################################################
 
-$fwindow = open_window($x, {
+my $fwindow = open_window({
         dont_map => 1,
         background_color => '#FF0000',
         window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
@@ -189,7 +183,7 @@ $fwindow = open_window($x, {
 $fwindow->transient_for($window);
 $fwindow->map;
 
-wait_for_map $x;
+wait_for_map $fwindow;
 
 does_i3_live;
 
index 6e35ebe49ee7c0ba5847b4077bc4bf2b0d81e701..040faf203edbe05f90e0f72aed1d67d46cda667f 100644 (file)
@@ -48,7 +48,7 @@ sub focus_after {
     return $x->input_focus;
 }
 
-$focus = $x->input_focus;
+my $focus = $x->input_focus;
 is($focus, $bottom->id, "Latest window focused");
 
 $focus = focus_after("ml");
index b5be284c3b0cbde3eedc9b6387643a490100d06c..3b3fe74d79f5c02a844aa053e005a2a275e327f1 100644 (file)
@@ -5,25 +5,23 @@
 
 use i3test;
 
-my $x = X11::XCB::Connection->new;
-
 fresh_workspace;
 
 cmd 'split h';
-my $tiled_left = open_window($x);
-my $tiled_right = open_window($x);
+my $tiled_left = open_window;
+my $tiled_right = open_window;
 
 # Get input focus before creating the floating window
 my $focus = $x->input_focus;
 
 # Create a floating window which is smaller than the minimum enforced size of i3
-my $window = open_floating_window($x);
+my $window = open_floating_window;
 
 is($x->input_focus, $window->id, 'floating window focused');
 
 $window->unmap;
 
-wait_for_unmap($x);
+wait_for_unmap $window;
 
 is($x->input_focus, $focus, 'Focus correctly restored');
 
index cc285f32788162d946c1dac5a47d2830d725d8ea..ec7b8df874ab71d6b33b7742a83fda3d5cbffd12 100644 (file)
@@ -46,7 +46,7 @@ sub focus_after {
     return $x->input_focus;
 }
 
-$focus = $x->input_focus;
+my $focus = $x->input_focus;
 is($focus, $bottom->id, "Latest window focused");
 
 $focus = focus_after("s");
index 903fa0c400b8c3c8b6dea016cff6e477570dae9c..dd60d9cf200d33964b8471cd306d2e43be3410ae 100644 (file)
@@ -4,8 +4,6 @@
 use i3test;
 use File::Temp;
 
-my $x = X11::XCB::Connection->new;
-
 my $tmp = fresh_workspace;
 
 cmd 'split h';
@@ -14,9 +12,9 @@ cmd 'split h';
 # Create two windows and make sure focus switching works
 #####################################################################
 
-my $top = open_window($x);
-my $mid = open_window($x);
-my $bottom = open_window($x);
+my $top = open_window;
+my $mid = open_window;
+my $bottom = open_window;
 
 #
 # Returns the input focus after sending the given command to i3 via IPC
@@ -26,11 +24,10 @@ sub focus_after {
     my $msg = shift;
 
     cmd $msg;
-    sync_with_i3($x);
     return $x->input_focus;
 }
 
-$focus = $x->input_focus;
+my $focus = $x->input_focus;
 is($focus, $bottom->id, "Latest window focused");
 
 $focus = focus_after('focus left');
index ac3387a9ef7afcd69120ca25ca316a2b02005e2d..ff3a1c04e0922cf3b3cd759b278f27894e2f8651 100644 (file)
@@ -2,13 +2,6 @@
 # vim:ts=4:sw=4:expandtab
 
 use i3test;
-use X11::XCB qw(:all);
-
-BEGIN {
-    use_ok('X11::XCB::Connection') or BAIL_OUT('Cannot load X11::XCB::Connection');
-}
-
-my $x = X11::XCB::Connection->new;
 
 fresh_workspace;
 
@@ -16,13 +9,13 @@ fresh_workspace;
 # Create a floating window and see if resizing works
 #####################################################################
 
-my $window = open_floating_window($x);
+my $window = open_floating_window;
 
 # See if configurerequests cause window movements (they should not)
 my ($a, $t) = $window->rect;
 $window->rect(X11::XCB::Rect->new(x => $a->x, y => $a->y, width => $a->width, height => $a->height));
 
-sync_with_i3($x);
+sync_with_i3;
 
 my ($na, $nt) = $window->rect;
 is_deeply($na, $a, 'Rects are equal after configurerequest');
@@ -30,7 +23,7 @@ is_deeply($na, $a, 'Rects are equal after configurerequest');
 sub test_resize {
     $window->rect(X11::XCB::Rect->new(x => 0, y => 0, width => 100, height => 100));
 
-    sync_with_i3($x);
+    sync_with_i3;
 
     my ($absolute, $top) = $window->rect;
 
@@ -41,7 +34,7 @@ sub test_resize {
 
     $window->rect(X11::XCB::Rect->new(x => 0, y => 0, width => 300, height => 500));
 
-    sync_with_i3($x);
+    sync_with_i3;
 
     ($absolute, $top) = $window->rect;
 
index 7954408fa2bf30efaa30d590f0c16ef1fedf2ea2..5ee61a4d2b887c34a2776e3920a27743ffefe2df 100644 (file)
@@ -2,15 +2,8 @@
 # vim:ts=4:sw=4:expandtab
 
 use i3test;
-use X11::XCB qw(:all);
 use List::Util qw(first);
 
-BEGIN {
-    use_ok('X11::XCB::Connection') or BAIL_OUT('Cannot load X11::XCB::Connection');
-}
-
-my $x = X11::XCB::Connection->new;
-
 my $tmp = fresh_workspace;
 
 #####################################################################
@@ -19,8 +12,8 @@ my $tmp = fresh_workspace;
 
 cmd 'split v';
 
-my $top = open_window($x);
-my $bottom = open_window($x);
+my $top = open_window;
+my $bottom = open_window;
 
 my @urgent = grep { $_->{urgent} } @{get_ws_content($tmp)};
 is(@urgent, 0, 'no window got the urgent flag');
@@ -31,12 +24,12 @@ is(@urgent, 0, 'no window got the urgent flag');
 # Add the urgency hint, switch to a different workspace and back again
 #####################################################################
 $top->add_hint('urgency');
-sync_with_i3($x);
+sync_with_i3;
 
-@content = @{get_ws_content($tmp)};
+my @content = @{get_ws_content($tmp)};
 @urgent = grep { $_->{urgent} } @content;
-$top_info = first { $_->{window} == $top->id } @content;
-$bottom_info = first { $_->{window} == $bottom->id } @content;
+my $top_info = first { $_->{window} == $top->id } @content;
+my $bottom_info = first { $_->{window} == $bottom->id } @content;
 
 ok($top_info->{urgent}, 'top window is marked urgent');
 ok(!$bottom_info->{urgent}, 'bottom window is not marked urgent');
@@ -48,7 +41,7 @@ cmd '[id="' . $top->id . '"] focus';
 is(@urgent, 0, 'no window got the urgent flag after focusing');
 
 $top->add_hint('urgency');
-sync_with_i3($x);
+sync_with_i3;
 
 @urgent = grep { $_->{urgent} } @{get_ws_content($tmp)};
 is(@urgent, 0, 'no window got the urgent flag after re-setting urgency hint');
@@ -62,7 +55,7 @@ ok(!$ws->{urgent}, 'urgent flag not set on workspace');
 my $otmp = fresh_workspace;
 
 $top->add_hint('urgency');
-sync_with_i3($x);
+sync_with_i3;
 
 $ws = get_ws($tmp);
 ok($ws->{urgent}, 'urgent flag set on workspace');
index 6f7ffce032e3d440e99552c8e5ef5b9aa97037f5..497bad9e1aa73195bc8eb6d434650032a842fe83 100644 (file)
@@ -2,13 +2,6 @@
 # vim:ts=4:sw=4:expandtab
 
 use i3test;
-use X11::XCB qw(:all);
-
-BEGIN {
-    use_ok('X11::XCB::Connection') or BAIL_OUT('Cannot load X11::XCB::Connection');
-}
-
-my $x = X11::XCB::Connection->new;
 
 my $tmp = fresh_workspace;
 
@@ -20,42 +13,42 @@ my $tmp = fresh_workspace;
 # one of both (depending on your screen resolution) will be positioned wrong.
 ####################################################################################
 
-my $left = open_window($x, { name => 'Left' });
-my $right = open_window($x, { name => 'Right' });
+my $left = open_window({ name => 'Left' });
+my $right = open_window({ name => 'Right' });
 
 my ($abs, $rgeom) = $right->rect;
 
-my $child = open_floating_window($x, {
+my $child = open_floating_window({
         dont_map => 1,
         name => 'Child window',
     });
 $child->client_leader($right);
 $child->map;
 
-ok(wait_for_map($x), 'child window mapped');
+ok(wait_for_map($child), 'child window mapped');
 
 my $cgeom;
 ($abs, $cgeom) = $child->rect;
 cmp_ok($cgeom->x, '>=', $rgeom->x, 'Child X >= right container X');
 
-my $child2 = open_floating_window($x, {
+my $child2 = open_floating_window({
         dont_map => 1,
         name => 'Child window 2',
     });
 $child2->client_leader($left);
 $child2->map;
 
-ok(wait_for_map($x), 'second child window mapped');
+ok(wait_for_map($child2), 'second child window mapped');
 
 ($abs, $cgeom) = $child2->rect;
 cmp_ok(($cgeom->x + $cgeom->width), '<', $rgeom->x, 'child above left window');
 
 # check wm_transient_for
-my $fwindow = open_window($x, { dont_map => 1 });
+my $fwindow = open_window({ dont_map => 1 });
 $fwindow->transient_for($right);
 $fwindow->map;
 
-ok(wait_for_map($x), 'transient window mapped');
+ok(wait_for_map($fwindow), 'transient window mapped');
 
 my ($absolute, $top) = $fwindow->rect;
 ok($absolute->{x} != 0 && $absolute->{y} != 0, 'i3 did not map it to (0x0)');
@@ -67,10 +60,10 @@ SKIP: {
 # Create a parent window
 #####################################################################
 
-my $window = open_window($x, { dont_map => 1, name => 'Parent window' });
+my $window = open_window({ dont_map => 1, name => 'Parent window' });
 $window->map;
 
-ok(wait_for_map($x), 'parent window mapped');
+ok(wait_for_map($window), 'parent window mapped');
 
 #########################################################################
 # Switch to a different workspace and open a child window. It should be opened
@@ -78,11 +71,11 @@ ok(wait_for_map($x), 'parent window mapped');
 #########################################################################
 fresh_workspace;
 
-my $child = open_window($x, { dont_map => 1, name => 'Child window' });
+my $child = open_window({ dont_map => 1, name => 'Child window' });
 $child->client_leader($window);
 $child->map;
 
-ok(wait_for_map($x), 'child window mapped');
+ok(wait_for_map($child), 'child window mapped');
 
 isnt($x->input_focus, $child->id, "Child window focused");
 
index 4b3958a1eb4ec09bc94f15802f325a363649b893..f6d4848dd8afd57bcf9464c68a68fbf9f694a163 100644 (file)
@@ -29,28 +29,44 @@ my $i3 = i3(get_socket_path());
 
 my $tree = $i3->get_tree->recv;
 
+# a unique value
+my $ignore = \"";
+
 my $expected = {
     fullscreen_mode => 0,
-    nodes => ignore(),
+    nodes => $ignore,
     window => undef,
     name => 'root',
-    orientation => ignore(),
+    orientation => $ignore,
     type => 0,
-    id => ignore(),
-    rect => ignore(),
-    window_rect => ignore(),
-    geometry => ignore(),
-    swallows => ignore(),
+    id => $ignore,
+    rect => $ignore,
+    window_rect => $ignore,
+    geometry => $ignore,
+    swallows => $ignore,
     percent => undef,
     layout => 'default',
-    focus => ignore(),
+    focus => $ignore,
     focused => JSON::XS::false,
     urgent => JSON::XS::false,
     border => 'normal',
-    'floating_nodes' => ignore(),
+    'floating_nodes' => $ignore,
 };
 
-cmp_deeply($tree, $expected, 'root node OK');
+# a shallow copy is sufficient, since we only ignore values at the root
+my $tree_copy = { %$tree };
+
+for (keys %$expected) {
+    my $val = $expected->{$_};
+
+    # delete unwanted keys, so we can use is_deeply()
+    if (ref($val) eq 'SCALAR' and $val == $ignore) {
+        delete $tree_copy->{$_};
+        delete $expected->{$_};
+    }
+}
+
+is_deeply($tree_copy, $expected, 'root node OK');
 
 my @nodes = @{$tree->{nodes}};
 
index 3c3b6cc675421a510dc8c25b723198c2e8bf5006..3c52e31eea1399fc8e569ffcbfaf4bd57674b22f 100644 (file)
@@ -108,12 +108,12 @@ ok(defined($ws), "workspace 3: $tmp was created");
 is($ws->{num}, 3, 'workspace number is 3');
 
 cmd "workspace 0: $tmp";
-my $ws = get_ws("0: $tmp");
+$ws = get_ws("0: $tmp");
 ok(defined($ws), "workspace 0: $tmp was created");
 is($ws->{num}, 0, 'workspace number is 0');
 
 cmd "workspace aa: $tmp";
-my $ws = get_ws("aa: $tmp");
+$ws = get_ws("aa: $tmp");
 ok(defined($ws), "workspace aa: $tmp was created");
 is($ws->{num}, -1, 'workspace number is -1');
 
index 8b9d21d3e0da46fbc422bda5041cd9365fcbbb61..e6a4e832fdb3daa2a7da0d7abe565d68c9cba049 100644 (file)
@@ -4,15 +4,14 @@
 # Tests all kinds of matching methods
 #
 use i3test;
-use X11::XCB qw(:all);
+use X11::XCB qw(PROP_MODE_REPLACE);
 
 my $tmp = fresh_workspace;
 
 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
 
 # Open a new window
-my $x = X11::XCB::Connection->new;
-my $window = open_window($x);
+my $window = open_window;
 my $content = get_ws_content($tmp);
 ok(@{$content} == 1, 'window mapped');
 my $win = $content->[0];
@@ -35,7 +34,7 @@ cmd 'nop now killing the window';
 my $id = $win->{id};
 cmd qq|[con_id="$id"] kill|;
 
-wait_for_unmap $x;
+wait_for_unmap $window;
 
 cmd 'nop checking if its gone';
 $content = get_ws_content($tmp);
@@ -70,31 +69,21 @@ sub set_wm_class {
     );
 }
 
-my $left = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30 ],
-    background_color => '#0000ff',
-    event_mask => [ 'structure_notify' ],
-);
-
-$left->_create;
-set_wm_class($left->id, 'special', 'special');
-$left->name('left');
-$left->map;
-ok(wait_for_map($x), 'left window mapped');
-
-my $right = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30 ],
-    background_color => '#0000ff',
-    event_mask => [ 'structure_notify' ],
-);
-
-$right->_create;
-set_wm_class($right->id, 'special', 'special');
-$right->name('right');
-$right->map;
-ok(wait_for_map($x), 'right window mapped');
+sub open_special {
+    my %args = @_;
+    my $wm_class = delete($args{wm_class}) || 'special';
+
+    return open_window(
+        %args,
+        before_map => sub { set_wm_class($_->id, $wm_class, $wm_class) },
+    );
+}
+
+my $left = open_special(name => 'left');
+ok($left->mapped, 'left window mapped');
+
+my $right = open_special(name => 'right');
+ok($right->mapped, 'right window mapped');
 
 # two windows should be here
 $content = get_ws_content($tmp);
@@ -102,7 +91,7 @@ ok(@{$content} == 2, 'two windows opened');
 
 cmd '[class="special" title="left"] kill';
 
-sync_with_i3($x);
+sync_with_i3;
 
 $content = get_ws_content($tmp);
 is(@{$content}, 1, 'one window still there');
@@ -113,18 +102,8 @@ is(@{$content}, 1, 'one window still there');
 
 $tmp = fresh_workspace;
 
-$left = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30 ],
-    background_color => '#0000ff',
-    event_mask => [ 'structure_notify' ],
-);
-
-$left->_create;
-set_wm_class($left->id, 'special7', 'special7');
-$left->name('left');
-$left->map;
-ok(wait_for_map($x), 'left window mapped');
+$left = open_special(name => 'left', wm_class => 'special7');
+ok($left->mapped, 'left window mapped');
 
 # two windows should be here
 $content = get_ws_content($tmp);
@@ -132,7 +111,7 @@ ok(@{$content} == 1, 'window opened');
 
 cmd '[class="^special[0-9]$"] kill';
 
-wait_for_unmap $x;
+wait_for_unmap $left;
 
 $content = get_ws_content($tmp);
 is(@{$content}, 0, 'window killed');
@@ -143,18 +122,8 @@ is(@{$content}, 0, 'window killed');
 
 $tmp = fresh_workspace;
 
-$left = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30 ],
-    background_color => '#0000ff',
-    event_mask => [ 'structure_notify' ],
-);
-
-$left->_create;
-set_wm_class($left->id, 'special7', 'special7');
-$left->name('ä 3');
-$left->map;
-ok(wait_for_map($x), 'left window mapped');
+$left = open_special(name => 'ä 3', wm_class => 'special7');
+ok($left->mapped, 'left window mapped');
 
 # two windows should be here
 $content = get_ws_content($tmp);
@@ -162,7 +131,7 @@ ok(@{$content} == 1, 'window opened');
 
 cmd '[title="^\w [3]$"] kill';
 
-wait_for_unmap $x;
+wait_for_unmap $left;
 
 $content = get_ws_content($tmp);
 is(@{$content}, 0, 'window killed');
index 3484c7fc81212ae4db9328d00ea7de6ee61eb086..71736db462fd7e8d47400f5f25e4cf754d36d12e 100644 (file)
@@ -4,7 +4,6 @@
 # Tests splitting
 #
 use i3test;
-use X11::XCB qw(:all);
 
 my $tmp = fresh_workspace;
 
index a6eb61648384d2108a3a4f1a53f769cc9fe3b091..a078a9e842f5f4454dc71ecd7ac8864c611e515c 100644 (file)
@@ -8,9 +8,7 @@
 # 4) move a container in a different direction so that we need to go up in tree
 #
 use i3test;
-use X11::XCB::Connection;
 
-my $x = X11::XCB::Connection->new;
 my $i3 = i3(get_socket_path());
 
 my $tmp = fresh_workspace;
@@ -138,7 +136,7 @@ is(@{$content}, 1, 'only one nodes on this workspace');
 ######################################################################
 
 $tmp = fresh_workspace;
-my $floatwin = open_floating_window($x);
+my $floatwin = open_floating_window;
 my ($absolute_before, $top_before) = $floatwin->rect;
 
 cmd 'move left';
index 52b8b9c0624693e1ae246fd9e22b8d805b64900c..c83c0809e3c50942704be11a7b04c6f3037c4f1f 100644 (file)
@@ -16,8 +16,7 @@ cmd 'open';
 my $floating = get_focused($tmp);
 diag("focused floating: " . get_focused($tmp));
 cmd 'mode toggle';
-# TODO: eliminate this race conditition
-sleep 1;
+sync_with_i3;
 
 # kill old container
 cmd qq|[con_id="$old"] focus|;
@@ -33,7 +32,7 @@ cmd 'kill';
 cmd qq|[con_id="$floating"] focus|;
 is(get_focused($tmp), $floating, 'floating window focused');
 
-sleep 1;
+sync_with_i3;
 cmd 'mode toggle';
 
 does_i3_live;
index b638e708446606d3820b4fbfd506a744b5b1975f..ee58968f7f083166adf4c1c0eccb156670f50374 100644 (file)
@@ -27,7 +27,7 @@ isnt($first, $second, 'different container focused');
 
 cmd qq|[con_id="$first"] focus|;
 cmd 'open';
-$content = get_ws_content($tmp);
+my $content = get_ws_content($tmp);
 ok(@{$content} == 3, 'three containers opened');
 
 is($content->[0]->{id}, $first, 'first container unmodified');
index 8d22561359234e5643d3815e9fc3081e350c183f..5fc3786e02292bfdbf988053aad25a56fc95a5a0 100644 (file)
@@ -4,11 +4,8 @@
 # Check if the focus is correctly restored after closing windows.
 #
 use i3test;
-use X11::XCB qw(:all);
 use List::Util qw(first);
 
-my $x = X11::XCB::Connection->new;
-
 my $i3 = i3(get_socket_path());
 
 my $tmp = fresh_workspace;
@@ -45,9 +42,8 @@ isnt(get_focused($tmp), $second, 'different container focused');
 ##############################################################
 
 cmd 'kill';
-# TODO: this testcase sometimes has different outcomes when the
-# sleep is missing. why?
-sleep 0.25;
+sync_with_i3;
+
 ($nodes, $focus) = get_ws_content($tmp);
 is($nodes->[1]->{nodes}->[0]->{id}, $second, 'second container found');
 ok($nodes->[1]->{nodes}->[0]->{focused}, 'second container focused');
@@ -102,12 +98,11 @@ $first = open_empty_con($i3);
 $middle = open_empty_con($i3);
 # XXX: the $right empty con will be filled with the x11 window we are creating afterwards
 $right = open_empty_con($i3);
-my $win = open_window($x, { background_color => '#00ff00' });
+my $win = open_window({ background_color => '#00ff00' });
 
 cmd qq|[con_id="$middle"] focus|;
 $win->destroy;
-
-sleep 0.25;
+sync_with_i3;
 
 is(get_focused($tmp), $middle, 'middle container focused');
 
index 57855cd5cfb4409fece6e8b5cdc5854c9e6b0b4e..ce9ebd7a16afa79e6dd79f4c0af26d51c68d41bf 100644 (file)
@@ -17,7 +17,7 @@ cmd qq|[con_id="$first"] focus|;
 
 cmd 'split v';
 
-($nodes, $focus) = get_ws_content($tmp);
+my ($nodes, $focus) = get_ws_content($tmp);
 
 is($nodes->[0]->{focused}, 0, 'split container not focused');
 
@@ -27,7 +27,7 @@ cmd 'level up';
 my $split = $focus->[0];
 cmd 'level down';
 
-my $second = open_empty_con($i3);
+$second = open_empty_con($i3);
 
 isnt($first, $second, 'different container focused');
 
@@ -62,10 +62,10 @@ is($nodes->[0]->{focused}, 0, 'split container not focused');
 # focus the split container
 cmd 'level up';
 ($nodes, $focus) = get_ws_content($tmp);
-my $split = $focus->[0];
+$split = $focus->[0];
 cmd 'level down';
 
-my $second = open_empty_con($i3);
+$second = open_empty_con($i3);
 
 isnt($first, $second, 'different container focused');
 
index 82e59dd11cea0f1373fe2dacc76187a2f9e0e2fb..1643fc756cece0d274db3a7b721f6729cbb03125 100644 (file)
@@ -9,7 +9,6 @@ my $i3 = i3(get_socket_path());
 
 # We move the pointer out of our way to avoid a bug where the focus will
 # be set to the window under the cursor
-my $x = X11::XCB::Connection->new;
 $x->root->warp_pointer(0, 0);
 
 my $tmp = get_unused_workspace();
index d2d77e8e0f2ae6abb6b14684776dfcf26b96c62b..d3736e3c35fee0d64db0546b4de63adcdd8da24b 100644 (file)
@@ -5,13 +5,11 @@
 #
 use i3test;
 
-my $x = X11::XCB::Connection->new;
-
 my $tmp = fresh_workspace;
 
 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
 
-my $win = open_window($x, { dont_map => 1 });
+my $win = open_window({ dont_map => 1 });
 # XXX: we should check screen size. in screens with an AR of 2.0,
 # this is not a good idea.
 my $aspect = X11::XCB::Sizehints::Aspect->new;
@@ -21,11 +19,11 @@ $aspect->max_num(600);
 $aspect->max_den(300);
 $win->_create;
 $win->map;
-wait_for_map $x;
+wait_for_map $win;
 $win->hints->aspect($aspect);
 $x->flush;
 
-sync_with_i3($x);
+sync_with_i3;
 
 my $rect = $win->rect;
 my $ar = $rect->width / $rect->height;
index a1ab211e501d59531bb50e8cb8d86a4bf91cc008..c7218130166f082f22a8dcd2a991df3fcc000fc7 100644 (file)
@@ -2,10 +2,6 @@
 # vim:ts=4:sw=4:expandtab
 
 use i3test;
-use X11::XCB qw(:all);
-use X11::XCB::Connection;
-
-my $x = X11::XCB::Connection->new;
 
 my $tmp = fresh_workspace;
 
@@ -13,8 +9,8 @@ my $tmp = fresh_workspace;
 # 1: see if focus stays the same when toggling tiling/floating mode
 #############################################################################
 
-my $first = open_window($x);
-my $second = open_window($x);
+my $first = open_window;
+my $second = open_window;
 
 is($x->input_focus, $second->id, 'second window focused');
 
@@ -30,9 +26,9 @@ is($x->input_focus, $second->id, 'second window still focused after mode toggle'
 
 $tmp = fresh_workspace;
 
-$first = open_window($x);    # window 2
-$second = open_window($x);   # window 3
-my $third = open_window($x); # window 4
+$first = open_window;    # window 2
+$second = open_window;   # window 3
+my $third = open_window; # window 4
 
 is($x->input_focus, $third->id, 'last container focused');
 
@@ -40,8 +36,6 @@ cmd 'floating enable';
 
 cmd '[id="' . $second->id . '"] focus';
 
-sync_with_i3($x);
-
 is($x->input_focus, $second->id, 'second con focused');
 
 cmd 'floating enable';
@@ -49,8 +43,7 @@ cmd 'floating enable';
 # now kill the third one (it's floating). focus should stay unchanged
 cmd '[id="' . $third->id . '"] kill';
 
-# TODO: wait for unmapnotify
-sync_with_i3($x);
+wait_for_unmap($third);
 
 is($x->input_focus, $second->id, 'second con still focused after killing third');
 
@@ -62,9 +55,9 @@ is($x->input_focus, $second->id, 'second con still focused after killing third')
 
 $tmp = fresh_workspace;
 
-$first = open_window($x, { background_color => '#ff0000' });    # window 5
-$second = open_window($x, { background_color => '#00ff00' });   # window 6
-my $third = open_window($x, { background_color => '#0000ff' }); # window 7
+$first = open_window({ background_color => '#ff0000' });    # window 5
+$second = open_window({ background_color => '#00ff00' });   # window 6
+$third = open_window({ background_color => '#0000ff' }); # window 7
 
 is($x->input_focus, $third->id, 'last container focused');
 
@@ -72,8 +65,6 @@ cmd 'floating enable';
 
 cmd '[id="' . $second->id . '"] focus';
 
-sync_with_i3($x);
-
 is($x->input_focus, $second->id, 'second con focused');
 
 cmd 'floating enable';
@@ -81,15 +72,12 @@ cmd 'floating enable';
 # now kill the second one. focus should fall back to the third one, which is
 # also floating
 cmd 'kill';
-
-# TODO: wait for unmapnotify
-sync_with_i3($x);
+wait_for_unmap($second);
 
 is($x->input_focus, $third->id, 'third con focused');
 
 cmd 'kill';
-# TODO: wait for unmapnotify
-sync_with_i3($x);
+wait_for_unmap($third);
 
 is($x->input_focus, $first->id, 'first con focused after killing all floating cons');
 
@@ -99,11 +87,11 @@ is($x->input_focus, $first->id, 'first con focused after killing all floating co
 
 $tmp = fresh_workspace;
 
-$first = open_window($x, { background_color => '#ff0000' });    # window 5
+$first = open_window({ background_color => '#ff0000' });    # window 5
 cmd 'split v';
 cmd 'layout stacked';
-$second = open_window($x, { background_color => '#00ff00' });   # window 6
-$third = open_window($x, { background_color => '#0000ff' }); # window 7
+$second = open_window({ background_color => '#00ff00' });   # window 6
+$third = open_window({ background_color => '#0000ff' }); # window 7
 
 is($x->input_focus, $third->id, 'last container focused');
 
@@ -111,26 +99,21 @@ cmd 'floating enable';
 
 cmd '[id="' . $second->id . '"] focus';
 
-sync_with_i3($x);
-
 is($x->input_focus, $second->id, 'second con focused');
 
 cmd 'floating enable';
 
-sync_with_i3($x);
+sync_with_i3;
 
 # now kill the second one. focus should fall back to the third one, which is
 # also floating
 cmd 'kill';
-
-# TODO: wait for unmapnotify
-sync_with_i3($x);
+wait_for_unmap($second);
 
 is($x->input_focus, $third->id, 'third con focused');
 
 cmd 'kill';
-# TODO: wait for unmapnotify
-sync_with_i3($x);
+wait_for_unmap($third);
 
 is($x->input_focus, $first->id, 'first con focused after killing all floating cons');
 
@@ -140,10 +123,8 @@ is($x->input_focus, $first->id, 'first con focused after killing all floating co
 
 $tmp = fresh_workspace;
 
-$first = open_window($x, { background_color => '#ff0000' });    # window 8
-$second = open_window($x, { background_color => '#00ff00' });   # window 9
-
-sync_with_i3($x);
+$first = open_window({ background_color => '#ff0000' });    # window 8
+$second = open_window({ background_color => '#00ff00' });   # window 9
 
 is($x->input_focus, $second->id, 'second container focused');
 
@@ -153,32 +134,22 @@ is($x->input_focus, $second->id, 'second container focused');
 
 cmd 'focus tiling';
 
-sync_with_i3($x);
-
 is($x->input_focus, $first->id, 'first (tiling) container focused');
 
 cmd 'focus floating';
 
-sync_with_i3($x);
-
 is($x->input_focus, $second->id, 'second (floating) container focused');
 
 cmd 'focus floating';
 
-sync_with_i3($x);
-
 is($x->input_focus, $second->id, 'second (floating) container still focused');
 
 cmd 'focus mode_toggle';
 
-sync_with_i3($x);
-
 is($x->input_focus, $first->id, 'first (tiling) container focused');
 
 cmd 'focus mode_toggle';
 
-sync_with_i3($x);
-
 is($x->input_focus, $second->id, 'second (floating) container focused');
 
 #############################################################################
@@ -187,42 +158,30 @@ is($x->input_focus, $second->id, 'second (floating) container focused');
 
 $tmp = fresh_workspace;
 
-$first = open_floating_window($x, { background_color => '#ff0000' });# window 10
-$second = open_floating_window($x, { background_color => '#00ff00' }); # window 11
-$third = open_floating_window($x, { background_color => '#0000ff' }); # window 12
-
-sync_with_i3($x);
+$first = open_floating_window({ background_color => '#ff0000' });# window 10
+$second = open_floating_window({ background_color => '#00ff00' }); # window 11
+$third = open_floating_window({ background_color => '#0000ff' }); # window 12
 
 is($x->input_focus, $third->id, 'third container focused');
 
 cmd 'focus left';
 
-sync_with_i3($x);
-
 is($x->input_focus, $second->id, 'second container focused');
 
 cmd 'focus left';
 
-sync_with_i3($x);
-
 is($x->input_focus, $first->id, 'first container focused');
 
 cmd 'focus left';
 
-sync_with_i3($x);
-
 is($x->input_focus, $third->id, 'focus wrapped to third container');
 
 cmd 'focus right';
 
-sync_with_i3($x);
-
 is($x->input_focus, $first->id, 'focus wrapped to first container');
 
 cmd 'focus right';
 
-sync_with_i3($x);
-
 is($x->input_focus, $second->id, 'focus on second container');
 
 done_testing;
index a6e0e405e867de092fa8a42035a3dcc45576fb89..f17b0ec146fa465b9ffad2c5de81a867b768edf6 100644 (file)
@@ -3,11 +3,6 @@
 # Regression test: when only having a floating window on a workspace, it should not be deleted.
 
 use i3test;
-use X11::XCB qw(:all);
-
-BEGIN {
-    use_ok('X11::XCB::Window');
-}
 
 my $i3 = i3(get_socket_path());
 
@@ -19,10 +14,8 @@ my $tmp = fresh_workspace;
 
 ok(workspace_exists($tmp), "workspace $tmp exists");
 
-my $x = X11::XCB::Connection->new;
-
 # Create a floating window which is smaller than the minimum enforced size of i3
-my $window = open_floating_window($x);
+my $window = open_floating_window;
 ok($window->mapped, 'Window is mapped');
 
 # switch to a different workspace, see if the window is still mapped?
index ab1a33d313e7d9478476e30258a0828baf437278..e91870bc35e0ee1fd6bd1a203803f96c1355635a 100644 (file)
@@ -4,11 +4,6 @@
 # to a different workspace.
 
 use i3test;
-use X11::XCB qw(:all);
-
-BEGIN {
-    use_ok('X11::XCB::Window');
-}
 
 my $i3 = i3(get_socket_path());
 
@@ -18,17 +13,15 @@ my $tmp = fresh_workspace;
 # 1: open a floating window, get it mapped
 #############################################################################
 
-my $x = X11::XCB::Connection->new;
-
 # Create a floating window which is smaller than the minimum enforced size of i3
-my $window = open_floating_window($x);
+my $window = open_floating_window;
 ok($window->mapped, 'Window is mapped');
 
 # switch to a different workspace, see if the window is still mapped?
 
 my $otmp = fresh_workspace;
 
-sync_with_i3($x);
+sync_with_i3;
 
 ok(!$window->mapped, 'Window is not mapped after switching ws');
 
index b08190a22036ba320f40848fcb83294411917fd3..db86e1ca71e6e9c2e044ad8c94508697f2628cff 100644 (file)
@@ -4,11 +4,6 @@
 # if only a floating window is present on the workspace.
 
 use i3test;
-use X11::XCB qw(:all);
-
-BEGIN {
-    use_ok('X11::XCB::Window');
-}
 
 my $i3 = i3(get_socket_path());
 
@@ -18,10 +13,8 @@ my $tmp = fresh_workspace;
 # 1: open a floating window, get it mapped
 #############################################################################
 
-my $x = X11::XCB::Connection->new;
-
 # Create a floating window
-my $window = open_floating_window($x);
+my $window = open_floating_window;
 ok($window->mapped, 'Window is mapped');
 
 my $ws = get_ws($tmp);
@@ -31,7 +24,7 @@ is(@{$ws->{floating_nodes}}, 1, 'one floating node');
 is(@{$nodes}, 0, 'no tiling nodes');
 
 # Create a tiling window
-my $twindow = open_window($x);
+my $twindow = open_window;
 
 ($nodes, $focus) = get_ws_content($tmp);
 
@@ -44,8 +37,8 @@ is(@{$nodes}, 1, 'one tiling node');
 
 $tmp = fresh_workspace;
 
-my $first = open_window($x);
-my $second = open_window($x);
+my $first = open_window;
+my $second = open_window;
 
 cmd 'layout stacked';
 
@@ -54,14 +47,14 @@ is(@{$ws->{floating_nodes}}, 0, 'no floating nodes so far');
 is(@{$ws->{nodes}}, 1, 'one tiling node (stacked con)');
 
 # Create a floating window
-my $window = open_floating_window($x);
+$window = open_floating_window;
 ok($window->mapped, 'Window is mapped');
 
 $ws = get_ws($tmp);
 is(@{$ws->{floating_nodes}}, 1, 'one floating nodes');
 is(@{$ws->{nodes}}, 1, 'one tiling node (stacked con)');
 
-my $third = open_window($x);
+my $third = open_window;
 
 
 $ws = get_ws($tmp);
index 31f013efd8cb2646d436f3f5d2e1bf9de13b0371..78b9191a73f278374d8b5a0a50aae230826434d9 100644 (file)
@@ -3,14 +3,8 @@
 # Check if numbered workspaces and named workspaces are sorted in the right way
 # in get_workspaces IPC output (necessary for i3bar etc.).
 use i3test;
-use X11::XCB qw(:all);
-
-BEGIN {
-    use_ok('X11::XCB::Window');
-}
 
 my $i3 = i3(get_socket_path());
-my $x = X11::XCB::Connection->new;
 
 sub check_order {
     my ($msg) = @_;
@@ -19,7 +13,7 @@ sub check_order {
     my @nums = map { $_->{num} } grep { defined($_->{num}) } @ws;
     my @sorted = sort @nums;
 
-    cmp_deeply(\@nums, \@sorted, $msg);
+    is_deeply(\@nums, \@sorted, $msg);
 }
 
 check_order('workspace order alright before testing');
@@ -30,7 +24,7 @@ check_order('workspace order alright before testing');
 
 cmd "workspace 93";
 
-open_window($x);
+open_window;
 
 my @ws = @{$i3->get_workspaces->recv};
 my @f = grep { defined($_->{num}) && $_->{num} == 93 } @ws;
@@ -38,23 +32,23 @@ is(@f, 1, 'ws 93 found by num');
 check_order('workspace order alright after opening 93');
 
 cmd "workspace 92";
-open_window($x);
+open_window;
 check_order('workspace order alright after opening 92');
 
 cmd "workspace 94";
-open_window($x);
+open_window;
 check_order('workspace order alright after opening 94');
 
 cmd "workspace 96";
-open_window($x);
+open_window;
 check_order('workspace order alright after opening 96');
 
 cmd "workspace foo";
-open_window($x);
+open_window;
 check_order('workspace order alright after opening foo');
 
 cmd "workspace 91";
-open_window($x);
+open_window;
 check_order('workspace order alright after opening 91');
 
 done_testing;
index fb77f01e83b75ebefb943da352e4cfde2af09162..3d78b1bd566a9ee022977e80bee5890cb67dfd00 100644 (file)
@@ -3,14 +3,8 @@
 # Regression: Check if the focus stays the same when switching the layout
 # bug introduced by 77d0d42ed2d7ac8cafe267c92b35a81c1b9491eb
 use i3test;
-use X11::XCB qw(:all);
-
-BEGIN {
-    use_ok('X11::XCB::Window');
-}
 
 my $i3 = i3(get_socket_path());
-my $x = X11::XCB::Connection->new;
 
 sub check_order {
     my ($msg) = @_;
@@ -19,16 +13,14 @@ sub check_order {
     my @nums = map { $_->{num} } grep { defined($_->{num}) } @ws;
     my @sorted = sort @nums;
 
-    cmp_deeply(\@nums, \@sorted, $msg);
+    is_deeply(\@nums, \@sorted, $msg);
 }
 
 my $tmp = fresh_workspace;
 
-my $left = open_window($x);
-my $mid = open_window($x);
-my $right = open_window($x);
-
-sync_with_i3($x);
+my $left = open_window;
+my $mid = open_window;
+my $right = open_window;
 
 diag("left = " . $left->id . ", mid = " . $mid->id . ", right = " . $right->id);
 
index e7424b5842908ff579bd6c9800560640eba9b231..c2038060a707c3fd55ae09ec951381a098cd651a 100644 (file)
@@ -2,22 +2,13 @@
 # vim:ts=4:sw=4:expandtab
 # Tests resizing tiling containers
 use i3test;
-use X11::XCB qw(:all);
-
-BEGIN {
-    use_ok('X11::XCB::Window');
-}
-
-my $x = X11::XCB::Connection->new;
 
 my $tmp = fresh_workspace;
 
 cmd 'split v';
 
-my $top = open_window($x);
-my $bottom = open_window($x);
-
-sync_with_i3($x);
+my $top = open_window;
+my $bottom = open_window;
 
 diag("top = " . $top->id . ", bottom = " . $bottom->id);
 
@@ -54,8 +45,8 @@ $tmp = fresh_workspace;
 
 cmd 'split v';
 
-$top = open_window($x);
-$bottom = open_window($x);
+$top = open_window;
+$bottom = open_window;
 
 cmd 'split h';
 cmd 'layout stacked';
@@ -103,7 +94,7 @@ is($nodes->[1]->{percent}, 0.75, 'right window got 75%');
 
 $tmp = fresh_workspace;
 
-$top = open_window($x);
+$top = open_window;
 
 cmd 'floating enable';
 
index de33eeb3961e76f581d08c9a7555ca937a81aea9..03318d7afbf366ea28d216c8c30a2df82f0a8b47 100644 (file)
@@ -11,23 +11,24 @@ use List::Util qw(sum);
 
 my $tmp = fresh_workspace;
 
-cmd 'exec /usr/bin/urxvt';
-sleep 0.5;
-cmd 'exec /usr/bin/urxvt';
-sleep 0.5;
+my $first = open_window;
+my $second = open_window;
+
 my ($nodes, $focus) = get_ws_content($tmp);
 my $old_sum = sum map { $_->{rect}->{width} } @{$nodes};
-#cmd 'open';
+
 cmd 'resize grow left 10 px or 25 ppt';
 cmd 'split v';
-#cmd 'open';
-cmd 'exec /usr/bin/urxvt';
-sleep 0.5;
+
+sync_with_i3;
+
+my $third = open_window;
+
 cmd 'mode toggle';
-sleep 0.5;
-cmd 'kill';
+sync_with_i3;
 
-sleep 0.5;
+cmd 'kill';
+sync_with_i3;
 
 ($nodes, $focus) = get_ws_content($tmp);
 my $new_sum = sum map { $_->{rect}->{width} } @{$nodes};
index 904252e74c16fcf503e15144892d308bdddfca95..9d22afc3d0f955c49e58096a0d7187bf39ca7932 100644 (file)
 #
 # This testcase checks that the tree is properly flattened after moving.
 #
-use X11::XCB qw(:all);
 use i3test;
 
-my $x = X11::XCB::Connection->new;
-
 my $tmp = fresh_workspace;
 
-my $left = open_window($x);
-my $mid = open_window($x);
-my $right = open_window($x);
+my $left = open_window;
+my $mid = open_window;
+my $right = open_window;
 
-cmd 'move before v';
-cmd 'move after h';
+cmd 'move up';
+cmd 'move right';
 my $ws = get_ws($tmp);
 
 is($ws->{orientation}, 'horizontal', 'workspace orientation is horizontal');
index bc1302bbe645d571107ea60719e0f636ba5fec39..ca209e1cadc1a799148aa00b4c4b9e8f0050896a 100644 (file)
@@ -1,22 +1,15 @@
 #!perl
 # vim:ts=4:sw=4:expandtab
 #
-use X11::XCB qw(:all);
 use i3test;
 
-BEGIN {
-    use_ok('X11::XCB::Window');
-}
-
-my $x = X11::XCB::Connection->new;
-
 my $tmp = fresh_workspace;
 
-my $left = open_window($x);
-my $mid = open_window($x);
+my $left = open_window;
+my $mid = open_window;
 
 cmd 'split v';
-my $bottom = open_window($x);
+my $bottom = open_window;
 
 my ($nodes, $focus) = get_ws_content($tmp);
 
@@ -25,7 +18,7 @@ my ($nodes, $focus) = get_ws_content($tmp);
 #############################################################################
 
 # Create a floating window
-my $window = open_floating_window($x);
+my $window = open_floating_window;
 ok($window->mapped, 'Window is mapped');
 
 ($nodes, $focus) = get_ws_content($tmp);
@@ -37,7 +30,7 @@ is(@{$nodes->[1]->{nodes}}, 2, 'two windows in split con');
 
 cmd 'floating toggle';
 
-my ($nodes, $focus) = get_ws_content($tmp);
+($nodes, $focus) = get_ws_content($tmp);
 
 is(@{$nodes->[1]->{nodes}}, 3, 'three windows in split con after floating toggle');
 
index 771ace320f59f6b1f7b9754b646ec63e41d4d15c..5a2c87d3f4c1e357fe900bc847121af56fa8c8eb 100644 (file)
@@ -4,40 +4,33 @@
 # Regression test for moving a con outside of a floating con when there are no
 # tiling cons on a workspace
 #
-use X11::XCB qw(:all);
-use Time::HiRes qw(sleep);
 use i3test;
 
-BEGIN {
-    use_ok('X11::XCB::Window');
+sub sync_cmd {
+    cmd @_;
+    sync_with_i3;
 }
 
-my $x = X11::XCB::Connection->new;
-
 my $tmp = fresh_workspace;
 
-my $left = open_window($x);
-my $mid = open_window($x);
-my $right = open_window($x);
+my $left = open_window;
+my $mid = open_window;
+my $right = open_window;
 
 # go to workspace level
-cmd 'level up';
-sleep 0.25;
+sync_cmd 'level up';
 
 # make it floating
-cmd 'mode toggle';
-sleep 0.25;
+sync_cmd 'mode toggle';
 
 # move the con outside the floating con
-cmd 'move before v';
-sleep 0.25;
+sync_cmd 'move up';
 
 does_i3_live;
 
 # move another con outside
-cmd '[id="' . $mid->id . '"] focus';
-cmd 'move before v';
-sleep 0.25;
+sync_cmd '[id="' . $mid->id . '"] focus';
+sync_cmd 'move up';
 
 does_i3_live;
 
index 6f9dfc40d47a36ceb2de6b888ec01a8fd68e8d70..3d71b5003387355344c443f2def994da406644de 100644 (file)
@@ -4,33 +4,24 @@
 # Regression test for correct focus behaviour when moving a floating con to
 # another workspace.
 #
-use X11::XCB qw(:all);
 use i3test;
 
-BEGIN {
-    use_ok('X11::XCB::Window');
-}
-
-my $x = X11::XCB::Connection->new;
-
 my $tmp = fresh_workspace;
 
 # open a tiling window on the first workspace
-open_window($x);
-#sleep 0.25;
+open_window;
 my $first = get_focused($tmp);
 
 # on a different ws, open a floating window
 my $otmp = fresh_workspace;
-open_window($x);
-#sleep 0.25;
+open_window;
 my $float = get_focused($otmp);
 cmd 'mode toggle';
-#sleep 0.25;
+sync_with_i3;
 
 # move the floating con to first workspace
 cmd "move workspace $tmp";
-#sleep 0.25;
+sync_with_i3;
 
 # switch to the first ws and check focus
 is(get_focused($tmp), $float, 'floating client correctly focused');
index e294e6bd3de9fe70dc312f72bf19011577adfcc4..7e5a5520147ad643a03f45723231f4a87f0a701d 100644 (file)
@@ -3,15 +3,8 @@
 #
 # Regression test for inplace restarting with dock clients
 #
-use X11::XCB qw(:all);
 use i3test;
 
-BEGIN {
-    use_ok('X11::XCB::Window');
-}
-
-my $x = X11::XCB::Connection->new;
-
 my $tmp = fresh_workspace;
 
 #####################################################################
@@ -25,7 +18,7 @@ is(@docked, 0, 'no dock clients yet');
 
 # open a dock client
 
-my $window = open_window($x, {
+my $window = open_window({
         background_color => '#FF0000',
         window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
     });
@@ -62,7 +55,7 @@ is($docknode->{rect}->{height}, 30, 'dock node has unchanged height after restar
 
 $window->destroy;
 
-wait_for_unmap $x;
+wait_for_unmap $window;
 
 @docked = get_dock_clients;
 is(@docked, 0, 'no dock clients found');
@@ -71,7 +64,7 @@ is(@docked, 0, 'no dock clients found');
 # create a dock client with a 1px border
 #####################################################################
 
-$window = open_window($x, {
+$window = open_window({
         border => 1,
         rect => [ 0, 0, 30, 20 ],
         background_color => '#00FF00',
index db0b6e9c63649bab76d70e31bc45b42dee62b9f2..83f3e85d58bf7cac5d9fe823d0c6ed07b57bda1c 100644 (file)
@@ -3,15 +3,12 @@
 #
 # Test if the requested width/height is set after making the window floating.
 #
-use X11::XCB qw(:all);
 use i3test;
 
 my $tmp = fresh_workspace;
 
-my $x = X11::XCB::Connection->new;
-
 # Create a floating window which is smaller than the minimum enforced size of i3
-my $window = open_window($x, { rect => [ 0, 0, 400, 150 ] });
+my $window = open_window({ rect => [ 0, 0, 400, 150 ] });
 
 my ($absolute, $top) = $window->rect;
 
@@ -20,7 +17,7 @@ cmp_ok($absolute->{width}, '>', 400, 'i3 raised the width');
 cmp_ok($absolute->{height}, '>', 150, 'i3 raised the height');
 
 cmd 'floating toggle';
-sync_with_i3($x);
+sync_with_i3;
 
 ($absolute, $top) = $window->rect;
 
index 8b7f456cd3bd9bc22cd2ff478e08651019e77a8c..76577fb36bb3b7ad6148338a7449098eecf23838 100644 (file)
@@ -3,15 +3,8 @@
 #
 # Regression test for closing one of multiple dock clients
 #
-use X11::XCB qw(:all);
 use i3test;
 
-BEGIN {
-    use_ok('X11::XCB::Window');
-}
-
-my $x = X11::XCB::Connection->new;
-
 my $tmp = fresh_workspace;
 
 #####################################################################
@@ -27,7 +20,7 @@ is(@docked, 0, 'no dock clients yet');
 # open a dock client
 #####################################################################
 
-my $first = open_window($x, {
+my $first = open_window({
         background_color => '#FF0000',
         window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
     });
@@ -36,7 +29,7 @@ my $first = open_window($x, {
 # Open a second dock client
 #####################################################################
 
-my $second = open_window($x, {
+my $second = open_window({
         background_color => '#FF0000',
         window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
     });
index 5de05e8b2dceb44fb55f9becef9f9d8ab7df71d0..76c31af6b0d1a37a0f1c02ac8ee7f5220b129711 100644 (file)
@@ -4,22 +4,15 @@
 # Test to see if i3 combines the geometry of all children in a split container
 # when setting the split container to floating
 #
-use X11::XCB qw(:all);
 use i3test;
 
-BEGIN {
-    use_ok('X11::XCB::Window');
-}
-
-my $x = X11::XCB::Connection->new;
-
 my $tmp = fresh_workspace;
 
 #####################################################################
 # open a window with 200x80
 #####################################################################
 
-my $first = open_window($x, {
+my $first = open_window({
         rect => [ 0, 0, 200, 80],
         background_color => '#FF0000',
     });
@@ -28,7 +21,7 @@ my $first = open_window($x, {
 # Open a second window with 300x90
 #####################################################################
 
-my $second = open_window($x, {
+my $second = open_window({
         rect => [ 0, 0, 300, 90],
         background_color => '#00FF00',
     });
index a559b5a5134cb81c4238924167d7bc760797b9d3..c7646e417e7c760c11fdf68c717ea41ced67ee65 100644 (file)
@@ -4,14 +4,8 @@
 # Test if new containers get focused when there is a fullscreen container at
 # the time of launching the new one.
 #
-use X11::XCB qw(:all);
 use i3test;
 
-BEGIN {
-    use_ok('X11::XCB::Window');
-}
-
-my $x = X11::XCB::Connection->new;
 my $i3 = i3(get_socket_path());
 
 my $tmp = fresh_workspace;
@@ -20,7 +14,7 @@ my $tmp = fresh_workspace;
 # open the left window
 #####################################################################
 
-my $left = open_window($x, { background_color => '#ff0000' });
+my $left = open_window({ background_color => '#ff0000' });
 
 is($x->input_focus, $left->id, 'left window focused');
 
@@ -30,7 +24,7 @@ diag("left = " . $left->id);
 # Open the right window
 #####################################################################
 
-my $right = open_window($x, { background_color => '#00ff00' });
+my $right = open_window({ background_color => '#00ff00' });
 
 diag("right = " . $right->id);
 
@@ -44,7 +38,7 @@ cmd 'fullscreen';
 # Open a third window
 #####################################################################
 
-my $third = open_window($x, {
+my $third = open_window({
         background_color => '#0000ff',
         name => 'Third window',
         dont_map => 1,
@@ -52,7 +46,7 @@ my $third = open_window($x, {
 
 $third->map;
 
-sync_with_i3 $x;
+sync_with_i3;
 
 diag("third = " . $third->id);
 
@@ -63,9 +57,6 @@ my $tmp2 = get_unused_workspace;
 cmd "move workspace $tmp2";
 
 # verify that the third window has the focus
-
-sync_with_i3($x);
-
 is($x->input_focus, $third->id, 'third window focused');
 
 done_testing;
index 7a101dbc8cf0dd777602262283ac098a01ed80fe..a861117ccb94372c37a7184fde31ecebf5d3cc91 100644 (file)
@@ -3,22 +3,15 @@
 #
 # Regression test: level up should be a noop during fullscreen mode
 #
-use X11::XCB qw(:all);
 use i3test;
 
-BEGIN {
-    use_ok('X11::XCB::Window');
-}
-
-my $x = X11::XCB::Connection->new;
-
 my $tmp = fresh_workspace;
 
 #####################################################################
 # open a window, verify it’s not in fullscreen mode
 #####################################################################
 
-my $win = open_window($x);
+my $win = open_window;
 
 my $nodes = get_ws_content $tmp;
 is(@$nodes, 1, 'exactly one client');
@@ -31,7 +24,7 @@ is($nodes->[0]->{fullscreen_mode}, 0, 'client not fullscreen');
 cmd 'nop making fullscreen';
 cmd 'fullscreen';
 
-my $nodes = get_ws_content $tmp;
+$nodes = get_ws_content $tmp;
 is($nodes->[0]->{fullscreen_mode}, 1, 'client fullscreen now');
 
 #####################################################################
@@ -40,7 +33,7 @@ is($nodes->[0]->{fullscreen_mode}, 1, 'client fullscreen now');
 cmd 'level up';
 cmd 'fullscreen';
 
-my $nodes = get_ws_content $tmp;
+$nodes = get_ws_content $tmp;
 is($nodes->[0]->{fullscreen_mode}, 0, 'client not fullscreen any longer');
 
 does_i3_live;
index a90ce1c3d04641cb6e18a8167cad7a7b4931f13e..94476bbd857d3cd1d08d86c92eecdf02dfaed882 100644 (file)
@@ -3,18 +3,14 @@
 #
 # Tests if the WM_TAKE_FOCUS protocol is correctly handled by i3
 #
-use X11::XCB qw(:all);
 use i3test;
-use v5.10;
-
-my $x = X11::XCB::Connection->new;
 
 subtest 'Window without WM_TAKE_FOCUS', sub {
     fresh_workspace;
 
-    my $window = open_window($x);
+    my $window = open_window;
 
-    ok(!wait_for_event($x, 1, sub { $_[0]->{response_type} == 161 }), 'did not receive ClientMessage');
+    ok(!wait_for_event(1, sub { $_[0]->{response_type} == 161 }), 'did not receive ClientMessage');
 
     done_testing;
 };
@@ -24,14 +20,14 @@ subtest 'Window with WM_TAKE_FOCUS', sub {
 
     my $take_focus = $x->atom(name => 'WM_TAKE_FOCUS');
 
-    my $window = open_window($x, {
+    my $window = open_window({
         dont_map => 1,
         protocols => [ $take_focus ],
     });
 
     $window->map;
 
-    ok(wait_for_event($x, 1, sub {
+    ok(wait_for_event(1, sub {
         return 0 unless $_[0]->{response_type} == 161;
         my ($data, $time) = unpack("L2", $_[0]->{data});
         return ($data == $take_focus->id);
index c5e3ef8080c2cd2224cee9815d931f0a98e71dab..6e1f64f08413af5d42edbfd25286bd2d0de150fe 100644 (file)
@@ -5,14 +5,12 @@
 # restart.
 # found in eb8ad348b28e243cba1972e802ca8ee636472fc9
 #
-use X11::XCB qw(:all);
 use List::Util qw(first);
 use i3test;
 
-my $x = X11::XCB::Connection->new;
 my $i3 = i3(get_socket_path());
 my $tmp = fresh_workspace;
-my $window = open_window($x);
+my $window = open_window;
 
 sub get_border_style {
     my @content = @{get_ws_content($tmp)};
index 5fb88129ea3c6e320e1f1916f603809680a9641d..3562ba7a2708b9b537d6b6d40c4a03afc1b71628 100644 (file)
@@ -4,14 +4,8 @@
 # Regression test for setting the urgent hint on dock clients.
 # found in 4be3178d4d360c2996217d811e61161c84d25898
 #
-use X11::XCB qw(:all);
 use i3test;
 
-BEGIN {
-    use_ok('X11::XCB::Window');
-}
-
-my $x = X11::XCB::Connection->new;
 my $i3 = i3(get_socket_path());
 
 my $tmp = fresh_workspace;
@@ -27,17 +21,10 @@ is(@docked, 0, 'no dock clients yet');
 
 # open a dock client
 
-my $window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30],
-    background_color => '#FF0000',
+my $window = open_window(
     window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
 );
 
-$window->map;
-
-sleep 0.25;
-
 #####################################################################
 # check that we can find it in the layout tree at the expected position
 #####################################################################
@@ -52,7 +39,7 @@ is($docknode->{rect}->{height}, 30, 'dock node has unchanged height');
 
 $window->add_hint('urgency');
 
-sync_with_i3($x);
+sync_with_i3;
 
 does_i3_live;
 
index e55d86824de934d97cab3fb0b21f5773618c3e0d..6df2bcbddf0392a5c7d71dec0da1a158818d01f9 100644 (file)
@@ -4,20 +4,16 @@
 # Tests if WM_STATE is WM_STATE_NORMAL when mapped and WM_STATE_WITHDRAWN when
 # unmapped.
 #
-use X11::XCB qw(:all);
 use i3test;
+use X11::XCB qw(ICCCM_WM_STATE_NORMAL ICCCM_WM_STATE_WITHDRAWN);
 
-my $x = X11::XCB::Connection->new;
-
-my $window = open_window($x);
-
-sync_with_i3($x);
+my $window = open_window;
 
 is($window->state, ICCCM_WM_STATE_NORMAL, 'WM_STATE normal');
 
 $window->unmap;
 
-wait_for_unmap $x;
+wait_for_unmap $window;
 
 is($window->state, ICCCM_WM_STATE_WITHDRAWN, 'WM_STATE withdrawn');
 
index ef45a789923e94556bbbffd8b10aab0d6a3c693a..165a32cec0f90438e31d4d6472104534af55cade 100644 (file)
@@ -6,17 +6,13 @@
 #
 use i3test;
 
-my $x = X11::XCB::Connection->new;
-
 sub two_windows {
     my $tmp = fresh_workspace;
 
     ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
 
-    my $first = open_window($x);
-    my $second = open_window($x);
-
-    sync_with_i3 $x;
+    my $first = open_window;
+    my $second = open_window;
 
     is($x->input_focus, $second->id, 'second window focused');
     ok(@{get_ws_content($tmp)} == 2, 'two containers opened');
@@ -42,7 +38,7 @@ ok(@{get_ws_content($tmp)} == 1, 'one container left after killing');
 # 'kill window'
 ##############################################################
 
-my $tmp = two_windows;
+$tmp = two_windows;
 
 cmd 'kill window';
 
@@ -55,7 +51,7 @@ ok(@{get_ws_content($tmp)} == 1, 'one container left after killing');
 # and check if both are gone
 ##############################################################
 
-my $tmp = two_windows;
+$tmp = two_windows;
 
 cmd 'kill client';
 
index cc100132f8c76723bab38ae14ab42ee17f5fa37d..0ca5bd112adf09cea7133f861beb0a6443564916 100644 (file)
@@ -3,11 +3,8 @@
 # !NO_I3_INSTANCE! will prevent complete-run.pl from starting i3
 #
 #
-use X11::XCB qw(:all);
-use X11::XCB::Connection;
 use i3test;
-
-my $x = X11::XCB::Connection->new;
+use X11::XCB qw(PROP_MODE_REPLACE);
 
 ##############################################################
 # 1: test the following directive:
@@ -28,36 +25,19 @@ my $pid = launch_with_config($config);
 
 my $tmp = fresh_workspace;
 
-my $window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30 ],
-    background_color => '#00ff00',
-    event_mask => [ 'structure_notify' ],
-);
-
-$window->name('Border window');
-$window->map;
-wait_for_map $x;
+my $window = open_window(name => 'Border window');
 
 my @content = @{get_ws_content($tmp)};
 cmp_ok(@content, '==', 1, 'one node on this workspace now');
 is($content[0]->{border}, 'normal', 'normal border');
 
 $window->unmap;
-wait_for_unmap $x;
+wait_for_unmap $window;
 
-my @content = @{get_ws_content($tmp)};
+@content = @{get_ws_content($tmp)};
 cmp_ok(@content, '==', 0, 'no more nodes');
 diag('content = '. Dumper(\@content));
 
-$window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30 ],
-    background_color => '#00ff00',
-    event_mask => [ 'structure_notify' ],
-);
-
-$window->_create;
 
 # TODO: move this to X11::XCB::Window
 sub set_wm_class {
@@ -78,17 +58,17 @@ sub set_wm_class {
     );
 }
 
-set_wm_class($window->id, 'borderless', 'borderless');
-$window->name('Borderless window');
-$window->map;
-wait_for_map $x;
+$window = open_window(
+    name => 'Borderless window',
+    before_map => sub { set_wm_class($_->id, 'borderless', 'borderless') },
+);
 
 @content = @{get_ws_content($tmp)};
 cmp_ok(@content, '==', 1, 'one node on this workspace now');
 is($content[0]->{border}, 'none', 'no border');
 
 $window->unmap;
-wait_for_unmap $x;
+wait_for_unmap $window;
 
 @content = @{get_ws_content($tmp)};
 cmp_ok(@content, '==', 0, 'no more nodes');
@@ -111,29 +91,20 @@ $pid = launch_with_config($config);
 
 $tmp = fresh_workspace;
 
-$window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30 ],
-    background_color => '#00ff00',
-    event_mask => [ 'structure_notify' ],
-);
-
-$window->name('special title');
-$window->map;
-wait_for_map $x;
+$window = open_window(name => 'special title');
 
 @content = @{get_ws_content($tmp)};
 cmp_ok(@content, '==', 1, 'one node on this workspace now');
 is($content[0]->{border}, 'normal', 'normal border');
 
 $window->name('special borderless title');
-sync_with_i3 $x;
+sync_with_i3;
 
 @content = @{get_ws_content($tmp)};
 is($content[0]->{border}, 'none', 'no border');
 
 $window->name('special title');
-sync_with_i3 $x;
+sync_with_i3;
 
 cmd 'border normal';
 
@@ -141,13 +112,13 @@ cmd 'border normal';
 is($content[0]->{border}, 'normal', 'border reset to normal');
 
 $window->name('special borderless title');
-sync_with_i3 $x;
+sync_with_i3;
 
 @content = @{get_ws_content($tmp)};
 is($content[0]->{border}, 'normal', 'still normal border');
 
 $window->unmap;
-wait_for_unmap $x;
+wait_for_unmap $window;
 
 @content = @{get_ws_content($tmp)};
 cmp_ok(@content, '==', 0, 'no more nodes');
@@ -171,22 +142,13 @@ $pid = launch_with_config($config);
 
 $tmp = fresh_workspace;
 
-$window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30 ],
-    background_color => '#00ff00',
-    event_mask => [ 'structure_notify' ],
-);
-
-$window->name('special mark title');
-$window->map;
-wait_for_map $x;
+$window = open_window(name => 'special mark title');
 
 @content = @{get_ws_content($tmp)};
 cmp_ok(@content, '==', 1, 'one node on this workspace now');
 is($content[0]->{border}, 'none', 'no border');
 
-my $other = open_window($x);
+my $other = open_window;
 
 @content = @{get_ws_content($tmp)};
 cmp_ok(@content, '==', 2, 'two nodes');
@@ -215,28 +177,22 @@ $pid = launch_with_config($config);
 
 $tmp = fresh_workspace;
 
-$window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30 ],
-    background_color => '#00ff00',
-    event_mask => [ 'structure_notify' ],
+$window = open_window(
+    name => 'usethis',
+    before_map => sub { set_wm_class($_->id, 'borderless', 'borderless') },
 );
 
-$window->_create;
-
-set_wm_class($window->id, 'borderless', 'borderless');
-$window->name('usethis');
-$window->map;
-wait_for_map $x;
-
 @content = @{get_ws_content($tmp)};
 cmp_ok(@content, '==', 1, 'one node on this workspace now');
 is($content[0]->{border}, 'none', 'no border');
 
 cmd 'kill';
-wait_for_unmap $x;
+wait_for_unmap $window;
 $window->destroy;
 
+# give i3 a chance to delete the window from its tree
+sync_with_i3;
+
 @content = @{get_ws_content($tmp)};
 cmp_ok(@content, '==', 0, 'no nodes on this workspace now');
 
@@ -245,7 +201,7 @@ $window->_create;
 set_wm_class($window->id, 'borderless', 'borderless');
 $window->name('notthis');
 $window->map;
-wait_for_map $x;
+wait_for_map $window;
 
 @content = @{get_ws_content($tmp)};
 cmp_ok(@content, '==', 1, 'one node on this workspace now');
@@ -268,19 +224,11 @@ $pid = launch_with_config($config);
 
 $tmp = fresh_workspace;
 
-$window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30 ],
-    background_color => '#00ff00',
-    event_mask => [ 'structure_notify' ],
-);
-
-$window->_create;
 
-set_wm_class($window->id, 'bar', 'foo');
-$window->name('usethis');
-$window->map;
-wait_for_map $x;
+$window = open_window(
+    name => 'usethis',
+    before_map => sub { set_wm_class($_->id, 'bar', 'foo') },
+);
 
 @content = @{get_ws_content($tmp)};
 cmp_ok(@content, '==', 1, 'one node on this workspace now');
@@ -303,20 +251,11 @@ $pid = launch_with_config($config);
 
 $tmp = fresh_workspace;
 
-$window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30 ],
-    background_color => '#00ff00',
-    event_mask => [ 'structure_notify' ],
+$window = open_window(
+    name => 'usethis',
+    before_map => sub { set_wm_class($_->id, 'bar', 'foo') },
 );
 
-$window->_create;
-
-set_wm_class($window->id, 'bar', 'foo');
-$window->name('usethis');
-$window->map;
-wait_for_map $x;
-
 @content = @{get_ws_content($tmp)};
 cmp_ok(@content, '==', 1, 'one node on this workspace now');
 is($content[0]->{border}, 'none', 'no border');
@@ -340,20 +279,11 @@ $pid = launch_with_config($config);
 
 $tmp = fresh_workspace;
 
-$window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30 ],
-    background_color => '#00ff00',
-    event_mask => [ 'structure_notify' ],
+$window = open_window(
+    name => 'usethis',
+    before_map => sub { set_wm_class($_->id, 'bar', 'foo') },
 );
 
-$window->_create;
-
-set_wm_class($window->id, 'bar', 'foo');
-$window->name('usethis');
-$window->map;
-wait_for_map $x;
-
 @content = @{get_ws_content($tmp)};
 cmp_ok(@content, '==', 1, 'one node on this workspace now');
 is($content[0]->{border}, 'normal', 'normal border');
@@ -377,31 +307,24 @@ $pid = launch_with_config($config);
 
 $tmp = fresh_workspace;
 
-$window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30 ],
-    background_color => '#00ff00',
-    event_mask => [ 'structure_notify' ],
-);
-
-$window->_create;
-
-my $atomname = $x->atom(name => 'WM_WINDOW_ROLE');
-my $atomtype = $x->atom(name => 'STRING');
-$x->change_property(
-  PROP_MODE_REPLACE,
-  $window->id,
-  $atomname->id,
-  $atomtype->id,
-  8,
-  length("i3test") + 1,
-  "i3test\x00"
+$window = open_window(
+    name => 'usethis',
+    before_map => sub {
+        my ($window) = @_;
+        my $atomname = $x->atom(name => 'WM_WINDOW_ROLE');
+        my $atomtype = $x->atom(name => 'STRING');
+        $x->change_property(
+            PROP_MODE_REPLACE,
+            $window->id,
+            $atomname->id,
+            $atomtype->id,
+            8,
+            length("i3test") + 1,
+            "i3test\x00"
+        );
+    },
 );
 
-$window->name('usethis');
-$window->map;
-wait_for_map $x;
-
 @content = @{get_ws_content($tmp)};
 cmp_ok(@content, '==', 1, 'one node on this workspace now');
 is($content[0]->{border}, 'none', 'no border (window_role)');
@@ -426,25 +349,14 @@ $pid = launch_with_config($config);
 
 $tmp = fresh_workspace;
 
-$window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30 ],
-    background_color => '#00ff00',
-    event_mask => [ 'structure_notify' ],
-);
-
-$window->_create;
-
-$window->name('usethis');
-$window->map;
-wait_for_map $x;
+$window = open_window(name => 'usethis');
 
 @content = @{get_ws_content($tmp)};
 cmp_ok(@content, '==', 1, 'one node on this workspace now');
 is($content[0]->{border}, 'normal', 'normal border (window_role 2)');
 
-$atomname = $x->atom(name => 'WM_WINDOW_ROLE');
-$atomtype = $x->atom(name => 'STRING');
+my $atomname = $x->atom(name => 'WM_WINDOW_ROLE');
+my $atomtype = $x->atom(name => 'STRING');
 $x->change_property(
   PROP_MODE_REPLACE,
   $window->id,
@@ -457,7 +369,7 @@ $x->change_property(
 
 $x->flush;
 
-sync_with_i3 $x;
+sync_with_i3;
 
 @content = @{get_ws_content($tmp)};
 cmp_ok(@content, '==', 1, 'one node on this workspace now');
index 4844f5b547e28508df13c5ca29d677ba771b5949..254616c5bc26130340c63930aec4e869004dff8a 100644 (file)
@@ -5,11 +5,7 @@
 # Tests if assignments work
 #
 use i3test;
-use X11::XCB qw(:all);
-use X11::XCB::Connection;
-use v5.10;
-
-my $x = X11::XCB::Connection->new;
+use X11::XCB qw(PROP_MODE_REPLACE);
 
 # TODO: move to X11::XCB
 sub set_wm_class {
@@ -30,6 +26,16 @@ sub set_wm_class {
     );
 }
 
+sub open_special {
+    my %args = @_;
+    my $wm_class = delete($args{wm_class}) || 'special';
+    $args{name} //= 'special window';
+
+    return open_window(
+        %args,
+        before_map => sub { set_wm_class($_->id, $wm_class, $wm_class) },
+    );
+}
 
 #####################################################################
 # start a window and see that it does not get assigned with an empty config
@@ -46,18 +52,7 @@ my $tmp = fresh_workspace;
 
 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
 
-my $window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30 ],
-    background_color => '#0000ff',
-    event_mask => [ 'structure_notify' ],
-);
-
-$window->_create;
-set_wm_class($window->id, 'special', 'special');
-$window->name('special window');
-$window->map;
-wait_for_map $x;
+my $window = open_special;
 
 ok(@{get_ws_content($tmp)} == 1, 'special window got managed to current (random) workspace');
 
@@ -84,18 +79,7 @@ ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
 my $workspaces = get_workspace_names;
 ok(!("targetws" ~~ @{$workspaces}), 'targetws does not exist yet');
 
-my $window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30 ],
-    background_color => '#0000ff',
-    event_mask => [ 'structure_notify' ],
-);
-
-$window->_create;
-set_wm_class($window->id, 'special', 'special');
-$window->name('special window');
-$window->map;
-wait_for_map $x;
+$window = open_special;
 
 ok(@{get_ws_content($tmp)} == 0, 'still no containers');
 ok("targetws" ~~ @{get_workspace_names()}, 'targetws exists');
@@ -124,22 +108,13 @@ $tmp = fresh_workspace;
 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
 ok("targetws" ~~ @{get_workspace_names()}, 'targetws does not exist yet');
 
-my $window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30 ],
-    background_color => '#0000ff',
-    event_mask => [ 'structure_notify' ],
-);
-
-$window->_create;
-set_wm_class($window->id, 'special', 'special');
-$window->name('special window');
-$window->map;
 
 # We use sync_with_i3 instead of wait_for_map here because i3 will not actually
 # map the window -- it will be assigned to a different workspace and will only
 # be mapped once you switch to that workspace
-sync_with_i3 $x;
+$window = open_special(dont_map => 1);
+$window->map;
+sync_with_i3;
 
 ok(@{get_ws_content($tmp)} == 0, 'still no containers');
 ok(@{get_ws_content('targetws')} == 2, 'two containers on targetws');
@@ -162,21 +137,10 @@ $pid = launch_with_config($config);
 $tmp = fresh_workspace;
 
 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
-my $workspaces = get_workspace_names;
+$workspaces = get_workspace_names;
 ok(!("targetws" ~~ @{$workspaces}), 'targetws does not exist yet');
 
-my $window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30 ],
-    background_color => '#0000ff',
-    event_mask => [ 'structure_notify' ],
-);
-
-$window->_create;
-set_wm_class($window->id, 'special', 'special');
-$window->name('special window');
-$window->map;
-wait_for_map $x;
+$window = open_special;
 
 my $content = get_ws($tmp);
 ok(@{$content->{nodes}} == 0, 'no tiling cons');
@@ -203,23 +167,12 @@ $pid = launch_with_config($config);
 $tmp = fresh_workspace;
 
 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
-my $workspaces = get_workspace_names;
+$workspaces = get_workspace_names;
 ok(!("targetws" ~~ @{$workspaces}), 'targetws does not exist yet');
 
-my $window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30 ],
-    background_color => '#0000ff',
-    event_mask => [ 'structure_notify' ],
-);
-
-$window->_create;
-set_wm_class($window->id, 'SPEcial', 'SPEcial');
-$window->name('special window');
-$window->map;
-wait_for_map $x;
+$window = open_special(wm_class => 'SPEcial');
 
-my $content = get_ws($tmp);
+$content = get_ws($tmp);
 ok(@{$content->{nodes}} == 0, 'no tiling cons');
 ok(@{$content->{floating_nodes}} == 1, 'one floating con');
 
@@ -255,21 +208,11 @@ my @docked = get_dock_clients;
 # syntax
 is(@docked, 1, 'one dock client yet');
 
-my $window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30 ],
-    background_color => '#0000ff',
+$window = open_special(
     window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
-    event_mask => [ 'structure_notify' ],
 );
 
-$window->_create;
-set_wm_class($window->id, 'special', 'special');
-$window->name('special window');
-$window->map;
-wait_for_map $x;
-
-my $content = get_ws($tmp);
+$content = get_ws($tmp);
 ok(@{$content->{nodes}} == 0, 'no tiling cons');
 ok(@{$content->{floating_nodes}} == 0, 'one floating con');
 @docked = get_dock_clients;
@@ -281,6 +224,4 @@ does_i3_live;
 
 exit_gracefully($pid);
 
-sleep 0.25;
-
 done_testing;
index e4b18adfbd5bfe5f86b08b6c1b05b9b6c5b30551..aeb700bad0a1f945f9571f0e9553ae95d4a53b21 100644 (file)
@@ -6,10 +6,6 @@
 #
 
 use i3test;
-use X11::XCB qw(:all);
-use X11::XCB::Connection;
-
-my $x = X11::XCB::Connection->new;
 
 #####################################################################
 # 1: check that with an empty config, cons are place next to each
@@ -27,13 +23,12 @@ my $tmp = fresh_workspace;
 
 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
 
-my $first = open_window($x);
-my $second = open_window($x);
-
-sync_with_i3($x);
+my $first = open_window;
+my $second = open_window;
 
 is($x->input_focus, $second->id, 'second window focused');
-ok(@{get_ws_content($tmp)} == 2, 'two containers opened');
+my @content = @{get_ws_content($tmp)};
+ok(@content == 2, 'two containers opened');
 isnt($content[0]->{layout}, 'stacked', 'layout not stacked');
 isnt($content[1]->{layout}, 'stacked', 'layout not stacked');
 
@@ -56,13 +51,11 @@ $tmp = fresh_workspace;
 
 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
 
-$first = open_window($x);
-$second = open_window($x);
-
-sync_with_i3($x);
+$first = open_window;
+$second = open_window;
 
 is($x->input_focus, $second->id, 'second window focused');
-my @content = @{get_ws_content($tmp)};
+@content = @{get_ws_content($tmp)};
 ok(@content == 1, 'one con at workspace level');
 is($content[0]->{layout}, 'stacked', 'layout stacked');
 
@@ -72,8 +65,8 @@ is($content[0]->{layout}, 'stacked', 'layout stacked');
 #####################################################################
 
 cmd 'focus parent';
-my $right_top = open_window($x);
-my $right_bot = open_window($x);
+my $right_top = open_window;
+my $right_bot = open_window;
 
 @content = @{get_ws_content($tmp)};
 is(@content, 2, 'two cons at workspace level after focus parent');
index 1418b402d38bff307f2386b13f34254e643a3b5a..27812565f86026cca1000703cd603a7dce0d8e3b 100644 (file)
@@ -4,21 +4,17 @@
 # Verifies that i3 survives inplace restarts with fullscreen containers
 #
 use i3test;
-use X11::XCB qw(:all);
-use X11::XCB::Connection;
-
-my $x = X11::XCB::Connection->new;
 
 fresh_workspace;
 
-open_window($x);
-open_window($x);
+open_window;
+open_window;
 
 cmd 'layout stacking';
-sleep 1;
+sync_with_i3;
 
 cmd 'fullscreen';
-sleep 1;
+sync_with_i3;
 
 cmd 'restart';
 sleep 1;
index 8a990f23e96dc649880f306de312a2438aaf1f80..30458b00a2920503dfe3b1c70882f331a3d7a602 100644 (file)
@@ -5,10 +5,6 @@
 # Tests if the 'force_focus_wrapping' config directive works correctly.
 #
 use i3test;
-use X11::XCB qw(:all);
-use X11::XCB::Connection;
-
-my $x = X11::XCB::Connection->new;
 
 #####################################################################
 # 1: test the wrapping behaviour without force_focus_wrapping
@@ -25,13 +21,13 @@ my $tmp = fresh_workspace;
 
 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
 
-my $first = open_window($x);
-my $second = open_window($x);
+my $first = open_window;
+my $second = open_window;
 
 cmd 'layout tabbed';
 cmd 'focus parent';
 
-my $third = open_window($x);
+my $third = open_window;
 is($x->input_focus, $third->id, 'third window focused');
 
 cmd 'focus left';
@@ -66,15 +62,13 @@ $tmp = fresh_workspace;
 
 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
 
-$first = open_window($x);
-$second = open_window($x);
+$first = open_window;
+$second = open_window;
 
 cmd 'layout tabbed';
 cmd 'focus parent';
 
-$third = open_window($x);
-
-sync_with_i3($x);
+$third = open_window;
 
 is($x->input_focus, $third->id, 'third window focused');
 
index 4493bf8332f8e23226a312fbf725099619679765..34d987768bd0b2915e72a1d9c630028fb1578f75 100644 (file)
@@ -4,12 +4,8 @@
 #
 # checks if i3 starts up on workspace '1' or the first configured named workspace
 #
-use X11::XCB qw(:all);
-use X11::XCB::Connection;
 use i3test;
 
-my $x = X11::XCB::Connection->new;
-
 ##############################################################
 # 1: i3 should start with workspace '1'
 ##############################################################
@@ -22,7 +18,7 @@ EOT
 my $pid = launch_with_config($config);
 
 my @names = @{get_workspace_names()};
-cmp_deeply(\@names, [ '1' ], 'i3 starts on workspace 1 without any configuration');
+is_deeply(\@names, [ '1' ], 'i3 starts on workspace 1 without any configuration');
 
 exit_gracefully($pid);
 
@@ -39,8 +35,8 @@ EOT
 
 $pid = launch_with_config($config);
 
-my @names = @{get_workspace_names()};
-cmp_deeply(\@names, [ 'foobar' ], 'i3 starts on named workspace foobar');
+@names = @{get_workspace_names()};
+is_deeply(\@names, [ 'foobar' ], 'i3 starts on named workspace foobar');
 
 exit_gracefully($pid);
 
@@ -57,8 +53,8 @@ EOT
 
 $pid = launch_with_config($config);
 
-my @names = @{get_workspace_names()};
-cmp_deeply(\@names, [ 'foobar' ], 'i3 starts on named workspace foobar');
+@names = @{get_workspace_names()};
+is_deeply(\@names, [ 'foobar' ], 'i3 starts on named workspace foobar');
 
 exit_gracefully($pid);
 
index e74c233a6a3560858909c9631f3164ff35780b0b..75b6e7a8f0594c34e593cdbb73be277f4053b8c7 100644 (file)
@@ -16,7 +16,7 @@ sub get_marks {
 my $tmp = fresh_workspace;
 
 my $marks = get_marks();
-cmp_deeply($marks, [], 'no marks set so far');
+is_deeply($marks, [], 'no marks set so far');
 
 ##############################################################
 # 2: check that setting a mark is reflected in the get_marks reply
@@ -25,7 +25,7 @@ cmp_deeply($marks, [], 'no marks set so far');
 cmd 'open';
 cmd 'mark foo';
 
-cmp_deeply(get_marks(), [ 'foo' ], 'mark foo set');
+is_deeply(get_marks(), [ 'foo' ], 'mark foo set');
 
 ##############################################################
 # 3: check that the mark is gone after killing the container
@@ -33,7 +33,7 @@ cmp_deeply(get_marks(), [ 'foo' ], 'mark foo set');
 
 cmd 'kill';
 
-cmp_deeply(get_marks(), [ ], 'mark gone');
+is_deeply(get_marks(), [ ], 'mark gone');
 
 ##############################################################
 # 4: check that duplicate marks are included twice in the get_marks reply
@@ -45,6 +45,6 @@ cmd 'mark bar';
 cmd 'open';
 cmd 'mark bar';
 
-cmp_deeply(get_marks(), [ 'bar', 'bar' ], 'duplicate mark found twice');
+is_deeply(get_marks(), [ 'bar', 'bar' ], 'duplicate mark found twice');
 
 done_testing;
index 70414af398264f56752025861277a58bd3497549..cf3385ccb303b7218cf07efceeee26f919e28884 100644 (file)
@@ -6,11 +6,7 @@
 # assigned to an invisible workspace
 #
 use i3test;
-use X11::XCB qw(:all);
-use X11::XCB::Connection;
-use v5.10;
-
-my $x = X11::XCB::Connection->new;
+use X11::XCB qw(PROP_MODE_REPLACE);
 
 # TODO: move to X11::XCB
 sub set_wm_class {
@@ -31,6 +27,16 @@ sub set_wm_class {
     );
 }
 
+sub open_special {
+    my %args = @_;
+    my $wm_class = delete($args{wm_class}) || 'special';
+    $args{name} //= 'special window';
+
+    return open_window(
+        %args,
+        before_map => sub { set_wm_class($_->id, $wm_class, $wm_class) },
+    );
+}
 
 #####################################################################
 # start a window and see that it does not get assigned with an empty config
@@ -49,18 +55,7 @@ my $tmp = fresh_workspace;
 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
 ok(get_ws($tmp)->{focused}, 'current workspace focused');
 
-my $window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30 ],
-    background_color => '#0000ff',
-);
-
-$window->_create;
-set_wm_class($window->id, 'special', 'special');
-$window->name('special window');
-$window->map;
-sleep 0.25;
-
+my $window = open_special;
 
 ok(@{get_ws_content($tmp)} == 0, 'special window not on current workspace');
 ok(@{get_ws_content('targetws')} == 1, 'special window on targetws');
@@ -70,20 +65,10 @@ ok(get_ws($tmp)->{focused}, 'current workspace still focused');
 # the same test, but with a floating window
 #####################################################################
 
-$window = $x->root->create_child(
-    class => WINDOW_CLASS_INPUT_OUTPUT,
-    rect => [ 0, 0, 30, 30 ],
-    background_color => '#0000ff',
+$window = open_special(
     window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'),
 );
 
-$window->_create;
-set_wm_class($window->id, 'special', 'special');
-$window->name('special window');
-$window->map;
-sleep 0.25;
-
-
 ok(@{get_ws_content($tmp)} == 0, 'special window not on current workspace');
 ok(@{get_ws_content('targetws')} == 1, 'special window on targetws');
 ok(get_ws($tmp)->{focused}, 'current workspace still focused');
index cc1f5c90298137a0d3dc10928f0229c567061d9f..009f5094ef437597a429ba1333314751a3b70e1a 100644 (file)
@@ -7,8 +7,6 @@
 
 use i3test;
 
-my $x = X11::XCB::Connection->new;
-
 #####################################################################
 # 1: check that new windows start with 'normal' border unless configured
 # otherwise
@@ -25,7 +23,7 @@ my $tmp = fresh_workspace;
 
 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
 
-my $first = open_window($x);
+my $first = open_window;
 
 my @content = @{get_ws_content($tmp)};
 ok(@content == 1, 'one container opened');
@@ -51,7 +49,7 @@ $tmp = fresh_workspace;
 
 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
 
-$first = open_window($x);
+$first = open_window;
 
 @content = @{get_ws_content($tmp)};
 ok(@content == 1, 'one container opened');
@@ -75,7 +73,7 @@ $tmp = fresh_workspace;
 
 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
 
-$first = open_floating_window($x);
+$first = open_floating_window;
 
 my $wscontent = get_ws($tmp);
 my @floating = @{$wscontent->{floating_nodes}};
@@ -103,7 +101,7 @@ $tmp = fresh_workspace;
 
 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
 
-$first = open_floating_window($x);
+$first = open_floating_window;
 
 $wscontent = get_ws($tmp);
 @floating = @{$wscontent->{floating_nodes}};
index 55df41429f75705e7b144be60f2e81ce91a57857..3a4dbc81f3ed5d4a305f725acaab5cc516c3bacd 100644 (file)
@@ -8,7 +8,6 @@ use i3test;
 use POSIX qw(mkfifo);
 use File::Temp qw(:POSIX);
 
-my $x = X11::XCB::Connection->new;
 use ExtUtils::PkgConfig;
 
 # setup dependency on libstartup-notification using pkg-config
@@ -98,13 +97,13 @@ my $second_ws = fresh_workspace;
 
 is(@{get_ws_content($second_ws)}, 0, 'no containers on the second workspace yet');
 
-my $win = open_window($x, { dont_map => 1 });
+my $win = open_window({ dont_map => 1 });
 mark_window($win->id);
 $win->map;
 # We don’t use wait_for_map because the window will not get mapped -- it is on
 # a different workspace.
 # We sync with i3 here to make sure $x->input_focus is updated.
-sync_with_i3($x);
+sync_with_i3;
 
 is(@{get_ws_content($second_ws)}, 0, 'still no containers on the second workspace');
 is(@{get_ws_content($first_ws)}, 1, 'one container on the first workspace');
@@ -113,12 +112,12 @@ is(@{get_ws_content($first_ws)}, 1, 'one container on the first workspace');
 # same thing, but with _NET_STARTUP_ID set on the leader
 ######################################################################
 
-my $leader = open_window($x, { dont_map => 1 });
+my $leader = open_window({ dont_map => 1 });
 mark_window($leader->id);
 
-$win = open_window($x, { dont_map => 1, client_leader => $leader });
+$win = open_window({ dont_map => 1, client_leader => $leader });
 $win->map;
-sync_with_i3($x);
+sync_with_i3;
 
 is(@{get_ws_content($second_ws)}, 0, 'still no containers on the second workspace');
 is(@{get_ws_content($first_ws)}, 2, 'two containers on the first workspace');
@@ -129,9 +128,9 @@ is(@{get_ws_content($first_ws)}, 2, 'two containers on the first workspace');
 ######################################################################
 
 complete_startup();
-sync_with_i3($x);
+sync_with_i3;
 
-my $otherwin = open_window($x);
+my $otherwin = open_window;
 is(@{get_ws_content($second_ws)}, 1, 'one container on the second workspace');
 
 ######################################################################
index 48ea948ddba518f2276d7866eab5ca85417865f9..31c4f2484f3eb673753785c88a55c560945bb478 100644 (file)
@@ -7,9 +7,6 @@
 #
 
 use i3test;
-use X11::XCB::Connection;
-
-my $x = X11::XCB::Connection->new;
 
 my $config = <<EOT;
 # i3 config file (v4)
diff --git a/testcases/t/180-fd-leaks.t b/testcases/t/180-fd-leaks.t
new file mode 100644 (file)
index 0000000..487803c
--- /dev/null
@@ -0,0 +1,49 @@
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Verifies that i3 does not leak any file descriptors in 'exec'.
+#
+use i3test;
+use POSIX qw(mkfifo);
+use File::Temp qw(:POSIX tempfile);
+
+my $i3 = i3(get_socket_path());
+
+my $tmp = tmpnam();
+mkfifo($tmp, 0600) or die "Could not create FIFO in $tmp";
+my ($outfh, $outname) = tempfile('/tmp/i3-ls-output.XXXXXX', UNLINK => 1);
+
+cmd qq|exec ls -l /proc/self/fd >$outname && echo done >$tmp|;
+
+open(my $fh, '<', $tmp);
+# Block on the FIFO, this will return exactly when the command is done.
+<$fh>;
+close($fh);
+unlink($tmp);
+
+# Get the ls /proc/self/fd output
+my $output;
+{
+    local $/;
+    $output = <$outfh>;
+}
+close($outfh);
+
+# Split lines, keep only those which are symlinks.
+my @lines = grep { /->/ } split("\n", $output);
+
+my %fds = map { /([0-9]+) -> (.+)$/; ($1, $2) } @lines;
+
+# Filter out 0, 1, 2 (stdin, stdout, stderr).
+delete $fds{0};
+delete $fds{1};
+delete $fds{2};
+
+# Filter out the fd which is caused by ls calling readdir().
+for my $fd (keys %fds) {
+    delete $fds{$fd} if $fds{$fd} =~ m,^/proc/\d+/fd$,;
+}
+
+is(scalar keys %fds, 0, 'No file descriptors leaked');
+
+done_testing;
diff --git a/testcases/t/181-regress-float-border.t b/testcases/t/181-regress-float-border.t
new file mode 100644 (file)
index 0000000..f77f780
--- /dev/null
@@ -0,0 +1,26 @@
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Regression test: Changing border style should not have an impact on the size
+# (geometry) of the child window. See ticket http://bugs.i3wm.org/561
+# Wrong behaviour manifested itself up to (including) commit
+# d805d1bbeaf89e11f67c981f94c9f55bbb4b89d9
+#
+use i3test;
+use Data::Dumper;
+
+fresh_workspace;
+
+my $win = open_floating_window(rect => [10, 10, 200, 100]);
+
+my $geometry = $win->rect;
+is($geometry->{width}, 200, 'width correct');
+is($geometry->{height}, 100, 'height correct');
+
+cmd 'border 1pixel';
+
+$geometry = $win->rect;
+is($geometry->{width}, 200, 'width correct');
+is($geometry->{height}, 100, 'height correct');
+
+done_testing;