Merge branch 'next'

This commit is contained in:
Michael Stapelberg 2015-02-28 15:02:31 +01:00
commit fe482cf193
153 changed files with 4613 additions and 4169 deletions

View File

@ -7,5 +7,4 @@ AlwaysBreakBeforeMultilineStrings: false
IndentWidth: 4
PointerBindsToType: false
ColumnLimit: 0
ForEachMacros: [ TAILQ_FOREACH, TAILQ_FOREACH_REVERSE, SLIST_FOREACH, CIRCLEQ_FOREACH, CIRCLEQ_FOREACH_REVERSE, NODES_FOREACH, NODES_FOREACH_REVERSE ]
SpaceBeforeParens: ControlStatements

20
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,20 @@
# i3status/i3lock bugreports/feature requests
Note that i3status and i3lock related bugreports and feature requests should be
filed in the corresponding repositories, i.e. https://github.com/i3/i3status
and https://github.com/i3/i3lock
# i3 bugreports/feature requests
1. Read http://i3wm.org/docs/debugging.html
2. Make sure you include a link to your logfile in your report (section 3).
3. Make sure you include the i3 version number in your report (section 1).
# Pull requests
* Before sending a pull request for new features, please check with us that the
feature is something we want to see in i3 by opening an issue which has
“feature request” or “enhancement” in its title.
* Use the `next` branch for developing and sending your pull request.
* Use `clang-format` to format your code.
* Run the testsuite, see http://i3wm.org/docs/testsuite.html

22
DEPENDS
View File

@ -4,25 +4,27 @@
"min" means minimum required version
"lkgv" means last known good version
┌─────────────┬────────┬────────┬────────────────────────────────────────┐
┌─────────────┬────────┬────────┬────────────────────────────────────────┐
│ dependency │ min. │ lkgv │ URL │
├─────────────┼────────┼────────┼────────────────────────────────────────┤
├─────────────┼────────┼────────┼────────────────────────────────────────┤
│ pkg-config │ 0.25 │ 0.26 │ http://pkgconfig.freedesktop.org/ │
│ libxcb │ 1.1.93 │ 1.7 │ http://xcb.freedesktop.org/dist/ │
│ xcb-util │ 0.3.3 │ 0.3.8 │ http://xcb.freedesktop.org/dist/ │
│ util-cursor³│ 0.0.99 │ 0.0.99 │ http://xcb.freedesktop.org/dist/ │
│ libxcb │ 1.1.93 │ 1.10 │ http://xcb.freedesktop.org/dist/ │
│ xcb-util │ 0.3.3 │ 0.4.1 │ http://xcb.freedesktop.org/dist/ │
│ xkbcommon │ 0.4.0 │ 0.4.0 │ http://xkbcommon.org/ │
│ xkbcommon-x11│ 0.4.0 │ 0.4.0 │ http://xkbcommon.org/ │
│ util-cursor³ │ 0.0.99 │ 0.0.99 │ http://xcb.freedesktop.org/dist/ │
│ libev │ 4.0 │ 4.11 │ http://libev.schmorp.de/ │
│ yajl │ 2.0.1 │ 2.0.4 │ http://lloyd.github.com/yajl/ │
│ asciidoc │ 8.3.0 │ 8.6.4 │ http://www.methods.co.nz/asciidoc/ │
│ xmlto │ 0.0.23 │ 0.0.23 │ http://www.methods.co.nz/asciidoc/ │
│ Pod::Simple²│ 3.22 │ 3.22 │ http://search.cpan.org/~dwheeler/Pod-Simple-3.23/
│ Pod::Simple² │ 3.22 │ 3.22 │ http://search.cpan.org/~dwheeler/Pod-Simple-3.23/
│ docbook-xml │ 4.5 │ 4.5 │ http://www.methods.co.nz/asciidoc/ │
│ Xlib │ 1.3.3 │ 1.4.3 │ http://ftp.x.org/pub/current/src/lib/ │
│ PCRE │ 8.12 │ 8.12 │ http://www.pcre.org/ │
│ libsn¹ │ 0.10 │ 0.12 │ http://freedesktop.org/wiki/Software/startup-notification
│ pango │ 1.30.0 | 1.30.0 │ http://www.pango.org/ │
│ cairo │ 1.12.2 │ 1.12.2 │ http://cairographics.org/ │
└─────────────┴────────┴────────┴────────────────────────────────────────┘
└─────────────┴────────┴────────┴────────────────────────────────────────┘
¹ libsn = libstartup-notification
² Pod::Simple is a Perl module required for converting the testsuite
documentation to HTML. See http://michael.stapelberg.de/cpan/#Pod::Simple
@ -35,6 +37,6 @@
i3-migrate-config-to-v4 and i3-dmenu-desktop are implemented in Perl, but have
no dependencies besides Perl 5.10.
i3-save-tree is also implemented in Perl and needs AnyEvent::I3 and JSON::XS.
While i3-save-tree is not required for running i3 itself, it is strongly
recommended to provide it in distribution packages.
i3-save-tree is also implemented in Perl and needs AnyEvent::I3 (>= 0.12) and
JSON::XS. While i3-save-tree is not required for running i3 itself, it is
strongly recommended to provide it in distribution packages.

View File

@ -1,138 +0,0 @@
┌──────────────────────────────┐
│ Release notes for i3 v4.8 │
└──────────────────────────────┘
This is i3 v4.8. This version is considered stable. All users of i3 are
strongly encouraged to upgrade.
The biggest new feature certainly is layout saving/restoring. See
http://i3wm.org/docs/layout-saving.html for more details. tl;dr: export your
current layout as JSON file, load it into new i3 sessions, get placeholder
windows that will be replaced by the actual apps once you start them.
Also very important for owners of HiDPI/“retina” displays is that i3 will now
respect your configured DPI and scale up its UI elements accordingly. Use
“xrandr --dpi 184” to set your dpi to 184, in case your setup does not figure
it out automatically. To get properly scaling fonts, we also changed the
default font from a bitmap font to a pango font (“DejaVu Sans Mono 8”).
Multiple changes improve the compatibility of i3 with other software, e.g.
java-based software (focus handling, once again) or external pagers (we now
provide _NET_CLIENT_LIST and let pager applications change workspaces).
For packagers, another change is that yajl ≥ 2.0 is now required for compiling
i3. This should not be a problem for anyone, as that version is pretty old by
now.
For contributors, note that we have starting formatting the source code with
clang-format-3.5. This means that there will no longer be a need to argue about
coding style when discussing patches :).
┌────────────────────────────┐
│ Changes in v4.8 │
└────────────────────────────┘
• docs/ipc: reformat/update list of ipc libraries
• docs/ipc: fix current_workspace outputs reply member
• docs/ipc: update ipc COMMAND reply docs
• docs/userguide: fix multiple typos
• docs/debugging: use bzip2
• docs/debugging: explain how to enable logging on the fly
• docs/debugging: merge the debug symbols/backtrace section
• docs/debugging: recommend i3 --moreversion
• man/i3-nagbar.man: update manpage to document all options
• i3bar: Amend status line error 127 message
• i3bar: dont kill watcher on EOF, leads to better error messages
• i3bar: send mouse wheel events to child too
• i3bar: do click handling and tray padding retina-correctly
• i3bar: render separators render-correctly
• i3bar: reinit colors on barconfig update
• i3bar: Don't start child unless status_command
• i3bar: implement custom workspace numbers config
• resize floating windows when right-clicking the decoration
• enable shmlog when invoked as i3-with-shmlog
• Disable pointer warps when focus_follows_mouse is disabled
• Movement into a branch considers movement direction
• set ewmh desktop properties on startup
• handle ButtonPress events with child != XCB_NONE
• implement layout restoring
• only LOG() the DPI when it changes, DLOG() it otherwise
• send IPC window events for focus and title changes
• these types of windows are now floating by default:
dialog, utility, toolbar and splash windows, modal windows, windows with an
equal minimum and maximum size
• send last event timestamp with WM_TAKE_FOCUS message
• maintain the _NET_CLIENT_LIST property
• dont set input focus _and_ send WM_TAKE_FOCUS
• respect CFLAGS in linking command
• fix parallel make
• reset SIGPIPE handler before executing a command
• render default window border width retina-correctly
• draw workspace buttons and padded text blocks retina-correctly
• render resize windows retina-correctly
• delegate click handling to dock clients
• send complete config on barconfig_update
• implement the window::fullscreen_mode ipc event
• make all workspaces starting with "__" internal
• improve error messages for i3-internal workspace names
• allow _NET_ACTIVE_WINDOW requests to switch workspaces if they indicate
that they are a pager (following the spec)
• workspace assignments by number
• add configuration option for disabling mouse warping
• set _NET_ACTIVE_WINDOW to None when none has focus
• set X-LightDM-DesktopName in i3.xsession.desktop to fix autostart on Ubuntu
• dont ELOG ipc EOF
• replace all printf()s with D?LOG
• delete ipc socket when exiting, cleanup tmpdir
• default config: switch to DejaVu Sans Mono 8 as default font
• cleanup tmpdir when restarting and not using XDG_RUNTIME_DIR
• Snap pointer to resize bar on drag resize
• Size resizebar according to container size
• Fix clang -Wextra except -Wunused-parameter
• Respect Motif hint for window decorations
┌────────────────────────────┐
│ Bugfixes │
└────────────────────────────┘
• create con pixmaps when not needed
• i3bar: fix resource leak: statusline_ctx needs to be freed first
• tree_split should not split floating cons
• fix memory leak with ipc_receive_message
• fix invalid reads by setting con->window to NULL in tree_close
• fix memory leak when closing windows
• fix memory leak when matching window by criteria
• fix memory leak when matching window by con_id
• ignore dock clients in the resize command
• clear wm_size_hints if they are not set
• resize window check should check for NULL
• fix window event crash with no window
• i3-dmenu-desktop: also quote the %c field code
• new_window and new_float can now be used simultaneously with different
border widths
• fix crash when using multiple for_window statements that move windows
• Set input focus with last timestamp
• handle windows whose WM_TRANSIENT_FOR points to themselve
• dont overwrite the original size of floating windows when changing border
• dont errnously render floating fullscreen windows during restart
• ensure floating windows dont drop out of fullscreen when restarting
• dont overwrite the windows geometry after restartingnext
• i3bar: Set `mapped` flag on trayclient creation
• i3bar: don't show "EOF" status line error
┌────────────────────────────┐
│ Thanks! │
└────────────────────────────┘
Thanks for testing, bugfixes, discussions and everything I forgot go out to:
Aleksi Blinnikka, Alexander Berntsen, Alexander Kedrik, Antonio, Arun
Persaud, Atte Peltomaki, bo, Campbell Barton, chris, David Coppa, eeemsi,
Holger Langenau, Jean-Philippe Ouellet, Jens, jeroentbt, Jonas Maaskola,
Julian Ospald, Kernc, Koston, lasers, lkraav, Marcin, Marco Hunsicker,
Marcus Crestani, Matthias Thubauville, Maxime, Michael Stapelberg, Peter
Boström, Petr Písař, Quentin Glidic, Steve Jones, TonyC, Tony Crisci,
Vivien Didelot, Wieland Hoffmann, x33a, xeen
-- Michael Stapelberg, 2014-06-15

124
RELEASE-NOTES-4.9 Normal file
View File

@ -0,0 +1,124 @@
┌──────────────────────────────┐
│ Release notes for i3 v4.9 │
└──────────────────────────────┘
This is i3 v4.9. This version is considered stable. All users of i3 are
strongly encouraged to upgrade.
Notable new features include mouse button bindings and improved EWMH
compatibility, meaning more external pager programs work with i3 now.
Aside from that, this release contains plenty of bugfixes and little
enhancements.
The new dependency on libxkbcommon ≥ 0.4.0 is notable for distribution
packages. This dependency allowed us to drop our last direct dependency
on Xlib :).
Its also worth mentioning that all i3 repositories are now on GitHub, see
http://thread.gmane.org/gmane.comp.window-managers.i3.general/1666 for the
announcement.
┌────────────────────────────┐
│ Changes in v4.9 │
└────────────────────────────┘
• docs/ipc: use an actual event type
• docs/debugging: use logs.i3wm.org
• docs/testsuite: add hint to use xvfb-run
• testcases: use Xephyr instead of XDummy
• i3-sensible-*: use command -v (built-in) instead of which(1)
• i3.xsession.desktop: set DesktopNames (which gdm uses)
• i3-save-tree: interpret commandline parameters as utf-8
• i3-save-tree: add 'mark' as allowed key to i3-save-tree output
• i3bar-protocol: ensure align = left is the default
• i3bar: implement custom mouse wheel commands
• i3bar: improve error message when a full_text property is missing
• i3bar: respect the urgency flag on status blocks
• i3bar: inset the urgent background of a status block for consistency with
workspace buttons
• i3bar: suspend the child when bars are fully obscured
• i3bar: use Pango markup
• ipc: implement the window::close event
• ipc: implement the window::move event
• ipc: implement the window::floating event
• ipc: implement the window::urgent event
• ipc: set ws reply "num" member to -1 when named
• ipc: add deco_rect property to con in ipc response
• ipc: include workspace con in workspace event
• ewmh: implement property _NET_NUMBER_OF_DESKTOPS
• ewmh: implement property _NET_DESKTOP_VIEWPORT
• ewmh: implement property _NET_DESKTOP_NAMES
• ewmh: handle _NET_CURRENT_DESKTOP requests
• ewmh: handle _NET_CLOSE_WINDOW requests
• ewmh: handle _NET_WM_MOVERESIZE requests
• implement mouse bindings (e.g. bindsym button3 kill)
• add mouse binding --whole-window flag
• add mouse binding --release flag
• switch to xcb-xkb and libxkbcommon, removing our last direct Xlib dep
• make “move [direction]” work with criteria
• make “move <window|container> to <absolute> position” work with criteria
• “workspace <n>” and “move to workspace <n>” now look for a workspace
starting with number <n> (unless there is a workspace exactly matching that
number). I.e., “workspace 4” will go to a workspace called “4: www” unless
you have a workspace “4”
• “focus <direction>” now focuses floating containers when there are no
tiling containers on the destination output
• take the motif border into account when calculating floating window
geometry
• revert “Disable pointer warps when focus_follows_mouse is disabled” as it
was unexpected by a number of users. Sorry for the back-and-forth
• handle WM_CLASS changes
• raise floating windows on “focus <direction>”
• align lower line of bar decoration to border width
• parse tray_output as a word, not string
• allow to validate the config file without X
• do not resend focus on click, fixes compatibility problems with some wine
or mono apps (e.g. Office 2010)
• don't draw borders wider than actual width
• prevent workspace change during global fullscreen
• extend the fullscreen command (fullscreen <enable|toggle|disable> [global])
• fix start_application() doc about which shell is used
┌────────────────────────────┐
│ Bugfixes │
└────────────────────────────┘
• i3-dmenu-desktop: quote path
• i3bar: fix a double free when changing color configuration
• i3bar: render bars after the first chunk of JSON
• i3bar: add a sync call to confirm reparents before exiting (fixes tray
restart issues)
• i3bar: correctly calculate clicks on i3bar status blocks
• i3bar: make click events on status blocks work with 'workspace_buttons no'
• retina support: convert logical to physical pixels for default_border_width
• retina support: treat everything up to 120 dpi as 96 dpi
• dont set input focus if not accepted (fixes problems with xfce4-notifyd)
• dont focus unmapped container on manage
• create the directory for storing the restart state
• avoid changing border width when changing containers from tiling to
floating
• layout saving: properly restore workspace containers
• rerender the decoration when the container requires a pixmap and doesnt
have one
• dont set focus in con_set_layout() on invisible workspaces
• properly handle windows unsetting WM_TRANSIENT_FOR
• use the command parser to properly extract workspace names
• copy binding before run (fixes reloads)
• revert "Bugfix: Set input focus with last timestamp"
• render floating windows during global fullscreen
• actually parse client.placeholder
┌────────────────────────────┐
│ Thanks! │
└────────────────────────────┘
Thanks for testing, bugfixes, discussions and everything I forgot go out to:
Alexander Monakov, aszlig, cornerman, dmurph, Mats, dsargrad, hercek, hjem,
Ingo, Ingo Bürk, Janus, javier, jefvel, Lukas K, Marein Konings, Mats,
Michael Stapelberg, Mii, nikolaus, okraits, Peter, smlb, sur5r, Tony Crisci,
val, vals, xeen, Yves-Alexis
-- Michael Stapelberg, 2015-02-28

View File

@ -92,6 +92,7 @@ else
XCB_CFLAGS += $(call cflags_for_lib, xcb-util)
XCB_LIBS += $(call ldflags_for_lib, xcb-util)
endif
XCB_XKB_LIBS := $(call ldflags_for_lib, xcb-xkb,xcb-xkb)
# XCB keyboard stuff
XCB_KBD_CFLAGS := $(call cflags_for_lib, xcb-keysyms)
@ -105,9 +106,10 @@ XCB_WM_LIBS := $(call ldflags_for_lib, xcb-icccm,xcb-icccm)
XCB_WM_LIBS += $(call ldflags_for_lib, xcb-xinerama,xcb-xinerama)
XCB_WM_LIBS += $(call ldflags_for_lib, xcb-randr,xcb-randr)
# Xlib
X11_CFLAGS := $(call cflags_for_lib, x11)
X11_LIBS := $(call ldflags_for_lib, x11,X11)
XKB_COMMON_CFLAGS := $(call cflags_for_lib, xkbcommon,xkbcommon)
XKB_COMMON_LIBS := $(call ldflags_for_lib, xkbcommon,xkbcommon)
XKB_COMMON_X11_CFLAGS := $(call cflags_for_lib, xkbcommon-x11,xkbcommon-x11)
XKB_COMMON_X11_LIBS := $(call ldflags_for_lib, xkbcommon-x11,xkbcommon-x11)
# Xcursor
XCURSOR_CFLAGS := $(call cflags_for_lib, xcb-cursor)

19
debian/changelog vendored
View File

@ -1,8 +1,23 @@
i3-wm (4.7.3-1) unstable; urgency=low
i3-wm (4.8.1-1) unstable; urgency=medium
* NOT YET RELEASED
-- Michael Stapelberg <stapelberg@debian.org> Thu, 23 Jan 2014 23:11:48 +0100
-- Michael Stapelberg <stapelberg@debian.org> Sun, 15 Jun 2014 19:37:32 +0200
i3-wm (4.8-2) unstable; urgency=medium
* Backport two bugfixes:
- backport-dpi-fix.patch (Closes: #778460)
- backport-i3bar-tray-fix.patch (Closes: #778461)
-- Michael Stapelberg <stapelberg@debian.org> Sun, 15 Feb 2015 13:24:42 +0100
i3-wm (4.8-1) unstable; urgency=medium
* New upstream release.
* Bump standards-version to 3.9.5 (no changes necessary)
-- Michael Stapelberg <stapelberg@debian.org> Sun, 15 Jun 2014 19:15:29 +0200
i3-wm (4.7.2-1) unstable; urgency=low

7
debian/control vendored
View File

@ -10,6 +10,9 @@ Build-Depends: debhelper (>= 7.0.50~),
libxcb-randr0-dev,
libxcb-icccm4-dev,
libxcb-cursor-dev,
libxcb-xkb-dev,
libxkbcommon-dev (>= 0.4.0),
libxkbcommon-x11-dev (>= 0.4.0),
asciidoc (>= 8.4.4),
xmlto,
docbook-xml,
@ -21,7 +24,7 @@ Build-Depends: debhelper (>= 7.0.50~),
libcairo2-dev,
libpango1.0-dev,
libpod-simple-perl
Standards-Version: 3.9.4
Standards-Version: 3.9.5
Homepage: http://i3wm.org/
Package: i3
@ -38,7 +41,7 @@ Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, x11-utils
Provides: x-window-manager
Suggests: rxvt-unicode | x-terminal-emulator
Recommends: xfonts-base, fonts-dejavu-core, libanyevent-i3-perl, libjson-xs-perl
Recommends: xfonts-base, fonts-dejavu-core, libanyevent-i3-perl (>= 0.12), libjson-xs-perl
Description: improved dynamic tiling window manager
Key features of i3 are good documentation, reasonable defaults (changeable in
a simple configuration file) and good multi-monitor support. The user

View File

@ -9,4 +9,5 @@ man/i3-sensible-pager.1
man/i3-sensible-editor.1
man/i3-sensible-terminal.1
man/i3-dmenu-desktop.1
man/i3-save-tree.1
man/i3bar.1

View File

@ -1,20 +0,0 @@
Description: list x-terminal-emulator as one of i3-sensible-terminals choices
Author: Michael Stapelberg <stapelberg@debian.org>
Origin: vendor
Forwarded: not-needed
Last-Update: 2011-12-28
---
Index: i3-4.1.1/man/i3-sensible-terminal.man
===================================================================
--- i3-4.1.1.orig/man/i3-sensible-terminal.man 2011-12-28 23:56:55.487581000 +0100
+++ i3-4.1.1/man/i3-sensible-terminal.man 2011-12-28 23:57:06.725802633 +0100
@@ -22,6 +22,7 @@
It tries to start one of the following (in that order):
* $TERMINAL (this is a non-standard variable)
+* x-terminal-emulator
* urxvt
* rxvt
* terminator

View File

@ -1,2 +0,0 @@
use-x-terminal-emulator.patch
manpage-x-terminal-emulator.patch

View File

@ -1,25 +0,0 @@
Description: i3-sensible-terminal: try x-terminal-emulator first
Author: Michael Stapelberg <stapelberg@debian.org>
Origin: vendor
Forwarded: not-needed
Last-Update: 2012-09-19
---
Index: i3-4.3/i3-sensible-terminal
===================================================================
--- i3-4.3.orig/i3-sensible-terminal 2012-09-19 18:08:09.000000000 +0200
+++ i3-4.3/i3-sensible-terminal 2012-09-19 18:32:06.393883488 +0200
@@ -4,11 +4,7 @@
#
# This script tries to exec a terminal emulator by trying some known terminal
# emulators.
-#
-# Distributions/packagers should enhance this script with a
-# distribution-specific mechanism to find the preferred terminal emulator. On
-# Debian, there is the x-terminal-emulator symlink for example.
-for terminal in $TERMINAL urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal; do
+for terminal in $TERMINAL x-terminal-emulator urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal; do
if which $terminal > /dev/null 2>&1; then
exec $terminal "$@"
fi

View File

@ -72,15 +72,17 @@ i3-msg 'debuglog on; shmlog on; reload'
No matter whether i3 misbehaved in some way without crashing or whether it just
crashed, the logfile provides all information necessary to debug the problem.
To save a compressed version of the logfile (suitable for attaching it to a
bugreport), use:
--------------------------------------------------------------------
DISPLAY=:0 i3-dump-log | bzip2 -c > /tmp/i3.log.bz2
--------------------------------------------------------------------
To upload a compressed version of the logfile (for a bugreport), use:
------------------------------------------------------------------------------
DISPLAY=:0 i3-dump-log | bzip2 -c | curl --data-binary @- http://logs.i3wm.org
------------------------------------------------------------------------------
This command does not depend on i3 (it also works while i3 displays
the crash dialog), but it requires a working X11 connection.
After running it, you will get a URL to the logfile. Please include that URL in
your bug report.
== On crashes: Obtaining a backtrace
When i3 crashes, it will display a dialog stating “i3 just crashed”, offering

View File

@ -956,10 +956,10 @@ accepted. There are a few things which we dont want to see in i3, e.g. a
command which will focus windows in an alt+tab like way.
When working on bugfixes, please make sure you mention that you are working on
it in the corresponding bugreport at http://bugs.i3wm.org/. In case there is no
bugreport yet, please create one.
it in the corresponding bugreport at https://github.com/i3/i3/issues In case
there is no bugreport yet, please create one.
After you are done, please submit your work for review at http://cr.i3wm.org/
After you are done, please submit your work for review at https://github.com/i3/i3
Do not send emails to the mailing list or any author directly, and dont submit
them in the bugtracker, since all reviews should be done in public at

View File

@ -119,7 +119,8 @@ click_events::
full_text::
The most simple block you can think of is one which just includes the
only required key, the +full_text+ key. i3bar will display the string
value and thats it.
value parsed as
https://developer.gnome.org/pango/stable/PangoMarkupFormat.html[Pango markup].
short_text::
Where appropriate, the +short_text+ (string) entry should also be
provided. It will be used in case the status line needs to be shortened
@ -148,7 +149,7 @@ min_width::
when you want to set a sensible minimum width regardless of which font you
are using, and at what particular size.
align::
Align text on the +center+ (default), +right+ or +left+ of the block, when
Align text on the +center+, +right+ or +left+ (default) of the block, when
the minimum width of the latter, specified by the +min_width+ key, is not
reached.
name and instance::

View File

@ -1,7 +1,7 @@
IPC interface (interprocess communication)
==========================================
Michael Stapelberg <michael@i3wm.org>
February 2014
October 2014
This document describes how to interface with i3 from a separate process. This
is useful for example to remote-control i3 (to write test cases for example) or
@ -156,7 +156,7 @@ following properties:
num (integer)::
The logical number of the workspace. Corresponds to the command
to switch to this workspace.
to switch to this workspace. For named workspaces, this will be -1.
name (string)::
The name of this workspace (by default num+1), as changed by the
user. Encoded in UTF-8.
@ -316,6 +316,10 @@ window_rect (map)::
So, when using the +default+ layout, you will have a 2 pixel border on
each side, making the window_rect +{ "x": 2, "y": 0, "width": 632,
"height": 366 }+ (for example).
deco_rect (map)::
The coordinates of the *window decoration* inside its container. These
coordinates are relative to the container and do not include the actual
client window.
geometry (map)::
The original geometry the window specified when i3 mapped it. Used when
switching a window to floating mode, for example.
@ -613,7 +617,7 @@ you can register to an event.
*Example:*
---------------------------------
type: SUBSCRIBE
payload: [ "workspace", "focus" ]
payload: [ "workspace", "output" ]
---------------------------------
@ -638,6 +642,9 @@ window (3)::
barconfig_update (4)::
Sent when the hidden_state or mode field in the barconfig of any bar
instance was updated and when the config is reloaded.
binding (5)::
Sent when a configured command binding is triggered with the keyboard or
mouse
*Example:*
--------------------------------------------------------------------
@ -661,15 +668,16 @@ if ($is_event) {
This event consists of a single serialized map containing a property
+change (string)+ which indicates the type of the change ("focus", "init",
"empty", "urgent").
"empty", "urgent"). A +current (object)+ property will be present with the
affected workspace whenever the type of event affects a workspace (otherwise,
it will be +null).
Moreover, when the change is "focus", an +old (object)+ and a +current
(object)+ properties will be present with the previous and current
workspace respectively. When the first switch occurs (when i3 focuses
the workspace visible at the beginning) there is no previous
workspace, and the +old+ property will be set to +null+. Also note
that if the previous is empty it will get destroyed when switching,
but will still be present in the "old" property.
When the change is "focus", an +old (object)+ property will be present with the
previous workspace. When the first switch occurs (when i3 focuses the
workspace visible at the beginning) there is no previous workspace, and the
+old+ property will be set to +null+. Also note that if the previous is empty
it will get destroyed when switching, but will still be present in the "old"
property.
*Example:*
---------------------
@ -717,9 +725,13 @@ This event consists of a single serialized map containing a property
+change (string)+ which indicates the type of the change
* +new+ - the window has become managed by i3
* +close+ - the window has closed
* +focus+ - the window has received input focus
* +title+ - the window's title has changed
* +fullscreen_mode+ - the window has entered or exited fullscreen mode
* +move+ - the window has changed its position in the tree
* +floating+ - the window has transitioned to or from floating
* +urgent+ - the window has become urgent or lost its urgent status
Additionally a +container (object)+ field will be present, which consists
of the window's parent container. Be aware that for the "new" event, the
@ -745,6 +757,47 @@ This event consists of a single serialized map reporting on options from the
barconfig of the specified bar_id that were updated in i3. This event is the
same as a +GET_BAR_CONFIG+ reply for the bar with the given id.
=== binding event
This event consists of a single serialized map reporting on the details of a
binding that ran a command because of user input. The +change (sring)+ field
indicates what sort of binding event was triggered (right now it will always be
+"run"+ but may be expanded in the future).
The +binding (object)+ field contains details about the binding that was run:
command (string)::
The i3 command that is configured to run for this binding.
mods (array of strings)::
The modifier keys that were configured with this binding.
input_code (integer)::
If the binding was configured with +bindcode+, this will be the key code
that was given for the binding. If the binding is a mouse binding, it will be
the number of the mouse button that was pressed. Otherwise it will be 0.
symbol (string or null)::
If this is a keyboard binding that was configured with +bindsym+, this
field will contain the given symbol. Otherwise it will be +null+.
input_type (string)::
This will be +"keyboard"+ or +"mouse"+ depending on whether or not this was
a keyboard or a mouse binding.
*Example:*
---------------------------
{
"change": "run",
"binding": {
"command": "nop",
"mods": [
"shift",
"ctrl"
],
"input_code": 0,
"symbol": "t",
"input_type": "keyboard"
}
}
---------------------------
== See also (existing libraries)
[[libraries]]
@ -754,15 +807,14 @@ all this on your own). This list names some (if you wrote one, please let me
know):
C::
i3 includes a headerfile +i3/ipc.h+ which provides you all constants.
https://github.com/acrisci/i3ipc-glib
* i3 includes a headerfile +i3/ipc.h+ which provides you all constants.
* https://github.com/acrisci/i3ipc-glib
Go::
* https://github.com/proxypoke/i3ipc
JavaScript::
* https://github.com/acrisci/i3ipc-gjs
Lua::
* https:/github.com/acrisci/i3ipc-lua
* https://github.com/acrisci/i3ipc-lua
Perl::
* https://metacpan.org/module/AnyEvent::I3
Python::
@ -770,4 +822,4 @@ Python::
* https://github.com/whitelynx/i3ipc (not maintained)
* https://github.com/ziberna/i3-py (not maintained)
Ruby::
http://github.com/badboy/i3-ipc
* http://github.com/badboy/i3-ipc

View File

@ -74,6 +74,9 @@ client, simply called +cpan+. It comes with every Perl installation and can be
used to install the testsuite. Many users prefer to use the more modern
+cpanminus+ instead, though (because it asks no questions and just works):
The tests additionally require +Xephyr(1)+ to run a nested X server. Install
+xserver-xephyr+ on Debian or +xorg-xserver-xephyr+ on Arch Linux.
.Installing testsuite dependencies using cpanminus (preferred)
--------------------------------------------------------------------------------
$ cd ~/i3/testcases
@ -102,7 +105,12 @@ more testcases. Also, it takes care of starting up a separate instance of i3
with an appropriate configuration file and creates a folder for each run
containing the appropriate i3 logfile for each testcase. The latest folder can
always be found under the symlink +latest/+. Unless told differently, it will
run the tests on a separate X server instance (using the Xdummy script).
run the tests on a separate X server instance (using Xephyr).
Xephyr will open a window where you can inspect the running test. You can run
the tests without an X session with Xvfb, such as with +xvfb-run
./complete-run+. This will also speed up the tests signficantly especially on
machines without a powerful video card.
.Example invocation of complete-run.pl+
---------------------------------------
@ -146,12 +154,11 @@ $ less latest/i3-log-for-04-floating.t
If your attempt to run the tests with a bare call to ./complete-run.pl fails, try this:
---------------------------------------------------
$ ./complete-run.pl --parallel=1 --keep-xdummy-output
$ ./complete-run.pl --parallel=1 --keep-xserver-output
---------------------------------------------------
One common cause of failures is not having the X dummy server module
installed. Under Debian and Ubuntu this is the package
+xserver-xorg-video-dummy+.
This will show the output of Xephyr, which is the X server implementation we
use for testing.
==== IPC interface
@ -175,10 +182,9 @@ manager.
=== Filesystem structure
In the git root of i3, the testcases live in the folder +testcases+. This
folder contains the +complete-run.pl+ and +Xdummy+ scripts and a base
configuration file which will be used for the tests. The different testcases
(their file extension is .t, not .pl) themselves can be found in the
conventionally named subfolder +t+:
folder contains the +complete-run.pl+ and a base configuration file which will
be used for the tests. The different testcases (their file extension is .t, not
.pl) themselves can be found in the conventionally named subfolder +t+:
.Filesystem structure
--------------------------------------------
@ -197,7 +203,6 @@ conventionally named subfolder +t+:
│   │   ├── omitted for brevity
│   │   ├── ...
│   │   └── 74-regress-focus-toggle.t
│   └── Xdummy
--------------------------------------------
== Anatomy of a testcase

View File

@ -91,7 +91,7 @@ To display a window in fullscreen mode or to go out of fullscreen mode again,
press +$mod+f+.
There is also a global fullscreen mode in i3 in which the client will span all
available outputs (the command is +fullscreen global+).
available outputs (the command is +fullscreen toggle global+).
=== Opening other applications
@ -153,6 +153,7 @@ to upgrade to a newer version of i3) you can use +$mod+Shift+r+.
=== Exiting i3
To cleanly exit i3 without killing your X server, you can use +$mod+Shift+e+.
By default, a dialog will ask you to confirm if you really want to quit.
=== Floating
@ -366,7 +367,7 @@ bindcode [--release] [Modifiers+]keycode command
*Examples*:
--------------------------------
# Fullscreen
bindsym $mod+f fullscreen
bindsym $mod+f fullscreen toggle
# Restart
bindsym $mod+Shift+r restart
@ -393,6 +394,41 @@ umlauts or special characters 'and' having some comfortably reachable key
bindings. For example, when typing, capslock+1 or capslock+2 for switching
workspaces is totally convenient. Try it :-).
[[mousebindings]]
=== Mouse bindings
A mouse binding makes i3 execute a command upon pressing a specific mouse
button in the scope of the clicked container (see <<command_criteria>>). You
can configure mouse bindings in a similar way to key bindings.
*Syntax*:
----------------------------------
bindsym [--release] [--whole-window] [Modifiers+]button[n] command
----------------------------------
By default, the binding will only run when you click on the titlebar of the
window. If the +--whole-window+ flag is given, it will run when any part of the
window is clicked. If the +--release+ flag is given, it will run when the mouse
button is released.
*Examples*:
--------------------------------
# The middle button over a titlebar kills the window
bindsym --release button2 kill
# The middle button and a modifer over any part of the window kills the window
bindsym --whole-window $mod+button2 kill
# The right button toggles floating
bindsym button3 floating toggle
bindsym $mod+button3 floating toggle
# The side buttons move the window around
bindsym button9 move left
bindsym button8 move right
--------------------------------
[[floating_modifier]]
=== The floating modifier
@ -1069,6 +1105,27 @@ bar {
Available modifiers are Mod1-Mod5, Shift, Control (see +xmodmap(1)+).
=== Mouse button commands
Specifies a command to run when a button was pressed on i3bar to override the
default behavior. Currently only the mouse wheel buttons are supported. This is
useful for disabling the scroll wheel action or running scripts that implement
custom behavior for these buttons.
*Syntax*:
---------------------
wheel_up_cmd <command>
wheel_down_cmd <command>
---------------------
*Example*:
---------------------
bar {
wheel_up_cmd nop
wheel_down_cmd exec ~/.i3/scripts/custom_wheel_down
}
---------------------
=== Bar ID
Specifies the bar ID for the configured bar instance. If this option is missing,
@ -1446,9 +1503,13 @@ Use +layout toggle split+, +layout stacking+, +layout tabbed+, +layout splitv+
or +layout splith+ to change the current container layout to splith/splitv,
stacking, tabbed layout, splitv or splith, respectively.
To make the current window (!) fullscreen, use +fullscreen+, to make
it floating (or tiling again) use +floating enable+ respectively +floating disable+
(or +floating toggle+):
To make the current window (!) fullscreen, use +fullscreen enable+ (or
+fullscreen enable global+ for the global mode), to leave either fullscreen
mode use +fullscreen disable+, and to toggle between these two states use
+fullscreen toggle+ (or +fullscreen toggle global+).
Likewise, to make the current window floating (or tiling again) use +floating
enable+ respectively +floating disable+ (or +floating toggle+):
*Syntax*:
--------------
@ -1469,7 +1530,7 @@ bindsym $mod+x layout toggle
bindsym $mod+x layout toggle all
# Toggle fullscreen
bindsym $mod+f fullscreen
bindsym $mod+f fullscreen toggle
# Toggle floating/tiling
bindsym $mod+t floating toggle
@ -1564,6 +1625,10 @@ container to the next/previous workspace and +move container to workspace curren
See <<move_to_outputs>> for how to move a container/workspace to a different
RandR output.
Workspace names are parsed as
https://developer.gnome.org/pango/stable/PangoMarkupFormat.html[Pango markup]
by i3bar.
[[back_and_forth]]
To switch back to the previously focused workspace, use +workspace
back_and_forth+; likewise, you can move containers to the previously focused
@ -1585,6 +1650,7 @@ move [window|container] [to] workspace <prev|next|current>
-------------------------
bindsym $mod+1 workspace 1
bindsym $mod+2 workspace 2
bindsym $mod+3 workspace 3:<span foreground="red">vim</span>
...
bindsym $mod+Shift+1 move container to workspace 1

View File

@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3-config-wizard
i3_config_wizard_SOURCES := $(wildcard i3-config-wizard/*.c)
i3_config_wizard_HEADERS := $(wildcard i3-config-wizard/*.h)
i3_config_wizard_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(X11_CFLAGS) $(PANGO_CFLAGS)
i3_config_wizard_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(X11_LIBS) $(PANGO_LIBS)
i3_config_wizard_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(PANGO_CFLAGS) $(XKB_COMMON_CFLAGS) $(XKB_COMMON_X11_CFLAGS)
i3_config_wizard_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(PANGO_LIBS) $(XKB_COMMON_LIBS) $(XKB_COMMON_X11_LIBS)
i3_config_wizard_OBJECTS := $(i3_config_wizard_SOURCES:.c=.o)

View File

@ -43,6 +43,9 @@
#include <xcb/xcb_event.h>
#include <xcb/xcb_keysyms.h>
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-x11.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/XKBlib.h>
@ -83,7 +86,9 @@ static xcb_pixmap_t pixmap;
static xcb_gcontext_t pixmap_gc;
static xcb_key_symbols_t *symbols;
xcb_window_t root;
Display *dpy;
static struct xkb_keymap *xkb_keymap;
static uint8_t xkb_base_event;
static uint8_t xkb_base_error;
static void finish();
@ -250,12 +255,24 @@ static char *next_state(const cmdp_token *token) {
* This reduces a lot of confusion for users who switch keyboard
* layouts from qwerty to qwertz or other slight variations of
* qwerty (yes, that happens quite often). */
KeySym sym = XkbKeycodeToKeysym(dpy, keycode, 0, 0);
if (!keysym_used_on_other_key(sym, keycode))
const xkb_keysym_t *syms;
int num = xkb_keymap_key_get_syms_by_level(xkb_keymap, keycode, 0, 0, &syms);
if (num == 0)
errx(1, "xkb_keymap_key_get_syms_by_level returned no symbols for keycode %d", keycode);
if (!keysym_used_on_other_key(syms[0], keycode))
level = 0;
}
KeySym sym = XkbKeycodeToKeysym(dpy, keycode, 0, level);
char *str = XKeysymToString(sym);
const xkb_keysym_t *syms;
int num = xkb_keymap_key_get_syms_by_level(xkb_keymap, keycode, 0, level, &syms);
if (num == 0)
errx(1, "xkb_keymap_key_get_syms_by_level returned no symbols for keycode %d", keycode);
if (num > 1)
printf("xkb_keymap_key_get_syms_by_level (keycode = %d) returned %d symbolsinstead of 1, using only the first one.\n", keycode, num);
char str[4096];
if (xkb_keysym_get_name(syms[0], str, sizeof(str)) == -1)
errx(EXIT_FAILURE, "xkb_keysym_get_name(%u) failed", syms[0]);
const char *release = get_string("release");
char *res;
char *modrep = (modifiers == NULL ? sstrdup("") : sstrdup(modifiers));
@ -642,8 +659,14 @@ static void handle_button_press(xcb_button_press_event_t *event) {
static void finish() {
printf("creating \"%s\"...\n", config_path);
if (!(dpy = XOpenDisplay(NULL)))
errx(1, "Could not connect to X11");
struct xkb_context *xkb_context;
if ((xkb_context = xkb_context_new(0)) == NULL)
errx(1, "could not create xkbcommon context");
int32_t device_id = xkb_x11_get_core_keyboard_device_id(conn);
if ((xkb_keymap = xkb_x11_keymap_new_from_device(xkb_context, conn, device_id, 0)) == NULL)
errx(1, "xkb_x11_keymap_new_from_device failed");
FILE *kc_config = fopen(SYSCONFDIR "/i3/config.keycodes", "r");
if (kc_config == NULL)
@ -797,6 +820,16 @@ int main(int argc, char *argv[]) {
xcb_connection_has_error(conn))
errx(1, "Cannot open display\n");
if (xkb_x11_setup_xkb_extension(conn,
XKB_X11_MIN_MAJOR_XKB_VERSION,
XKB_X11_MIN_MINOR_XKB_VERSION,
0,
NULL,
NULL,
&xkb_base_event,
&xkb_base_error) != 1)
errx(EXIT_FAILURE, "Could not setup XKB extension.");
if (socket_path == NULL)
socket_path = root_atom_contents("I3_SOCKET_PATH", conn, screen);

View File

@ -3,6 +3,6 @@
/* from X11/keysymdef.h */
#define XCB_NUM_LOCK 0xff7f
#define xmacro(atom) xcb_atom_t A_ ## atom;
#define xmacro(atom) xcb_atom_t A_##atom;
#include "atoms.xmacro"
#undef xmacro

View File

@ -3,12 +3,12 @@
#include <err.h>
#define die(...) errx(EXIT_FAILURE, __VA_ARGS__);
#define FREE(pointer) do { \
#define FREE(pointer) \
do { \
if (pointer != NULL) { \
free(pointer); \
pointer = NULL; \
} \
} \
while (0)
} while (0)
extern xcb_window_t root;

View File

@ -3,15 +3,15 @@
#include <err.h>
#define die(...) errx(EXIT_FAILURE, __VA_ARGS__);
#define FREE(pointer) do { \
#define FREE(pointer) \
do { \
if (pointer != NULL) { \
free(pointer); \
pointer = NULL; \
} \
} \
while (0)
} while (0)
#define xmacro(atom) xcb_atom_t A_ ## atom;
#define xmacro(atom) xcb_atom_t A_##atom;
#include "atoms.xmacro"
#undef xmacro

View File

@ -92,6 +92,7 @@ my %allowed_keys = map { ($_, 1) } qw(
name
geometry
window_properties
mark
);
sub strip_containers {

View File

@ -10,7 +10,7 @@
# Hopefully one of these is installed (no flamewars about preference please!):
for editor in $VISUAL $EDITOR nano vim vi emacs pico qe mg jed gedit mc-edit; do
if which $editor > /dev/null 2>&1; then
if command -v $editor > /dev/null 2>&1; then
exec $editor "$@"
fi
done

View File

@ -12,7 +12,7 @@
# We don't use 'more' because it will exit if the file is too short.
# Worst case scenario we'll open the file in your editor.
for pager in $PAGER less most w3m i3-sensible-editor; do
if which $pager > /dev/null 2>&1; then
if command -v $pager > /dev/null 2>&1; then
exec $pager "$@"
fi
done

View File

@ -5,11 +5,11 @@
# This script tries to exec a terminal emulator by trying some known terminal
# emulators.
#
# Distributions/packagers should enhance this script with a
# distribution-specific mechanism to find the preferred terminal emulator. On
# Debian, there is the x-terminal-emulator symlink for example.
for terminal in $TERMINAL urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal; do
if which $terminal > /dev/null 2>&1; then
# We welcome patches that add distribution-specific mechanisms to find the
# preferred terminal emulator. On Debian, there is the x-terminal-emulator
# symlink for example.
for terminal in $TERMINAL x-terminal-emulator urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal termite lxterminal; do
if command -v $terminal > /dev/null 2>&1; then
exec $terminal "$@"
fi
done

View File

@ -75,7 +75,7 @@ bindsym Mod1+h split h
bindsym Mod1+v split v
# enter fullscreen mode for the focused container
bindsym Mod1+f fullscreen
bindsym Mod1+f fullscreen toggle
# change container layout (stacked, tabbed, toggle split)
bindsym Mod1+s layout stacking

View File

@ -69,7 +69,7 @@ bindcode $mod+43 split h
bindcode $mod+55 split v
# enter fullscreen mode for the focused container
bindcode $mod+41 fullscreen
bindcode $mod+41 fullscreen toggle
# change container layout (stacked, tabbed, toggle split)
bindcode $mod+39 layout stacking

View File

@ -5,3 +5,4 @@ Exec=i3
TryExec=i3
Type=Application
X-LightDM-DesktopName=i3
DesktopNames=i3

View File

@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3bar
i3bar_SOURCES := $(wildcard i3bar/src/*.c)
i3bar_HEADERS := $(wildcard i3bar/include/*.h)
i3bar_CFLAGS = $(XCB_CFLAGS) $(X11_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS)
i3bar_LIBS = $(XCB_LIBS) $(X11_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS)
i3bar_CFLAGS = $(XCB_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS)
i3bar_LIBS = $(XCB_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(XCB_XKB_LIBS)
i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o)

View File

@ -15,7 +15,7 @@
typedef struct rect_t rect;
struct ev_loop* main_loop;
struct ev_loop *main_loop;
char *statusline;
char *statusline_buffer;
@ -27,6 +27,7 @@ struct rect_t {
};
typedef enum {
/* First value to make it the default. */
ALIGN_LEFT,
ALIGN_CENTER,
ALIGN_RIGHT

View File

@ -18,10 +18,14 @@ typedef enum {
} position_t;
/* Bar display mode (hide unless modifier is pressed or show in dock mode or always hide in invisible mode) */
typedef enum { M_DOCK = 0, M_HIDE = 1, M_INVISIBLE = 2 } bar_display_mode_t;
typedef enum { M_DOCK = 0,
M_HIDE = 1,
M_INVISIBLE = 2 } bar_display_mode_t;
typedef struct config_t {
int modifier;
char *wheel_up_cmd;
char *wheel_down_cmd;
position_t position;
int verbose;
struct xcb_color_strings_t colors;
@ -38,7 +42,8 @@ typedef struct config_t {
bar_display_mode_t hide_on_modifier;
/* The current hidden_state of the bar, which indicates whether it is hidden or shown */
enum { S_HIDE = 0, S_SHOW = 1 } hidden_state;
enum { S_HIDE = 0,
S_SHOW = 1 } hidden_state;
} config_t;
config_t config;

View File

@ -29,7 +29,7 @@ void destroy_connection(void);
* type must be a valid I3_IPC_MESSAGE_TYPE (see i3/ipc.h for further information)
*
*/
int i3_send_msg(uint32_t type, const char* payload);
int i3_send_msg(uint32_t type, const char *payload);
/*
* Subscribe to all the i3-events, we need

View File

@ -16,7 +16,7 @@
typedef struct i3_output i3_output;
SLIST_HEAD(outputs_head, i3_output);
struct outputs_head *outputs;
struct outputs_head* outputs;
/*
* Start parsing the received json-string
@ -40,6 +40,7 @@ struct i3_output {
char* name; /* Name of the output */
bool active; /* If the output is active */
bool primary; /* If it is the primary output */
bool visible; /* If the bar is visible on this output */
int ws; /* The number of the currently visible ws */
rect rect; /* The rect (relative to the root-win) */
@ -47,8 +48,8 @@ struct i3_output {
xcb_pixmap_t buffer; /* An extra pixmap for double-buffering */
xcb_gcontext_t bargc; /* The graphical context of the bar */
struct ws_head *workspaces; /* The workspaces on this output */
struct tc_head *trayclients; /* The tray clients on this output */
struct ws_head* workspaces; /* The workspaces on this output */
struct tc_head* trayclients; /* The tray clients on this output */
SLIST_ENTRY(i3_output) slist; /* Pointer for the SLIST-Macro */
};

View File

@ -11,55 +11,60 @@
/* Get the maximum/minimum of x and y */
#undef MAX
#define MAX(x,y) ((x) > (y) ? (x) : (y))
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#undef MIN
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define STARTS_WITH(string, len, needle) ((len >= strlen(needle)) && strncasecmp(string, needle, strlen(needle)) == 0)
/* Securely free p */
#define FREE(p) do { \
#define FREE(p) \
do { \
if (p != NULL) { \
free(p); \
p = NULL; \
} \
} while (0)
} while (0)
/* Securely fee single-linked list */
#define FREE_SLIST(l, type) do { \
#define FREE_SLIST(l, type) \
do { \
type *walk = SLIST_FIRST(l); \
while (!SLIST_EMPTY(l)) { \
SLIST_REMOVE_HEAD(l, slist); \
FREE(walk); \
walk = SLIST_FIRST(l); \
} \
} while (0)
} while (0)
/* Securely fee tail-queues */
#define FREE_TAILQ(l, type) do { \
#define FREE_TAILQ(l, type) \
do { \
type *walk = TAILQ_FIRST(l); \
while (!TAILQ_EMPTY(l)) { \
TAILQ_REMOVE(l, TAILQ_FIRST(l), tailq); \
FREE(walk); \
walk = TAILQ_FIRST(l); \
} \
} while (0)
} while (0)
#if defined(DLOG)
#undef DLOG
#endif
/* Use cool logging-macros */
#define DLOG(fmt, ...) do { \
#define DLOG(fmt, ...) \
do { \
if (config.verbose) { \
printf("[%s:%d] " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
} \
} while(0)
} while (0)
/* We will include libi3.h which define its own version of ELOG.
* We want *our* version, so we undef the libi3 one. */
#if defined(ELOG)
#undef ELOG
#endif
#define ELOG(fmt, ...) do { \
#define ELOG(fmt, ...) \
do { \
fprintf(stderr, "[%s:%d] ERROR: " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
} while(0)
} while (0)

View File

@ -182,21 +182,21 @@ static int stdin_boolean(void *context, int val) {
static int stdin_string(void *context, const unsigned char *val, size_t len) {
parser_ctx *ctx = context;
if (strcasecmp(ctx->last_map_key, "full_text") == 0) {
ctx->block.full_text = i3string_from_utf8_with_length((const char *)val, len);
ctx->block.full_text = i3string_from_markup_with_length((const char *)val, len);
}
if (strcasecmp(ctx->last_map_key, "color") == 0) {
sasprintf(&(ctx->block.color), "%.*s", len, val);
}
if (strcasecmp(ctx->last_map_key, "align") == 0) {
if (len == strlen("left") && !strncmp((const char *)val, "left", strlen("left"))) {
ctx->block.align = ALIGN_LEFT;
if (len == strlen("center") && !strncmp((const char *)val, "center", strlen("center"))) {
ctx->block.align = ALIGN_CENTER;
} else if (len == strlen("right") && !strncmp((const char *)val, "right", strlen("right"))) {
ctx->block.align = ALIGN_RIGHT;
} else {
ctx->block.align = ALIGN_CENTER;
ctx->block.align = ALIGN_LEFT;
}
} else if (strcasecmp(ctx->last_map_key, "min_width") == 0) {
i3String *text = i3string_from_utf8_with_length((const char *)val, len);
i3String *text = i3string_from_markup_with_length((const char *)val, len);
ctx->block.min_width = (uint32_t)predict_text_width(text);
i3string_free(text);
}
@ -233,7 +233,7 @@ static int stdin_end_map(void *context) {
/* Ensure we have a full_text set, so that when it is missing (or null),
* i3bar doesnt crash and the user gets an annoying message. */
if (!new_block->full_text)
new_block->full_text = i3string_from_utf8("SPEC VIOLATION (null)");
new_block->full_text = i3string_from_utf8("SPEC VIOLATION: full_text is NULL!");
if (new_block->urgent)
ctx->has_urgent = true;
TAILQ_INSERT_TAIL(&statusline_head, new_block, blocks);
@ -243,7 +243,7 @@ static int stdin_end_map(void *context) {
static int stdin_end_array(void *context) {
DLOG("dumping statusline:\n");
struct status_block *current;
TAILQ_FOREACH (current, &statusline_head, blocks) {
TAILQ_FOREACH(current, &statusline_head, blocks) {
DLOG("full_text = %s\n", i3string_as_utf8(current->full_text));
DLOG("color = %s\n", current->color);
}
@ -304,7 +304,7 @@ static void read_flat_input(char *buffer, int length) {
buffer[length - 1] = '\0';
else
buffer[length] = '\0';
first->full_text = i3string_from_utf8(buffer);
first->full_text = i3string_from_markup(buffer);
}
static bool read_json_input(unsigned char *input, int length) {

View File

@ -112,6 +112,20 @@ static int config_string_cb(void *params_, const unsigned char *val, size_t _len
return 1;
}
if (!strcmp(cur_key, "wheel_up_cmd")) {
DLOG("wheel_up_cmd = %.*s\n", len, val);
FREE(config.wheel_up_cmd);
sasprintf(&config.wheel_up_cmd, "%.*s", len, val);
return 1;
}
if (!strcmp(cur_key, "wheel_down_cmd")) {
DLOG("wheel_down_cmd = %.*s\n", len, val);
FREE(config.wheel_down_cmd);
sasprintf(&config.wheel_down_cmd, "%.*s", len, val);
return 1;
}
if (!strcmp(cur_key, "position")) {
DLOG("position = %.*s\n", len, val);
config.position = (len == 3 && !strncmp((const char *)val, "top", strlen("top")) ? POS_TOP : POS_BOT);

View File

@ -67,7 +67,7 @@ void got_output_reply(char *reply) {
reconfig_windows(false);
i3_output *o_walk;
SLIST_FOREACH (o_walk, outputs, slist) {
SLIST_FOREACH(o_walk, outputs, slist) {
kick_tray_clients(o_walk);
}
@ -159,6 +159,9 @@ void got_bar_config_update(char *event) {
if (found_id == NULL)
return;
/* reconfigure the bar based on the current outputs */
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
free_colors(&(config.colors));
/* update the configuration with the received settings */
@ -169,6 +172,8 @@ void got_bar_config_update(char *event) {
reconfig_windows(true);
}
/* update fonts and colors */
init_xcb_late(config.fontname);
init_colors(&(config.colors));
realloc_sl_buffer();

View File

@ -296,7 +296,7 @@ i3_output *get_output_by_name(char *name) {
if (name == NULL) {
return NULL;
}
SLIST_FOREACH (walk, outputs, slist) {
SLIST_FOREACH(walk, outputs, slist) {
if (!strcmp(walk->name, name)) {
break;
}

View File

@ -123,12 +123,12 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, size_t
/* Offset may be equal to length, in which case display the number */
params->workspaces_walk->name = (offset < len
? i3string_from_utf8_with_length(ws_name + offset, len - offset)
: i3string_from_utf8(ws_num));
? i3string_from_markup_with_length(ws_name + offset, len - offset)
: i3string_from_markup(ws_num));
} else {
/* Default case: just save the name */
params->workspaces_walk->name = i3string_from_utf8_with_length(ws_name, len);
params->workspaces_walk->name = i3string_from_markup_with_length(ws_name, len);
}
/* Save its rendered width */
@ -266,9 +266,9 @@ void free_workspaces(void) {
}
i3_ws *ws_walk;
SLIST_FOREACH (outputs_walk, outputs, slist) {
SLIST_FOREACH(outputs_walk, outputs, slist) {
if (outputs_walk->workspaces != NULL && !TAILQ_EMPTY(outputs_walk->workspaces)) {
TAILQ_FOREACH (ws_walk, outputs_walk->workspaces, tailq) {
TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
I3STRING_FREE(ws_walk->name);
FREE(ws_walk->canonical_name);
}

View File

@ -8,6 +8,7 @@
*
*/
#include <xcb/xcb.h>
#include <xcb/xkb.h>
#include <xcb/xproto.h>
#include <xcb/xcb_aux.h>
@ -63,8 +64,7 @@ static i3Font font;
int bar_height;
/* These are only relevant for XKB, which we only need for grabbing modifiers */
Display *xkb_dpy;
int xkb_event_base;
int xkb_base;
int mod_pressed = 0;
/* Because the statusline is the same on all outputs, we have
@ -117,6 +117,12 @@ int _xcb_request_failed(xcb_void_cookie_t cookie, char *err_msg, int line) {
return 0;
}
uint32_t get_sep_offset(struct status_block *block) {
if (!block->no_separator && block->sep_block_width > 0)
return block->sep_block_width / 2 + block->sep_block_width % 2;
return 0;
}
/*
* Redraws the statusline to the buffer
*
@ -128,7 +134,7 @@ void refresh_statusline(void) {
statusline_width = 0;
/* Predict the text width of all blocks (in pixels). */
TAILQ_FOREACH (block, &statusline_head, blocks) {
TAILQ_FOREACH(block, &statusline_head, blocks) {
if (i3string_get_num_bytes(block->full_text) == 0)
continue;
@ -148,15 +154,15 @@ void refresh_statusline(void) {
block->x_offset = padding_width;
break;
case ALIGN_CENTER:
block->x_offset = padding_width / logical_px(2);
block->x_append = padding_width / logical_px(2) + padding_width % logical_px(2);
block->x_offset = padding_width / 2;
block->x_append = padding_width / 2 + padding_width % 2;
break;
}
}
/* If this is not the last block, add some pixels for a separator. */
if (TAILQ_NEXT(block, blocks) != NULL)
block->width += block->sep_block_width;
statusline_width += block->sep_block_width;
statusline_width += block->width + block->x_offset + block->x_append;
}
@ -168,30 +174,49 @@ void refresh_statusline(void) {
realloc_sl_buffer();
/* Clear the statusline pixmap. */
xcb_rectangle_t rect = {0, 0, root_screen->width_in_pixels, font.height + logical_px(5)};
xcb_rectangle_t rect = {0, 0, root_screen->width_in_pixels, bar_height};
xcb_poly_fill_rectangle(xcb_connection, statusline_pm, statusline_clear, 1, &rect);
/* Draw the text of each block. */
uint32_t x = 0;
TAILQ_FOREACH (block, &statusline_head, blocks) {
TAILQ_FOREACH(block, &statusline_head, blocks) {
if (i3string_get_num_bytes(block->full_text) == 0)
continue;
uint32_t fg_color;
uint32_t colorpixel = (block->color ? get_colorpixel(block->color) : colors.bar_fg);
set_font_colors(statusline_ctx, colorpixel, colors.bar_bg);
draw_text(block->full_text, statusline_pm, statusline_ctx, x + block->x_offset, 1, block->width);
x += block->width + block->x_offset + block->x_append;
/* If this block is urgent, draw it with the defined color and border. */
if (block->urgent) {
fg_color = colors.urgent_ws_fg;
if (TAILQ_NEXT(block, blocks) != NULL && !block->no_separator && block->sep_block_width > 0) {
uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND;
/* Draw the background */
uint32_t bg_color = colors.urgent_ws_bg;
uint32_t bg_values[] = { bg_color, bg_color };
xcb_change_gc(xcb_connection, statusline_ctx, mask, bg_values);
/* The urgent background “overshoots” by 2 px so that the text that
* is printed onto it will not be look so cut off. */
xcb_rectangle_t bg_rect = { x - logical_px(2), logical_px(1), block->width + logical_px(4), bar_height - logical_px(2) };
xcb_poly_fill_rectangle(xcb_connection, statusline_pm, statusline_ctx, 1, &bg_rect);
} else {
fg_color = (block->color ? get_colorpixel(block->color) : colors.bar_fg);
}
set_font_colors(statusline_ctx, fg_color, colors.bar_bg);
draw_text(block->full_text, statusline_pm, statusline_ctx, x + block->x_offset, 3, block->width);
x += block->width + block->sep_block_width + block->x_offset + block->x_append;
uint32_t sep_offset = get_sep_offset(block);
if (TAILQ_NEXT(block, blocks) != NULL && sep_offset > 0) {
/* This is not the last block, draw a separator. */
uint32_t sep_offset = block->sep_block_width / 2 + block->sep_block_width % 2;
uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_LINE_WIDTH;
uint32_t values[] = {colors.sep_fg, colors.bar_bg, logical_px(1)};
xcb_change_gc(xcb_connection, statusline_ctx, mask, values);
xcb_poly_line(xcb_connection, XCB_COORD_MODE_ORIGIN, statusline_pm,
statusline_ctx, 2,
(xcb_point_t[]) {{x - sep_offset, 2},
{x - sep_offset, font.height - 2}});
(xcb_point_t[]) { { x - sep_offset, logical_px(4) },
{ x - sep_offset, bar_height - logical_px(4) } });
}
}
}
@ -206,7 +231,7 @@ void hide_bars(void) {
}
i3_output *walk;
SLIST_FOREACH (walk, outputs, slist) {
SLIST_FOREACH(walk, outputs, slist) {
if (!walk->active) {
continue;
}
@ -231,7 +256,7 @@ void unhide_bars(void) {
cont_child();
SLIST_FOREACH (walk, outputs, slist) {
SLIST_FOREACH(walk, outputs, slist) {
if (walk->bar == XCB_NONE) {
continue;
}
@ -303,7 +328,7 @@ void handle_button(xcb_button_press_event_t *event) {
/* Determine, which bar was clicked */
i3_output *walk;
xcb_window_t bar = event->event;
SLIST_FOREACH (walk, outputs, slist) {
SLIST_FOREACH(walk, outputs, slist) {
if (walk->bar == bar) {
break;
}
@ -326,27 +351,36 @@ void handle_button(xcb_button_press_event_t *event) {
/* First calculate width of tray area */
trayclient *trayclient;
int tray_width = 0;
TAILQ_FOREACH_REVERSE (trayclient, walk->trayclients, tc_head, tailq) {
TAILQ_FOREACH_REVERSE(trayclient, walk->trayclients, tc_head, tailq) {
if (!trayclient->mapped)
continue;
tray_width += (font.height + logical_px(2));
}
if (tray_width > 0)
tray_width += logical_px(2);
int block_x = 0, last_block_x;
int offset = (walk->rect.w - (statusline_width + tray_width)) - logical_px(10);
int offset = walk->rect.w - statusline_width - tray_width - logical_px(4);
x = original_x - offset;
if (x >= 0) {
struct status_block *block;
int sep_offset_remainder = 0;
TAILQ_FOREACH (block, &statusline_head, blocks) {
if (i3string_get_num_bytes(block->full_text) == 0)
continue;
last_block_x = block_x;
block_x += block->width + block->x_offset + block->x_append;
block_x += block->width + block->x_offset + block->x_append
+ get_sep_offset(block) + sep_offset_remainder;
if (x <= block_x && x >= last_block_x) {
send_block_clicked(event->detail, block->name, block->instance, event->root_x, event->root_y);
return;
}
sep_offset_remainder = block->sep_block_width - get_sep_offset(block);
}
}
x = original_x;
@ -370,6 +404,14 @@ void handle_button(xcb_button_press_event_t *event) {
* If there is no more workspace, dont even send the workspace
* command, otherwise (with workspace auto_back_and_forth) wed end
* up on the wrong workspace. */
/* If `wheel_up_cmd [COMMAND]` was specified, it should override
* the default behavior */
if (config.wheel_up_cmd) {
i3_send_msg(I3_IPC_MESSAGE_TYPE_COMMAND, config.wheel_up_cmd);
return;
}
if (cur_ws == TAILQ_FIRST(walk->workspaces))
return;
@ -380,6 +422,14 @@ void handle_button(xcb_button_press_event_t *event) {
* If there is no more workspace, dont even send the workspace
* command, otherwise (with workspace auto_back_and_forth) wed end
* up on the wrong workspace. */
/* if `wheel_down_cmd [COMMAND]` was specified, it should override
* the default behavior */
if (config.wheel_down_cmd) {
i3_send_msg(I3_IPC_MESSAGE_TYPE_COMMAND, config.wheel_down_cmd);
return;
}
if (cur_ws == TAILQ_LAST(walk->workspaces, ws_head))
return;
@ -387,7 +437,7 @@ void handle_button(xcb_button_press_event_t *event) {
break;
case 1:
/* Check if this event regards a workspace button */
TAILQ_FOREACH (cur_ws, walk->workspaces, tailq) {
TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
DLOG("x = %d\n", x);
if (x >= 0 && x < cur_ws->name_width + logical_px(10)) {
break;
@ -398,7 +448,7 @@ void handle_button(xcb_button_press_event_t *event) {
/* Otherwise, focus our currently visible workspace if it is not
* already focused */
if (cur_ws == NULL) {
TAILQ_FOREACH (cur_ws, walk->workspaces, tailq) {
TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
if (cur_ws->visible && !cur_ws->focused)
break;
}
@ -446,6 +496,39 @@ void handle_button(xcb_button_press_event_t *event) {
free(buffer);
}
/*
* Handle visibility notifications: when none of the bars are visible, e.g.
* if windows are in full-screen on each output, suspend the child process.
*
*/
static void handle_visibility_notify(xcb_visibility_notify_event_t *event) {
bool visible = (event->state != XCB_VISIBILITY_FULLY_OBSCURED);
int num_visible = 0;
i3_output *output;
SLIST_FOREACH (output, outputs, slist) {
if (!output->active) {
continue;
}
if (output->bar == event->window) {
if (output->visible == visible) {
return;
}
output->visible = visible;
}
num_visible += output->visible;
}
if (num_visible == 0) {
stop_child();
} else if (num_visible == visible) {
/* Wake the child only when transitioning from 0 to 1 visible bar.
* We cannot transition from 0 to 2 or more visible bars at once since
* visibility events are delivered to each window separately */
cont_child();
}
}
/*
* Adjusts the size of the tray window and alignment of the tray clients by
* configuring their respective x coordinates. To be called when mapping or
@ -455,12 +538,12 @@ void handle_button(xcb_button_press_event_t *event) {
static void configure_trayclients(void) {
trayclient *trayclient;
i3_output *output;
SLIST_FOREACH (output, outputs, slist) {
SLIST_FOREACH(output, outputs, slist) {
if (!output->active)
continue;
int clients = 0;
TAILQ_FOREACH_REVERSE (trayclient, output->trayclients, tc_head, tailq) {
TAILQ_FOREACH_REVERSE(trayclient, output->trayclients, tc_head, tailq) {
if (!trayclient->mapped)
continue;
clients++;
@ -544,7 +627,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
DLOG("X window %08x requested docking\n", client);
i3_output *walk, *output = NULL;
SLIST_FOREACH (walk, outputs, slist) {
SLIST_FOREACH(walk, outputs, slist) {
if (!walk->active)
continue;
if (config.tray_output) {
@ -562,7 +645,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
if (output == NULL &&
config.tray_output &&
strcasecmp("primary", config.tray_output) == 0) {
SLIST_FOREACH (walk, outputs, slist) {
SLIST_FOREACH(walk, outputs, slist) {
if (!walk->active)
continue;
DLOG("Falling back to output %s because no primary output is configured\n", walk->name);
@ -650,12 +733,12 @@ static void handle_destroy_notify(xcb_destroy_notify_event_t *event) {
DLOG("DestroyNotify for window = %08x, event = %08x\n", event->window, event->event);
i3_output *walk;
SLIST_FOREACH (walk, outputs, slist) {
SLIST_FOREACH(walk, outputs, slist) {
if (!walk->active)
continue;
DLOG("checking output %s\n", walk->name);
trayclient *trayclient;
TAILQ_FOREACH (trayclient, walk->trayclients, tailq) {
TAILQ_FOREACH(trayclient, walk->trayclients, tailq) {
if (trayclient->win != event->window)
continue;
@ -679,12 +762,12 @@ static void handle_map_notify(xcb_map_notify_event_t *event) {
DLOG("MapNotify for window = %08x, event = %08x\n", event->window, event->event);
i3_output *walk;
SLIST_FOREACH (walk, outputs, slist) {
SLIST_FOREACH(walk, outputs, slist) {
if (!walk->active)
continue;
DLOG("checking output %s\n", walk->name);
trayclient *trayclient;
TAILQ_FOREACH (trayclient, walk->trayclients, tailq) {
TAILQ_FOREACH(trayclient, walk->trayclients, tailq) {
if (trayclient->win != event->window)
continue;
@ -707,12 +790,12 @@ static void handle_unmap_notify(xcb_unmap_notify_event_t *event) {
DLOG("UnmapNotify for window = %08x, event = %08x\n", event->window, event->event);
i3_output *walk;
SLIST_FOREACH (walk, outputs, slist) {
SLIST_FOREACH(walk, outputs, slist) {
if (!walk->active)
continue;
DLOG("checking output %s\n", walk->name);
trayclient *trayclient;
TAILQ_FOREACH (trayclient, walk->trayclients, tailq) {
TAILQ_FOREACH(trayclient, walk->trayclients, tailq) {
if (trayclient->win != event->window)
continue;
@ -739,11 +822,11 @@ static void handle_property_notify(xcb_property_notify_event_t *event) {
DLOG("xembed_info updated\n");
trayclient *trayclient = NULL, *walk;
i3_output *o_walk;
SLIST_FOREACH (o_walk, outputs, slist) {
SLIST_FOREACH(o_walk, outputs, slist) {
if (!o_walk->active)
continue;
TAILQ_FOREACH (walk, o_walk->trayclients, tailq) {
TAILQ_FOREACH(walk, o_walk->trayclients, tailq) {
if (walk->win != event->window)
continue;
trayclient = walk;
@ -802,12 +885,12 @@ static void handle_configure_request(xcb_configure_request_event_t *event) {
trayclient *trayclient;
i3_output *output;
SLIST_FOREACH (output, outputs, slist) {
SLIST_FOREACH(output, outputs, slist) {
if (!output->active)
continue;
int clients = 0;
TAILQ_FOREACH_REVERSE (trayclient, output->trayclients, tc_head, tailq) {
TAILQ_FOREACH_REVERSE(trayclient, output->trayclients, tc_head, tailq) {
if (!trayclient->mapped)
continue;
clients++;
@ -854,7 +937,66 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) {
}
while ((event = xcb_poll_for_event(xcb_connection)) != NULL) {
switch (event->response_type & ~0x80) {
int type = (event->response_type & ~0x80);
if (type == xkb_base && xkb_base > -1) {
DLOG("received an xkb event\n");
xcb_xkb_state_notify_event_t *state = (xcb_xkb_state_notify_event_t *)event;
if (state->xkbType == XCB_XKB_STATE_NOTIFY) {
int modstate = state->mods & config.modifier;
#define DLOGMOD(modmask, status) \
do { \
switch (modmask) { \
case ShiftMask: \
DLOG("ShiftMask got " #status "!\n"); \
break; \
case ControlMask: \
DLOG("ControlMask got " #status "!\n"); \
break; \
case Mod1Mask: \
DLOG("Mod1Mask got " #status "!\n"); \
break; \
case Mod2Mask: \
DLOG("Mod2Mask got " #status "!\n"); \
break; \
case Mod3Mask: \
DLOG("Mod3Mask got " #status "!\n"); \
break; \
case Mod4Mask: \
DLOG("Mod4Mask got " #status "!\n"); \
break; \
case Mod5Mask: \
DLOG("Mod5Mask got " #status "!\n"); \
break; \
} \
} while (0)
if (modstate != mod_pressed) {
if (modstate == 0) {
DLOGMOD(config.modifier, released);
if (!activated_mode)
hide_bars();
} else {
DLOGMOD(config.modifier, pressed);
activated_mode = false;
unhide_bars();
}
mod_pressed = modstate;
}
#undef DLOGMOD
}
free(event);
continue;
}
switch (type) {
case XCB_VISIBILITY_NOTIFY:
/* Visibility change: a bar is [un]obscured by other window */
handle_visibility_notify((xcb_visibility_notify_event_t *)event);
break;
case XCB_EXPOSE:
/* Expose-events happen, when the window needs to be redrawn */
redraw_bars();
@ -900,76 +1042,6 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) {
void xcb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
}
/*
* We need to bind to the modifier per XKB. Sadly, XCB does not implement this
*
*/
void xkb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
XkbEvent ev;
int modstate = 0;
DLOG("Got XKB-Event!\n");
while (XPending(xkb_dpy)) {
XNextEvent(xkb_dpy, (XEvent *)&ev);
if (ev.type != xkb_event_base) {
ELOG("No Xkb-Event!\n");
continue;
}
if (ev.any.xkb_type != XkbStateNotify) {
ELOG("No State Notify!\n");
continue;
}
unsigned int mods = ev.state.mods;
modstate = mods & config.modifier;
}
#define DLOGMOD(modmask, status) \
do { \
switch (modmask) { \
case ShiftMask: \
DLOG("ShiftMask got " #status "!\n"); \
break; \
case ControlMask: \
DLOG("ControlMask got " #status "!\n"); \
break; \
case Mod1Mask: \
DLOG("Mod1Mask got " #status "!\n"); \
break; \
case Mod2Mask: \
DLOG("Mod2Mask got " #status "!\n"); \
break; \
case Mod3Mask: \
DLOG("Mod3Mask got " #status "!\n"); \
break; \
case Mod4Mask: \
DLOG("Mod4Mask got " #status "!\n"); \
break; \
case Mod5Mask: \
DLOG("Mod5Mask got " #status "!\n"); \
break; \
} \
} while (0)
if (modstate != mod_pressed) {
if (modstate == 0) {
DLOGMOD(config.modifier, released);
if (!activated_mode)
hide_bars();
} else {
DLOGMOD(config.modifier, pressed);
activated_mode = false;
unhide_bars();
}
mod_pressed = modstate;
}
#undef DLOGMOD
}
/*
* Early initialization of the connection to X11: Everything which does not
* depend on 'config'.
@ -1053,44 +1125,23 @@ char *init_xcb_early() {
*
*/
void register_xkb_keyevents() {
if (xkb_dpy == NULL) {
int xkb_major, xkb_minor, xkb_errbase, xkb_err;
xkb_major = XkbMajorVersion;
xkb_minor = XkbMinorVersion;
xkb_dpy = XkbOpenDisplay(NULL,
&xkb_event_base,
&xkb_errbase,
&xkb_major,
&xkb_minor,
&xkb_err);
if (xkb_dpy == NULL) {
ELOG("No XKB!\n");
const xcb_query_extension_reply_t *extreply;
extreply = xcb_get_extension_data(conn, &xcb_xkb_id);
if (!extreply->present) {
ELOG("xkb is not present on this server\n");
exit(EXIT_FAILURE);
}
if (fcntl(ConnectionNumber(xkb_dpy), F_SETFD, FD_CLOEXEC) == -1) {
ELOG("Could not set FD_CLOEXEC on xkbdpy: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
int i1;
if (!XkbQueryExtension(xkb_dpy, &i1, &xkb_event_base, &xkb_errbase, &xkb_major, &xkb_minor)) {
ELOG("XKB not supported by X-server!\n");
exit(EXIT_FAILURE);
}
if (!XkbSelectEvents(xkb_dpy, XkbUseCoreKbd, XkbStateNotifyMask, XkbStateNotifyMask)) {
ELOG("Could not grab Key!\n");
exit(EXIT_FAILURE);
}
xkb_io = smalloc(sizeof(ev_io));
ev_io_init(xkb_io, &xkb_io_cb, ConnectionNumber(xkb_dpy), EV_READ);
ev_io_start(main_loop, xkb_io);
XFlush(xkb_dpy);
}
DLOG("initializing xcb-xkb\n");
xcb_xkb_use_extension(conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION);
xcb_xkb_select_events(conn,
XCB_XKB_ID_USE_CORE_KBD,
XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
0,
XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
0xff,
0xff,
NULL);
xkb_base = extreply->first_event;
}
/*
@ -1098,13 +1149,14 @@ void register_xkb_keyevents() {
*
*/
void deregister_xkb_keyevents() {
if (xkb_dpy != NULL) {
ev_io_stop(main_loop, xkb_io);
XCloseDisplay(xkb_dpy);
close(xkb_io->fd);
FREE(xkb_io);
xkb_dpy = NULL;
}
xcb_xkb_select_events(conn,
XCB_XKB_ID_USE_CORE_KBD,
0,
0,
0,
0xff,
0xff,
NULL);
}
/*
@ -1280,7 +1332,7 @@ void init_tray_colors(void) {
void clean_xcb(void) {
i3_output *o_walk;
free_workspaces();
SLIST_FOREACH (o_walk, outputs, slist) {
SLIST_FOREACH(o_walk, outputs, slist) {
destroy_window(o_walk);
FREE(o_walk->trayclients);
FREE(o_walk->workspaces);
@ -1435,7 +1487,7 @@ void reconfig_windows(bool redraw_bars) {
static bool tray_configured = false;
i3_output *walk;
SLIST_FOREACH (walk, outputs, slist) {
SLIST_FOREACH(walk, outputs, slist) {
if (!walk->active) {
/* If an output is not active, we destroy its bar */
/* FIXME: Maybe we rather want to unmap? */
@ -1462,6 +1514,12 @@ void reconfig_windows(bool redraw_bars) {
values[2] = XCB_EVENT_MASK_EXPOSURE |
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
XCB_EVENT_MASK_BUTTON_PRESS;
if (config.hide_on_modifier == M_DOCK) {
/* If the bar is normally visible, catch visibility change events to suspend
* the status process when the bar is obscured by full-screened windows. */
values[2] |= XCB_EVENT_MASK_VISIBILITY_CHANGE;
walk->visible = true;
}
xcb_void_cookie_t win_cookie = xcb_create_window_checked(xcb_connection,
root_screen->root_depth,
walk->bar,
@ -1595,7 +1653,7 @@ void reconfig_windows(bool redraw_bars) {
* VGA-1 but output == [HDMI-1]).
*/
i3_output *output;
SLIST_FOREACH (output, outputs, slist) {
SLIST_FOREACH(output, outputs, slist) {
if (strcasecmp(output->name, tray_output) == 0 ||
(strcasecmp(tray_output, "primary") == 0 && output->primary)) {
init_tray();
@ -1684,7 +1742,7 @@ void draw_bars(bool unhide) {
refresh_statusline();
i3_output *outputs_walk;
SLIST_FOREACH (outputs_walk, outputs, slist) {
SLIST_FOREACH(outputs_walk, outputs, slist) {
if (!outputs_walk->active) {
DLOG("Output %s inactive, skipping...\n", outputs_walk->name);
continue;
@ -1714,29 +1772,29 @@ void draw_bars(bool unhide) {
* position */
trayclient *trayclient;
int traypx = 0;
TAILQ_FOREACH (trayclient, outputs_walk->trayclients, tailq) {
TAILQ_FOREACH(trayclient, outputs_walk->trayclients, tailq) {
if (!trayclient->mapped)
continue;
/* 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 + logical_px(2);
}
/* Add 2px of padding if there are any tray icons */
if (traypx > 0)
traypx += 2;
traypx += logical_px(2);
xcb_copy_area(xcb_connection,
statusline_pm,
outputs_walk->buffer,
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, (int)statusline_width), font.height + 2);
MAX(0, (int16_t)(statusline_width - outputs_walk->rect.w + logical_px(4))), 0,
MAX(0, (int16_t)(outputs_walk->rect.w - statusline_width - traypx - logical_px(4))), 0,
MIN(outputs_walk->rect.w - traypx - logical_px(4), (int)statusline_width), bar_height);
}
if (!config.disable_ws) {
i3_ws *ws_walk;
TAILQ_FOREACH (ws_walk, outputs_walk->workspaces, tailq) {
TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
DLOG("Drawing Button for WS %s at x = %d, len = %d\n",
i3string_as_utf8(ws_walk->name), i, ws_walk->name_width);
uint32_t fg_color = colors.inactive_ws_fg;
@ -1852,7 +1910,7 @@ void draw_bars(bool unhide) {
*/
void redraw_bars(void) {
i3_output *outputs_walk;
SLIST_FOREACH (outputs_walk, outputs, slist) {
SLIST_FOREACH(outputs_walk, outputs, slist) {
if (!outputs_walk->active) {
continue;
}

View File

@ -1,6 +1,7 @@
xmacro(_NET_SUPPORTED)
xmacro(_NET_SUPPORTING_WM_CHECK)
xmacro(_NET_WM_NAME)
xmacro(_NET_WM_MOVERESIZE)
xmacro(_NET_WM_STATE_FULLSCREEN)
xmacro(_NET_WM_STATE_DEMANDS_ATTENTION)
xmacro(_NET_WM_STATE_MODAL)
@ -16,7 +17,11 @@ xmacro(_NET_WM_STRUT_PARTIAL)
xmacro(_NET_CLIENT_LIST)
xmacro(_NET_CLIENT_LIST_STACKING)
xmacro(_NET_CURRENT_DESKTOP)
xmacro(_NET_NUMBER_OF_DESKTOPS)
xmacro(_NET_DESKTOP_NAMES)
xmacro(_NET_DESKTOP_VIEWPORT)
xmacro(_NET_ACTIVE_WINDOW)
xmacro(_NET_CLOSE_WINDOW)
xmacro(_NET_STARTUP_ID)
xmacro(_NET_WORKAREA)
xmacro(WM_PROTOCOLS)
@ -34,3 +39,4 @@ xmacro(I3_PID)
xmacro(_NET_REQUEST_FRAME_EXTENTS)
xmacro(_NET_FRAME_EXTENTS)
xmacro(_MOTIF_WM_HINTS)
xmacro(WM_CHANGE_STATE)

View File

@ -24,7 +24,7 @@ const char *DEFAULT_BINDING_MODE;
*
*/
Binding *configure_binding(const char *bindtype, const char *modifiers, const char *input_code,
const char *release, const char *command, const char *mode);
const char *release, const char *whole_window, const char *command, const char *mode);
/**
* Grab the bound keys (tell X to send us keypress events for those keycodes)
@ -61,9 +61,15 @@ void switch_mode(const char *new_mode);
void check_for_duplicate_bindings(struct context *context);
/**
* Runs the given binding and handles parse errors. Returns a CommandResult for
* running the binding's command. Caller should render tree if
* needs_tree_render is true. Free with command_result_free().
* Frees the binding. If bind is null, it simply returns.
*/
void binding_free(Binding *bind);
/**
* Runs the given binding and handles parse errors. If con is passed, it will
* execute the command binding with that container selected by criteria.
* Returns a CommandResult for running the binding's command. Caller should
* render tree if needs_tree_render is true. Free with command_result_free().
*
*/
CommandResult *run_binding(Binding *bind);
CommandResult *run_binding(Binding *bind, Con *con);

View File

@ -187,10 +187,10 @@ void cmd_focus_level(I3_CMD, char *level);
void cmd_focus(I3_CMD);
/**
* Implementation of 'fullscreen [global]'.
* Implementation of 'fullscreen [enable|disable|toggle] [global]'.
*
*/
void cmd_fullscreen(I3_CMD, char *fullscreen_mode);
void cmd_fullscreen(I3_CMD, char *action, char *fullscreen_mode);
/**
* Implementation of 'move <direction> [<pixels> [px]]'.

View File

@ -44,6 +44,14 @@ struct CommandResult {
bool needs_tree_render;
};
/**
* Parses a string (or word, if as_word is true). Extracted out of
* parse_command so that it can be used in src/workspace.c for interpreting
* workspace commands.
*
*/
char *parse_string(const char **walk, bool as_word);
/**
* Parses and executes the given command. If a caller-allocated yajl_gen is
* passed, a json reply will be generated in the format specified by the ipc

View File

@ -18,7 +18,6 @@
*/
Con *con_new_skeleton(Con *parent, i3Window *window);
/* A wrapper for con_new_skeleton, to retain the old con_new behaviour
*
*/
@ -173,6 +172,18 @@ void con_fix_percent(Con *con);
*/
void con_toggle_fullscreen(Con *con, int fullscreen_mode);
/**
* Enables fullscreen mode for the given container, if necessary.
*
*/
void con_enable_fullscreen(Con *con, fullscreen_mode_t fullscreen_mode);
/**
* Disables fullscreen mode for the given container, if necessary.
*
*/
void con_disable_fullscreen(Con *con);
/**
* Moves the given container to the currently focused container on the given
* workspace.
@ -345,3 +356,9 @@ void con_set_urgency(Con *con, bool urgent);
*
*/
char *con_get_tree_representation(Con *con);
/**
* force parent split containers to be redrawn
*
*/
void con_force_split_parents_redraw(Con *con);

View File

@ -241,10 +241,13 @@ struct Barconfig {
char *socket_path;
/** Bar display mode (hide unless modifier is pressed or show in dock mode or always hide in invisible mode) */
enum { M_DOCK = 0, M_HIDE = 1, M_INVISIBLE = 2 } mode;
enum { M_DOCK = 0,
M_HIDE = 1,
M_INVISIBLE = 2 } mode;
/* The current hidden_state of the bar, which indicates whether it is hidden or shown */
enum { S_HIDE = 0, S_SHOW = 1 } hidden_state;
enum { S_HIDE = 0,
S_SHOW = 1 } hidden_state;
/** Bar modifier (to show bar when in hide mode). */
enum {
@ -258,8 +261,17 @@ struct Barconfig {
M_MOD5 = 7
} modifier;
/** Command that should be run when mouse wheel up button is pressed over
* i3bar to override the default behavior. */
char *wheel_up_cmd;
/** Command that should be run when mouse wheel down button is pressed over
* i3bar to override the default behavior. */
char *wheel_down_cmd;
/** Bar position (bottom by default). */
enum { P_BOTTOM = 0, P_TOP = 1 } position;
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.
@ -314,6 +326,20 @@ struct Barconfig {
TAILQ_ENTRY(Barconfig) configs;
};
/**
* Finds the configuration file to use (either the one specified by
* override_configpath), the users one or the system default) and calls
* parse_file().
*
* If you specify override_configpath, only this path is used to look for a
* configuration file.
*
* If use_nagbar is false, don't try to start i3-nagbar but log the errors to
* stdout/stderr instead.
*
*/
bool parse_configuration(const char *override_configpath, bool use_nagbar);
/**
* Reads the configuration from ~/.i3/config or /etc/i3/config if not found.
*

View File

@ -24,7 +24,7 @@ uint32_t modifiers_from_str(const char *str);
* using 'call cfg_foo()' in parser-specs/.*.spec. Useful so that we dont need
* to repeat the definition all the time. */
#define CFGFUN(name, ...) \
void cfg_ ## name (I3_CFG, ## __VA_ARGS__ )
void cfg_##name(I3_CFG, ##__VA_ARGS__)
/* The following functions are called by the config parser, see
* parser-specs/config.spec. They get the parsed parameters and store them in
@ -61,10 +61,10 @@ CFGFUN(color_single, const char *colorclass, const char *color);
CFGFUN(floating_modifier, const char *modifiers);
CFGFUN(new_window, const char *windowtype, const char *border, const long width);
CFGFUN(workspace, const char *workspace, const char *output);
CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *command);
CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *whole_window, const char *command);
CFGFUN(enter_mode, const char *mode);
CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *command);
CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *whole_window, const char *command);
CFGFUN(bar_font, const char *font);
CFGFUN(bar_mode, const char *mode);
@ -73,6 +73,8 @@ CFGFUN(bar_id, const char *bar_id);
CFGFUN(bar_output, const char *output);
CFGFUN(bar_verbose, const char *verbose);
CFGFUN(bar_modifier, const char *modifier);
CFGFUN(bar_wheel_up_cmd, const char *command);
CFGFUN(bar_wheel_down_cmd, const char *command);
CFGFUN(bar_position, const char *position);
CFGFUN(bar_i3bar_command, const char *i3bar_command);
CFGFUN(bar_color, const char *colorclass, const char *border, const char *background, const char *text);

View File

@ -33,7 +33,10 @@ struct ConfigResultIR *parse_config(const char *input, struct context *context);
/**
* Parses the given file by first replacing the variables, then calling
* parse_config and possibly launching i3-nagbar.
* parse_config and launching i3-nagbar if use_nagbar is true.
*
* The return value is a boolean indicating whether there were errors during
* parsing.
*
*/
void parse_file(const char *f);
bool parse_file(const char *f, bool use_nagbar);

View File

@ -47,24 +47,32 @@ typedef struct Match Match;
typedef struct Assignment Assignment;
typedef struct Window i3Window;
/******************************************************************************
* Helper types
*****************************************************************************/
typedef enum { D_LEFT, D_RIGHT, D_UP, D_DOWN } direction_t;
typedef enum { NO_ORIENTATION = 0, HORIZ, VERT } orientation_t;
typedef enum { BS_NORMAL = 0, BS_NONE = 1, BS_PIXEL = 2 } border_style_t;
typedef enum { D_LEFT,
D_RIGHT,
D_UP,
D_DOWN } direction_t;
typedef enum { NO_ORIENTATION = 0,
HORIZ,
VERT } orientation_t;
typedef enum { BS_NORMAL = 0,
BS_NONE = 1,
BS_PIXEL = 2 } border_style_t;
/** parameter to specify whether tree_close() and x_window_kill() should kill
* only this specific window or the whole X11 client */
typedef enum { DONT_KILL_WINDOW = 0, KILL_WINDOW = 1, KILL_CLIENT = 2 } kill_window_t;
typedef enum { DONT_KILL_WINDOW = 0,
KILL_WINDOW = 1,
KILL_CLIENT = 2 } kill_window_t;
/** describes if the window is adjacent to the output (physical screen) edges. */
typedef enum { ADJ_NONE = 0,
ADJ_LEFT_SCREEN_EDGE = (1 << 0),
ADJ_RIGHT_SCREEN_EDGE = (1 << 1),
ADJ_UPPER_SCREEN_EDGE = (1 << 2),
ADJ_LOWER_SCREEN_EDGE = (1 << 4)} adjacent_t;
ADJ_LOWER_SCREEN_EDGE = (1 << 4) } adjacent_t;
enum {
BIND_NONE = 0,
@ -247,6 +255,11 @@ struct Binding {
B_UPON_KEYRELEASE_IGNORE_MODS = 2,
} release;
/** If this is true for a mouse binding, the binding should be executed
* when the button is pressed over any part of the window, not just the
* title bar (default). */
bool whole_window;
uint32_t number_keycodes;
/** Keycode to bind */
@ -267,7 +280,6 @@ struct Binding {
* This is an array of number_keycodes size. */
xcb_keycode_t *translated_to;
/** Command, like in command mode */
char *command;
@ -367,7 +379,9 @@ struct Window {
bool doesnt_accept_focus;
/** Whether the window says it is a dock window */
enum { W_NODOCK = 0, W_DOCK_TOP = 1, W_DOCK_BOTTOM = 2 } dock;
enum { W_NODOCK = 0,
W_DOCK_TOP = 1,
W_DOCK_BOTTOM = 2 } dock;
/** When this window was marked urgent. 0 means not urgent */
struct timeval urgent;
@ -407,7 +421,9 @@ struct Match {
M_DOCK_BOTTOM = 3
} dock;
xcb_window_t id;
enum { M_ANY = 0, M_TILING, M_FLOATING } floating;
enum { M_ANY = 0,
M_TILING,
M_FLOATING } floating;
Con *con_id;
/* Where the window looking for a match should be inserted:
@ -419,7 +435,9 @@ struct Match {
* (dockareas)
*
*/
enum { M_HERE = 0, M_ASSIGN_WS, M_BELOW } insert_where;
enum { M_HERE = 0,
M_ASSIGN_WS,
M_BELOW } insert_where;
TAILQ_ENTRY(Match) matches;
@ -470,7 +488,9 @@ struct Assignment {
};
/** Fullscreen modes. Used by Con.fullscreen_mode. */
typedef enum { CF_NONE = 0, CF_OUTPUT = 1, CF_GLOBAL = 2 } fullscreen_mode_t;
typedef enum { CF_NONE = 0,
CF_OUTPUT = 1,
CF_GLOBAL = 2 } fullscreen_mode_t;
/**
* A 'Con' represents everything from the X11 root window down to a single X11 window.
@ -596,7 +616,7 @@ struct Con {
TAILQ_ENTRY(Con) floating_windows;
/** callbacks */
void(*on_remove_child)(Con *);
void (*on_remove_child)(Con *);
enum {
/* Not a scratchpad window. */

View File

@ -18,6 +18,24 @@
*/
void ewmh_update_current_desktop(void);
/**
* Updates _NET_NUMBER_OF_DESKTOPS which we interpret as the number of
* noninternal workspaces.
*/
void ewmh_update_number_of_desktops(void);
/**
* Updates _NET_DESKTOP_NAMES: "The names of all virtual desktops. This is a
* list of NULL-terminated strings in UTF-8 encoding"
*/
void ewmh_update_desktop_names(void);
/**
* Updates _NET_DESKTOP_VIEWPORT, which is an array of pairs of cardinals that
* define the top left corner of each desktop's viewport.
*/
void ewmh_update_desktop_viewport(void);
/**
* Updates _NET_ACTIVE_WINDOW with the currently focused window.
*

View File

@ -12,7 +12,7 @@
#include "tree.h"
/** Callback for dragging */
typedef void(*callback_t)(Con*, Rect*, uint32_t, uint32_t, const void*);
typedef void (*callback_t)(Con *, Rect *, uint32_t, uint32_t, const void *);
/** Macro to create a callback function for dragging */
#define DRAGGING_CB(name) \
@ -23,7 +23,7 @@ typedef void(*callback_t)(Con*, Rect*, uint32_t, uint32_t, const void*);
typedef enum { BORDER_LEFT = (1 << 0),
BORDER_RIGHT = (1 << 1),
BORDER_TOP = (1 << 2),
BORDER_BOTTOM = (1 << 3)} border_t;
BORDER_BOTTOM = (1 << 3) } border_t;
/**
* Enables floating mode for the given container by detaching it from its

View File

@ -13,6 +13,7 @@
#include <xcb/randr.h>
extern int randr_base;
extern int xkb_base;
/**
* Adds the given sequence to the list of events which are ignored.

View File

@ -13,6 +13,7 @@
#include <sys/resource.h>
#include <xcb/xcb_keysyms.h>
#include <xcb/xkb.h>
#include <X11/XKBlib.h>
@ -42,7 +43,7 @@ extern xcb_key_symbols_t *keysyms;
extern char **start_argv;
extern Display *xlibdpy, *xkbdpy;
extern int xkb_current_group;
extern TAILQ_HEAD(bindings_head, Binding) *bindings;
extern TAILQ_HEAD(bindings_head, Binding) * bindings;
extern TAILQ_HEAD(autostarts_head, Autostart) autostarts;
extern TAILQ_HEAD(autostarts_always_head, Autostart) autostarts_always;
extern TAILQ_HEAD(ws_assignments_head, Workspace_Assignment) ws_assignments;

View File

@ -17,7 +17,7 @@ typedef struct i3_ipc_header {
char magic[6];
uint32_t size;
uint32_t type;
} __attribute__ ((packed)) i3_ipc_header_t;
} __attribute__((packed)) i3_ipc_header_t;
/*
* Messages from clients to i3
@ -100,3 +100,6 @@ typedef struct i3_ipc_header {
/** Bar config update will be triggered to update the bar config */
#define I3_IPC_EVENT_BARCONFIG_UPDATE (I3_IPC_EVENT_MASK | 4)
/** The binding event will be triggered when bindings run */
#define I3_IPC_EVENT_BINDING (I3_IPC_EVENT_MASK | 5)

View File

@ -42,11 +42,11 @@ typedef struct ipc_client {
* message_type is the type of the message as the sender specified it.
*
*/
typedef void(*handler_t)(int, uint8_t*, int, uint32_t, uint32_t);
typedef void (*handler_t)(int, uint8_t *, int, uint32_t, uint32_t);
/* Macro to declare a callback */
#define IPC_HANDLER(name) \
static void handle_ ## name (int fd, uint8_t *message, \
static void handle_##name(int fd, uint8_t *message, \
int size, uint32_t message_size, \
uint32_t message_type)
@ -89,11 +89,17 @@ void ipc_shutdown(void);
void dump_node(yajl_gen gen, Con *con, bool inplace_restart);
/**
* For the workspace "focus" event we send, along the usual "change" field,
* also the current and previous workspace, in "current" and "old"
* respectively.
* Generates a json workspace event. Returns a dynamically allocated yajl
* generator. Free with yajl_gen_free().
*/
void ipc_send_workspace_focus_event(Con *current, Con *old);
yajl_gen ipc_marshal_workspace_event(const char *change, Con *current, Con *old);
/**
* For the workspace events we send, along with the usual "change" field, also
* the workspace container in "current". For focus events, we send the
* previously focused workspace in "old".
*/
void ipc_send_workspace_event(const char *change, Con *current, Con *old);
/**
* For the window events we send, along the usual "change" field,
@ -105,3 +111,8 @@ void ipc_send_window_event(const char *property, Con *con);
* For the barconfig update events, we send the serialized barconfig.
*/
void ipc_send_barconfig_update_event(Barconfig *barconfig);
/**
* For the binding events, we send the serialized binding struct.
*/
void ipc_send_binding_event(const char *event_type, Binding *bind);

View File

@ -72,17 +72,17 @@ struct Font {
* infrastructure, we define a fallback. */
#if !defined(LOG)
void verboselog(char *fmt, ...)
__attribute__ ((format (printf, 1, 2)));
__attribute__((format(printf, 1, 2)));
#define LOG(fmt, ...) verboselog("[libi3] " __FILE__ " " fmt, ##__VA_ARGS__)
#endif
#if !defined(ELOG)
void errorlog(char *fmt, ...)
__attribute__ ((format (printf, 1, 2)));
__attribute__((format(printf, 1, 2)));
#define ELOG(fmt, ...) errorlog("[libi3] ERROR: " fmt, ##__VA_ARGS__)
#endif
#if !defined(DLOG)
void debuglog(char *fmt, ...)
__attribute__ ((format (printf, 1, 2)));
__attribute__((format(printf, 1, 2)));
#define DLOG(fmt, ...) debuglog("%s:%s:%d - " fmt, I3__FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#endif
@ -141,6 +141,12 @@ int sasprintf(char **strp, const char *fmt, ...);
*/
i3String *i3string_from_utf8(const char *from_utf8);
/**
* Build an i3String from an UTF-8 encoded string in Pango markup.
*
*/
i3String *i3string_from_markup(const char *from_markup);
/**
* Build an i3String from an UTF-8 encoded string with fixed length.
* To be used when no proper NUL-terminaison is available.
@ -149,6 +155,13 @@ i3String *i3string_from_utf8(const char *from_utf8);
*/
i3String *i3string_from_utf8_with_length(const char *from_utf8, size_t num_bytes);
/**
* Build an i3String from an UTF-8 encoded string in Pango markup with fixed
* length.
*
*/
i3String *i3string_from_markup_with_length(const char *from_markup, size_t num_bytes);
/**
* Build an i3String from an UCS-2 encoded string.
* Returns the newly-allocated i3String.
@ -168,12 +181,12 @@ void i3string_free(i3String *str);
*
*/
#define I3STRING_FREE(str) \
do { \
do { \
if (str != NULL) { \
i3string_free(str); \
str = NULL; \
} \
} while (0)
} while (0)
/**
* Returns the UTF-8 encoded version of the i3String.
@ -193,6 +206,11 @@ const xcb_char2b_t *i3string_as_ucs2(i3String *str);
*/
size_t i3string_get_num_bytes(i3String *str);
/**
* Whether the given i3String is in Pango markup.
*/
bool i3string_is_markup(i3String *str);
/**
* Returns the number of glyphs in an i3String.
*
@ -290,7 +308,8 @@ uint32_t get_mod_mask_for(uint32_t keysym,
/**
* Loads a font for usage, also getting its height. If fallback is true,
* the fonts 'fixed' or '-misc-*' will be loaded instead of exiting.
* the fonts 'fixed' or '-misc-*' will be loaded instead of exiting. If any
* font was previously loaded, it will be freed.
*
*/
i3Font load_font(const char *pattern, const bool fallback);
@ -302,7 +321,8 @@ i3Font load_font(const char *pattern, const bool fallback);
void set_font(i3Font *font);
/**
* Frees the resources taken by the current font.
* Frees the resources taken by the current font. If no font was previously
* loaded, it simply returns.
*
*/
void free_font(void);

View File

@ -78,14 +78,14 @@ void set_verbosity(bool _verbose);
*
*/
void debuglog(char *fmt, ...)
__attribute__ ((format (printf, 1, 2)));
__attribute__((format(printf, 1, 2)));
/**
* Logs the given message to stdout while prefixing the current time to it.
*
*/
void errorlog(char *fmt, ...)
__attribute__ ((format (printf, 1, 2)));
__attribute__((format(printf, 1, 2)));
/**
* Logs the given message to stdout while prefixing the current time to it,
@ -93,7 +93,7 @@ void errorlog(char *fmt, ...)
*
*/
void verboselog(char *fmt, ...)
__attribute__ ((format (printf, 1, 2)));
__attribute__((format(printf, 1, 2)));
/**
* Deletes the unused log files. Useful if i3 exits immediately, eg.

View File

@ -10,8 +10,8 @@
#pragma once
/**
* Moves the current container in the given direction (TOK_LEFT, TOK_RIGHT,
* Moves the given container in the given direction (TOK_LEFT, TOK_RIGHT,
* TOK_UP, TOK_DOWN from cmdparse.l)
*
*/
void tree_move(int direction);
void tree_move(Con *con, int direction);

View File

@ -91,17 +91,17 @@
* Singly-linked List definitions.
*/
#define SLIST_HEAD(name, type) \
struct name { \
struct name { \
struct type *slh_first; /* first element */ \
}
}
#define SLIST_HEAD_INITIALIZER(head) \
{ NULL }
#define SLIST_ENTRY(type) \
struct { \
struct { \
struct type *sle_next; /* next element */ \
}
}
/*
* Singly-linked List access methods.
@ -112,7 +112,7 @@ struct { \
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
#define SLIST_FOREACH(var, head, field) \
for((var) = SLIST_FIRST(head); \
for ((var) = SLIST_FIRST(head); \
(var) != SLIST_END(head); \
(var) = SLIST_NEXT(var, field))
@ -124,29 +124,35 @@ struct { \
/*
* Singly-linked List functions.
*/
#define SLIST_INIT(head) { \
#define SLIST_INIT(head) \
{ \
SLIST_FIRST(head) = SLIST_END(head); \
}
}
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
#define SLIST_INSERT_AFTER(slistelm, elm, field) \
do { \
(elm)->field.sle_next = (slistelm)->field.sle_next; \
(slistelm)->field.sle_next = (elm); \
} while (0)
} while (0)
#define SLIST_INSERT_HEAD(head, elm, field) do { \
#define SLIST_INSERT_HEAD(head, elm, field) \
do { \
(elm)->field.sle_next = (head)->slh_first; \
(head)->slh_first = (elm); \
} while (0)
} while (0)
#define SLIST_REMOVE_NEXT(head, elm, field) do { \
#define SLIST_REMOVE_NEXT(head, elm, field) \
do { \
(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
} while (0)
} while (0)
#define SLIST_REMOVE_HEAD(head, field) do { \
#define SLIST_REMOVE_HEAD(head, field) \
do { \
(head)->slh_first = (head)->slh_first->field.sle_next; \
} while (0)
} while (0)
#define SLIST_REMOVE(head, elm, type, field) do { \
#define SLIST_REMOVE(head, elm, type, field) \
do { \
if ((head)->slh_first == (elm)) { \
SLIST_REMOVE_HEAD((head), field); \
} else { \
@ -154,28 +160,27 @@ struct { \
\
while (curelm->field.sle_next != (elm)) \
curelm = curelm->field.sle_next; \
curelm->field.sle_next = \
curelm->field.sle_next->field.sle_next; \
curelm->field.sle_next = curelm->field.sle_next->field.sle_next; \
_Q_INVALIDATE((elm)->field.sle_next); \
} \
} while (0)
} while (0)
/*
* List definitions.
*/
#define LIST_HEAD(name, type) \
struct name { \
struct name { \
struct type *lh_first; /* first element */ \
}
}
#define LIST_HEAD_INITIALIZER(head) \
{ NULL }
#define LIST_ENTRY(type) \
struct { \
struct { \
struct type *le_next; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
}
}
/*
* List access methods
@ -186,74 +191,77 @@ struct { \
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
#define LIST_FOREACH(var, head, field) \
for((var) = LIST_FIRST(head); \
(var)!= LIST_END(head); \
for ((var) = LIST_FIRST(head); \
(var) != LIST_END(head); \
(var) = LIST_NEXT(var, field))
/*
* List functions.
*/
#define LIST_INIT(head) do { \
#define LIST_INIT(head) \
do { \
LIST_FIRST(head) = LIST_END(head); \
} while (0)
} while (0)
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
#define LIST_INSERT_AFTER(listelm, elm, field) \
do { \
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
(listelm)->field.le_next->field.le_prev = \
&(elm)->field.le_next; \
(listelm)->field.le_next->field.le_prev = &(elm)->field.le_next; \
(listelm)->field.le_next = (elm); \
(elm)->field.le_prev = &(listelm)->field.le_next; \
} while (0)
} while (0)
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
#define LIST_INSERT_BEFORE(listelm, elm, field) \
do { \
(elm)->field.le_prev = (listelm)->field.le_prev; \
(elm)->field.le_next = (listelm); \
*(listelm)->field.le_prev = (elm); \
(listelm)->field.le_prev = &(elm)->field.le_next; \
} while (0)
} while (0)
#define LIST_INSERT_HEAD(head, elm, field) do { \
#define LIST_INSERT_HEAD(head, elm, field) \
do { \
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
(head)->lh_first->field.le_prev = &(elm)->field.le_next; \
(head)->lh_first = (elm); \
(elm)->field.le_prev = &(head)->lh_first; \
} while (0)
} while (0)
#define LIST_REMOVE(elm, field) do { \
#define LIST_REMOVE(elm, field) \
do { \
if ((elm)->field.le_next != NULL) \
(elm)->field.le_next->field.le_prev = \
(elm)->field.le_prev; \
(elm)->field.le_next->field.le_prev = (elm)->field.le_prev; \
*(elm)->field.le_prev = (elm)->field.le_next; \
_Q_INVALIDATE((elm)->field.le_prev); \
_Q_INVALIDATE((elm)->field.le_next); \
} while (0)
} while (0)
#define LIST_REPLACE(elm, elm2, field) do { \
#define LIST_REPLACE(elm, elm2, field) \
do { \
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
(elm2)->field.le_next->field.le_prev = \
&(elm2)->field.le_next; \
(elm2)->field.le_next->field.le_prev = &(elm2)->field.le_next; \
(elm2)->field.le_prev = (elm)->field.le_prev; \
*(elm2)->field.le_prev = (elm2); \
_Q_INVALIDATE((elm)->field.le_prev); \
_Q_INVALIDATE((elm)->field.le_next); \
} while (0)
} while (0)
/*
* Simple queue definitions.
*/
#define SIMPLEQ_HEAD(name, type) \
struct name { \
struct name { \
struct type *sqh_first; /* first element */ \
struct type **sqh_last; /* addr of last next element */ \
}
}
#define SIMPLEQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).sqh_first }
#define SIMPLEQ_ENTRY(type) \
struct { \
struct { \
struct type *sqe_next; /* next element */ \
}
}
/*
* Simple queue access methods.
@ -264,58 +272,63 @@ struct { \
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
#define SIMPLEQ_FOREACH(var, head, field) \
for((var) = SIMPLEQ_FIRST(head); \
for ((var) = SIMPLEQ_FIRST(head); \
(var) != SIMPLEQ_END(head); \
(var) = SIMPLEQ_NEXT(var, field))
/*
* Simple queue functions.
*/
#define SIMPLEQ_INIT(head) do { \
#define SIMPLEQ_INIT(head) \
do { \
(head)->sqh_first = NULL; \
(head)->sqh_last = &(head)->sqh_first; \
} while (0)
} while (0)
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
#define SIMPLEQ_INSERT_HEAD(head, elm, field) \
do { \
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
(head)->sqh_last = &(elm)->field.sqe_next; \
(head)->sqh_first = (elm); \
} while (0)
} while (0)
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
#define SIMPLEQ_INSERT_TAIL(head, elm, field) \
do { \
(elm)->field.sqe_next = NULL; \
*(head)->sqh_last = (elm); \
(head)->sqh_last = &(elm)->field.sqe_next; \
} while (0)
} while (0)
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) \
do { \
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL) \
(head)->sqh_last = &(elm)->field.sqe_next; \
(listelm)->field.sqe_next = (elm); \
} while (0)
} while (0)
#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
#define SIMPLEQ_REMOVE_HEAD(head, field) \
do { \
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
(head)->sqh_last = &(head)->sqh_first; \
} while (0)
} while (0)
/*
* Tail queue definitions.
*/
#define TAILQ_HEAD(name, type) \
struct name { \
struct name { \
struct type *tqh_first; /* first element */ \
struct type **tqh_last; /* addr of last next element */ \
}
}
#define TAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).tqh_first }
#define TAILQ_ENTRY(type) \
struct { \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
}
/*
* tail queue access methods
@ -332,82 +345,86 @@ struct { \
(TAILQ_FIRST(head) == TAILQ_END(head))
#define TAILQ_FOREACH(var, head, field) \
for((var) = TAILQ_FIRST(head); \
for ((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head); \
(var) = TAILQ_NEXT(var, field))
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
for((var) = TAILQ_LAST(head, headname); \
for ((var) = TAILQ_LAST(head, headname); \
(var) != TAILQ_END(head); \
(var) = TAILQ_PREV(var, headname, field))
/*
* Tail queue functions.
*/
#define TAILQ_INIT(head) do { \
#define TAILQ_INIT(head) \
do { \
(head)->tqh_first = NULL; \
(head)->tqh_last = &(head)->tqh_first; \
} while (0)
} while (0)
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
#define TAILQ_INSERT_HEAD(head, elm, field) \
do { \
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
(head)->tqh_first->field.tqe_prev = \
&(elm)->field.tqe_next; \
(head)->tqh_first->field.tqe_prev = &(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(head)->tqh_first = (elm); \
(elm)->field.tqe_prev = &(head)->tqh_first; \
} while (0)
} while (0)
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
#define TAILQ_INSERT_TAIL(head, elm, field) \
do { \
(elm)->field.tqe_next = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &(elm)->field.tqe_next; \
} while (0)
} while (0)
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
(elm)->field.tqe_next->field.tqe_prev = \
&(elm)->field.tqe_next; \
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) \
do { \
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL) \
(elm)->field.tqe_next->field.tqe_prev = &(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(listelm)->field.tqe_next = (elm); \
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
} while (0)
} while (0)
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
#define TAILQ_INSERT_BEFORE(listelm, elm, field) \
do { \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
(elm)->field.tqe_next = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
} while (0)
} while (0)
#define TAILQ_REMOVE(head, elm, field) do { \
#define TAILQ_REMOVE(head, elm, field) \
do { \
if (((elm)->field.tqe_next) != NULL) \
(elm)->field.tqe_next->field.tqe_prev = \
(elm)->field.tqe_prev; \
(elm)->field.tqe_next->field.tqe_prev = (elm)->field.tqe_prev; \
else \
(head)->tqh_last = (elm)->field.tqe_prev; \
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
_Q_INVALIDATE((elm)->field.tqe_prev); \
_Q_INVALIDATE((elm)->field.tqe_next); \
} while (0)
} while (0)
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
#define TAILQ_REPLACE(head, elm, elm2, field) \
do { \
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
(elm2)->field.tqe_next->field.tqe_prev = \
&(elm2)->field.tqe_next; \
(elm2)->field.tqe_next->field.tqe_prev = &(elm2)->field.tqe_next; \
else \
(head)->tqh_last = &(elm2)->field.tqe_next; \
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
*(elm2)->field.tqe_prev = (elm2); \
_Q_INVALIDATE((elm)->field.tqe_prev); \
_Q_INVALIDATE((elm)->field.tqe_next); \
} while (0)
} while (0)
/* Swaps two consecutive elements. 'second' *MUST* follow 'first' */
#define TAILQ_SWAP(first, second, head, field) do { \
#define TAILQ_SWAP(first, second, head, field) \
do { \
*((first)->field.tqe_prev) = (second); \
(second)->field.tqe_prev = (first)->field.tqe_prev; \
(first)->field.tqe_prev = &((second)->field.tqe_next); \
@ -417,25 +434,25 @@ struct { \
(second)->field.tqe_next = first; \
if ((head)->tqh_last == &((second)->field.tqe_next)) \
(head)->tqh_last = &((first)->field.tqe_next); \
} while (0)
} while (0)
/*
* Circular queue definitions.
*/
#define CIRCLEQ_HEAD(name, type) \
struct name { \
struct name { \
struct type *cqh_first; /* first element */ \
struct type *cqh_last; /* last element */ \
}
}
#define CIRCLEQ_HEAD_INITIALIZER(head) \
{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
#define CIRCLEQ_ENTRY(type) \
struct { \
struct { \
struct type *cqe_next; /* next element */ \
struct type *cqe_prev; /* previous element */ \
}
}
/*
* Circular queue access methods
@ -449,24 +466,26 @@ struct { \
(CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
#define CIRCLEQ_FOREACH(var, head, field) \
for((var) = CIRCLEQ_FIRST(head); \
for ((var) = CIRCLEQ_FIRST(head); \
(var) != CIRCLEQ_END(head); \
(var) = CIRCLEQ_NEXT(var, field))
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
for((var) = CIRCLEQ_LAST(head); \
for ((var) = CIRCLEQ_LAST(head); \
(var) != CIRCLEQ_END(head); \
(var) = CIRCLEQ_PREV(var, field))
/*
* Circular queue functions.
*/
#define CIRCLEQ_INIT(head) do { \
#define CIRCLEQ_INIT(head) \
do { \
(head)->cqh_first = CIRCLEQ_END(head); \
(head)->cqh_last = CIRCLEQ_END(head); \
} while (0)
} while (0)
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) \
do { \
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
(elm)->field.cqe_prev = (listelm); \
if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
@ -474,9 +493,10 @@ struct { \
else \
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
(listelm)->field.cqe_next = (elm); \
} while (0)
} while (0)
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) \
do { \
(elm)->field.cqe_next = (listelm); \
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
@ -484,9 +504,10 @@ struct { \
else \
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
(listelm)->field.cqe_prev = (elm); \
} while (0)
} while (0)
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
#define CIRCLEQ_INSERT_HEAD(head, elm, field) \
do { \
(elm)->field.cqe_next = (head)->cqh_first; \
(elm)->field.cqe_prev = CIRCLEQ_END(head); \
if ((head)->cqh_last == CIRCLEQ_END(head)) \
@ -494,9 +515,10 @@ struct { \
else \
(head)->cqh_first->field.cqe_prev = (elm); \
(head)->cqh_first = (elm); \
} while (0)
} while (0)
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
#define CIRCLEQ_INSERT_TAIL(head, elm, field) \
do { \
(elm)->field.cqe_next = CIRCLEQ_END(head); \
(elm)->field.cqe_prev = (head)->cqh_last; \
if ((head)->cqh_first == CIRCLEQ_END(head)) \
@ -504,34 +526,32 @@ struct { \
else \
(head)->cqh_last->field.cqe_next = (elm); \
(head)->cqh_last = (elm); \
} while (0)
} while (0)
#define CIRCLEQ_REMOVE(head, elm, field) do { \
#define CIRCLEQ_REMOVE(head, elm, field) \
do { \
if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm)->field.cqe_prev; \
else \
(elm)->field.cqe_next->field.cqe_prev = \
(elm)->field.cqe_prev; \
(elm)->field.cqe_next->field.cqe_prev = (elm)->field.cqe_prev; \
if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm)->field.cqe_next; \
else \
(elm)->field.cqe_prev->field.cqe_next = \
(elm)->field.cqe_next; \
(elm)->field.cqe_prev->field.cqe_next = (elm)->field.cqe_next; \
_Q_INVALIDATE((elm)->field.cqe_prev); \
_Q_INVALIDATE((elm)->field.cqe_next); \
} while (0)
} while (0)
#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
CIRCLEQ_END(head)) \
#define CIRCLEQ_REPLACE(head, elm, elm2, field) \
do { \
if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm2); \
else \
(elm2)->field.cqe_next->field.cqe_prev = (elm2); \
if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
CIRCLEQ_END(head)) \
if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm2); \
else \
(elm2)->field.cqe_prev->field.cqe_next = (elm2); \
_Q_INVALIDATE((elm)->field.cqe_prev); \
_Q_INVALIDATE((elm)->field.cqe_next); \
} while (0)
} while (0)

View File

@ -68,15 +68,15 @@ extern "C" {
#ifndef _sd_printf_attr_
#if __GNUC__ >= 4
#define _sd_printf_attr_(a,b) __attribute__ ((format (printf, a, b)))
#define _sd_printf_attr_(a, b) __attribute__((format(printf, a, b)))
#else
#define _sd_printf_attr_(a,b)
#define _sd_printf_attr_(a, b)
#endif
#endif
#ifndef _sd_hidden_
#if (__GNUC__ >= 4) && !defined(SD_EXPORT_SYMBOLS)
#define _sd_hidden_ __attribute__ ((visibility("hidden")))
#define _sd_hidden_ __attribute__((visibility("hidden")))
#else
#define _sd_hidden_
#endif
@ -242,7 +242,7 @@ int sd_notify(int unset_environment, const char *state) _sd_hidden_;
See sd_notifyf(3) for more information.
*/
int sd_notifyf(int unset_environment, const char *format, ...) _sd_printf_attr_(2,3) _sd_hidden_;
int sd_notifyf(int unset_environment, const char *format, ...) _sd_printf_attr_(2, 3) _sd_hidden_;
/*
Returns > 0 if the system was booted with systemd. Returns < 0 on

View File

@ -21,8 +21,8 @@
* (immediately), the application is reparented to init (process-id 1), which
* correctly handles childs, so we dont have to do it :-).
*
* The shell is determined by looking for the SHELL environment variable. If
* it does not exist, /bin/sh is used.
* The shell used to start applications is the system's bourne shell (i.e.,
* /bin/sh).
*
* The no_startup_id flag determines whether a startup notification context
* (and ID) should be created, which is the default and encouraged behavior.

View File

@ -15,22 +15,24 @@
#include "data.h"
#define die(...) errx(EXIT_FAILURE, __VA_ARGS__);
#define exit_if_null(pointer, ...) { if (pointer == NULL) die(__VA_ARGS__); }
#define exit_if_null(pointer, ...) \
{ \
if (pointer == NULL) \
die(__VA_ARGS__); \
}
#define STARTS_WITH(string, needle) (strncasecmp(string, needle, strlen(needle)) == 0)
#define CIRCLEQ_NEXT_OR_NULL(head, elm, field) (CIRCLEQ_NEXT(elm, field) != CIRCLEQ_END(head) ? \
CIRCLEQ_NEXT(elm, field) : NULL)
#define CIRCLEQ_PREV_OR_NULL(head, elm, field) (CIRCLEQ_PREV(elm, field) != CIRCLEQ_END(head) ? \
CIRCLEQ_PREV(elm, field) : NULL)
#define CIRCLEQ_NEXT_OR_NULL(head, elm, field) (CIRCLEQ_NEXT(elm, field) != CIRCLEQ_END(head) ? CIRCLEQ_NEXT(elm, field) : NULL)
#define CIRCLEQ_PREV_OR_NULL(head, elm, field) (CIRCLEQ_PREV(elm, field) != CIRCLEQ_END(head) ? CIRCLEQ_PREV(elm, field) : NULL)
#define FOR_TABLE(workspace) \
for (int cols = 0; cols < (workspace)->cols; cols++) \
for (int rows = 0; rows < (workspace)->rows; rows++)
#define NODES_FOREACH(head) \
for (Con *child = (Con*)-1; (child == (Con*)-1) && ((child = 0), true);) \
for (Con *child = (Con *)-1; (child == (Con *)-1) && ((child = 0), true);) \
TAILQ_FOREACH(child, &((head)->nodes_head), nodes)
#define NODES_FOREACH_REVERSE(head) \
for (Con *child = (Con*)-1; (child == (Con*)-1) && ((child = 0), true);) \
for (Con *child = (Con *)-1; (child == (Con *)-1) && ((child = 0), true);) \
TAILQ_FOREACH_REVERSE(child, &((head)->nodes_head), nodes_head, nodes)
/* greps the ->nodes of the given head and returns the first node that matches the given condition */
@ -43,15 +45,15 @@
break; \
}
#define FREE(pointer) do { \
#define FREE(pointer) \
do { \
if (pointer != NULL) { \
free(pointer); \
pointer = NULL; \
} \
} \
while (0)
} while (0)
#define CALL(obj, member, ...) obj->member(obj, ## __VA_ARGS__)
#define CALL(obj, member, ...) obj->member(obj, ##__VA_ARGS__)
int min(int a, int b);
int max(int a, int b);

View File

@ -68,25 +68,25 @@ void workspace_show_by_name(const char *num);
* Returns the next workspace.
*
*/
Con* workspace_next(void);
Con *workspace_next(void);
/**
* Returns the previous workspace.
*
*/
Con* workspace_prev(void);
Con *workspace_prev(void);
/**
* Returns the next workspace on the same output
*
*/
Con* workspace_next_on_output(void);
Con *workspace_next_on_output(void);
/**
* Returns the previous workspace on the same output
*
*/
Con* workspace_prev_on_output(void);
Con *workspace_prev_on_output(void);
/**
* Focuses the previously focused workspace.
@ -100,7 +100,6 @@ void workspace_back_and_forth(void);
*/
Con *workspace_back_and_forth_get(void);
#if 0
/**
* Assigns the given workspace to the given screen by correctly updating its

View File

@ -53,7 +53,7 @@
XCB_EVENT_MASK_PROPERTY_CHANGE | \
XCB_EVENT_MASK_ENTER_WINDOW)
#define xmacro(atom) xcb_atom_t A_ ## atom;
#define xmacro(atom) xcb_atom_t A_##atom;
#include "atoms.xmacro"
#undef xmacro
@ -108,7 +108,6 @@ void xcb_raise_window(xcb_connection_t *conn, xcb_window_t window);
*/
void xcb_set_window_rect(xcb_connection_t *conn, xcb_window_t window, Rect r);
bool xcb_reply_contains_atom(xcb_get_property_reply_t *prop, xcb_atom_t atom);
/**

View File

@ -14,8 +14,8 @@
#include <yajl/yajl_version.h>
/* Shorter names for all those yajl_gen_* functions */
#define y(x, ...) yajl_gen_ ## x (gen, ##__VA_ARGS__)
#define ystr(str) yajl_gen_string(gen, (unsigned char*)str, strlen(str))
#define y(x, ...) yajl_gen_##x(gen, ##__VA_ARGS__)
#define ystr(str) yajl_gen_string(gen, (unsigned char *)str, strlen(str))
#define ygenalloc() yajl_gen_alloc(NULL)
#define yalloc(callbacks, client) yajl_alloc(callbacks, NULL, client)

View File

@ -1,3 +1,10 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* © 2009-2014 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
#include "libi3.h"
#include <math.h>
@ -12,5 +19,13 @@ extern xcb_screen_t *root_screen;
int logical_px(const int logical) {
const int dpi = (double)root_screen->height_in_pixels * 25.4 /
(double)root_screen->height_in_millimeters;
/* There are many misconfigurations out there, i.e. systems with screens
* whose dpi is in fact higher than 96 dpi, but not significantly higher,
* so software was never adapted. We could tell people to reconfigure their
* systems to 96 dpi in order to get the behavior they expect/are used to,
* but since we can easily detect this case in code, lets do it for them.
*/
if ((dpi / 96.0) < 1.25)
return logical;
return ceil((dpi / 96.0) * logical);
}

View File

@ -102,7 +102,8 @@ static bool load_pango_font(i3Font *font, const char *desc) {
*
*/
static void draw_text_pango(const char *text, size_t text_len,
xcb_drawable_t drawable, int x, int y, int max_width) {
xcb_drawable_t drawable, int x, int y,
int max_width, bool is_markup) {
/* Create the Pango layout */
/* root_visual_type is cached in load_pango_font */
cairo_surface_t *surface = cairo_xcb_surface_create(conn, drawable,
@ -116,6 +117,9 @@ static void draw_text_pango(const char *text, size_t text_len,
pango_layout_set_wrap(layout, PANGO_WRAP_CHAR);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
if (is_markup)
pango_layout_set_markup(layout, text, text_len);
else
pango_layout_set_text(layout, text, text_len);
/* Do the drawing */
@ -135,7 +139,7 @@ static void draw_text_pango(const char *text, size_t text_len,
* Calculate the text width using Pango rendering.
*
*/
static int predict_text_width_pango(const char *text, size_t text_len) {
static int predict_text_width_pango(const char *text, size_t text_len, bool is_markup) {
/* Create a dummy Pango layout */
/* root_visual_type is cached in load_pango_font */
cairo_surface_t *surface = cairo_xcb_surface_create(conn, root_screen->root, root_visual_type, 1, 1);
@ -145,7 +149,12 @@ static int predict_text_width_pango(const char *text, size_t text_len) {
/* Get the font width */
gint width;
pango_layout_set_font_description(layout, savedFont->specific.pango_desc);
if (is_markup)
pango_layout_set_markup(layout, text, text_len);
else
pango_layout_set_text(layout, text, text_len);
pango_cairo_update_layout(cr, layout);
pango_layout_get_pixel_size(layout, &width, NULL);
@ -160,13 +169,23 @@ static int predict_text_width_pango(const char *text, size_t text_len) {
/*
* Loads a font for usage, also getting its metrics. If fallback is true,
* the fonts 'fixed' or '-misc-*' will be loaded instead of exiting.
* the fonts 'fixed' or '-misc-*' will be loaded instead of exiting. If any
* font was previously loaded, it will be freed.
*
*/
i3Font load_font(const char *pattern, const bool fallback) {
/* if any font was previously loaded, free it now */
free_font();
i3Font font;
font.type = FONT_TYPE_NONE;
/* No XCB connction, return early because we're just validating the
* configuration file. */
if (conn == NULL) {
return font;
}
#if PANGO_SUPPORT
/* Try to load a pango font if specified */
if (strlen(pattern) > strlen("pango:") && !strncmp(pattern, "pango:", strlen("pango:"))) {
@ -251,10 +270,15 @@ void set_font(i3Font *font) {
}
/*
* Frees the resources taken by the current font.
* Frees the resources taken by the current font. If no font was previously
* loaded, it simply returns.
*
*/
void free_font(void) {
/* if there is no saved font, simply return */
if (savedFont == NULL)
return;
free(savedFont->pattern);
switch (savedFont->type) {
case FONT_TYPE_NONE:
@ -277,6 +301,8 @@ void free_font(void) {
assert(false);
break;
}
savedFont = NULL;
}
/*
@ -366,7 +392,7 @@ void draw_text(i3String *text, xcb_drawable_t drawable,
case FONT_TYPE_PANGO:
/* Render the text using Pango */
draw_text_pango(i3string_as_utf8(text), i3string_get_num_bytes(text),
drawable, x, y, max_width);
drawable, x, y, max_width, i3string_is_markup(text));
return;
#endif
default:
@ -405,7 +431,7 @@ void draw_text_ascii(const char *text, xcb_drawable_t drawable,
case FONT_TYPE_PANGO:
/* Render the text using Pango */
draw_text_pango(text, strlen(text),
drawable, x, y, max_width);
drawable, x, y, max_width, false);
return;
#endif
default:
@ -501,7 +527,8 @@ int predict_text_width(i3String *text) {
#if PANGO_SUPPORT
case FONT_TYPE_PANGO:
/* Calculate extents using Pango */
return predict_text_width_pango(i3string_as_utf8(text), i3string_get_num_bytes(text));
return predict_text_width_pango(i3string_as_utf8(text), i3string_get_num_bytes(text),
i3string_is_markup(text));
#endif
default:
assert(false);

View File

@ -20,6 +20,7 @@ struct _i3String {
xcb_char2b_t *ucs2;
size_t num_glyphs;
size_t num_bytes;
bool is_markup;
};
/*
@ -39,6 +40,19 @@ i3String *i3string_from_utf8(const char *from_utf8) {
return str;
}
/*
* Build an i3String from an UTF-8 encoded string in Pango markup.
*
*/
i3String *i3string_from_markup(const char *from_markup) {
i3String *str = i3string_from_utf8(from_markup);
/* Set the markup flag */
str->is_markup = true;
return str;
}
/*
* Build an i3String from an UTF-8 encoded string with fixed length.
* To be used when no proper NUL-terminaison is available.
@ -59,6 +73,20 @@ i3String *i3string_from_utf8_with_length(const char *from_utf8, size_t num_bytes
return str;
}
/*
* Build an i3String from an UTF-8 encoded string in Pango markup with fixed
* length.
*
*/
i3String *i3string_from_markup_with_length(const char *from_markup, size_t num_bytes) {
i3String *str = i3string_from_utf8_with_length(from_markup, num_bytes);
/* set the markup flag */
str->is_markup = true;
return str;
}
/*
* Build an i3String from an UCS-2 encoded string.
* Returns the newly-allocated i3String.
@ -133,6 +161,13 @@ size_t i3string_get_num_bytes(i3String *str) {
return str->num_bytes;
}
/*
* Whether the given i3String is in Pango markup.
*/
bool i3string_is_markup(i3String *str) {
return str->is_markup;
}
/*
* Returns the number of glyphs in an i3String.
*

View File

@ -7,7 +7,7 @@ template::[header-declarations]
<refentrytitle>{mantitle}</refentrytitle>
<manvolnum>{manvolnum}</manvolnum>
<refmiscinfo class="source">i3</refmiscinfo>
<refmiscinfo class="version">4.8</refmiscinfo>
<refmiscinfo class="version">4.9</refmiscinfo>
<refmiscinfo class="manual">i3 Manual</refmiscinfo>
</refmeta>
<refnamediv>

View File

@ -22,6 +22,7 @@ is appropriate for the distribution.
It tries to start one of the following (in that order):
* $TERMINAL (this is a non-standard variable)
* x-terminal-emulator (only present on Debian and derivatives)
* urxvt
* rxvt
* terminator

View File

@ -230,7 +230,7 @@ bindsym Mod1+h split h
bindsym Mod1+v split v
# enter fullscreen mode for the focused container
bindsym Mod1+f fullscreen
bindsym Mod1+f fullscreen toggle
# change container layout (stacked, tabbed, default)
bindsym Mod1+s layout stacking

View File

@ -156,12 +156,28 @@ state KILL:
end
-> call cmd_kill($kill_mode)
# fullscreen enable|toggle [global]
# fullscreen disable
# fullscreen [global]
state FULLSCREEN:
fullscreen_mode = 'global'
-> call cmd_fullscreen($fullscreen_mode)
action = 'disable'
-> call cmd_fullscreen($action, "output")
action = 'enable', 'toggle'
-> FULLSCREEN_MODE
action = ''
-> FULLSCREEN_COMPAT
state FULLSCREEN_MODE:
mode = 'global'
-> call cmd_fullscreen($action, $mode)
end
-> call cmd_fullscreen($fullscreen_mode)
-> call cmd_fullscreen($action, "output")
state FULLSCREEN_COMPAT:
mode = 'global'
-> call cmd_fullscreen("toggle", $mode)
end
-> call cmd_fullscreen("toggle", "output")
# split v|h|vertical|horizontal
state SPLIT:

View File

@ -278,6 +278,8 @@ state FONT:
state BINDING:
release = '--release'
->
whole_window = '--whole-window'
->
modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Ctrl', 'Mode_switch', '$mod'
->
'+'
@ -288,8 +290,10 @@ state BINDING:
state BINDCOMMAND:
release = '--release'
->
whole_window = '--whole-window'
->
command = string
-> call cfg_binding($bindtype, $modifiers, $key, $release, $command)
-> call cfg_binding($bindtype, $modifiers, $key, $release, $whole_window, $command)
################################################################################
# Mode configuration
@ -333,8 +337,10 @@ state MODE_BINDING:
state MODE_BINDCOMMAND:
release = '--release'
->
whole_window = '--whole-window'
->
command = string
-> call cfg_mode_binding($bindtype, $modifiers, $key, $release, $command); MODE
-> call cfg_mode_binding($bindtype, $modifiers, $key, $release, $whole_window, $command); MODE
################################################################################
# Bar configuration (i3bar)
@ -358,6 +364,8 @@ state BAR:
'hidden_state' -> BAR_HIDDEN_STATE
'id' -> BAR_ID
'modifier' -> BAR_MODIFIER
'wheel_up_cmd' -> BAR_WHEEL_UP_CMD
'wheel_down_cmd' -> BAR_WHEEL_DOWN_CMD
'position' -> BAR_POSITION
'output' -> BAR_OUTPUT
'tray_output' -> BAR_TRAY_OUTPUT
@ -403,6 +411,14 @@ state BAR_MODIFIER:
modifier = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Control', 'Ctrl', 'Shift'
-> call cfg_bar_modifier($modifier); BAR
state BAR_WHEEL_UP_CMD:
command = string
-> call cfg_bar_wheel_up_cmd($command); BAR
state BAR_WHEEL_DOWN_CMD:
command = string
-> call cfg_bar_wheel_down_cmd($command); BAR
state BAR_POSITION:
position = 'top', 'bottom'
-> call cfg_bar_position($position); BAR
@ -412,7 +428,7 @@ state BAR_OUTPUT:
-> call cfg_bar_output($output); BAR
state BAR_TRAY_OUTPUT:
output = string
output = word
-> call cfg_bar_tray_output($output); BAR
state BAR_FONT:

View File

@ -23,7 +23,7 @@ void run_assignments(i3Window *window) {
/* Check if any assignments match */
Assignment *current;
TAILQ_FOREACH (current, &assignments, assignments) {
TAILQ_FOREACH(current, &assignments, assignments) {
if (!match_matches_window(&(current->match), window))
continue;
@ -72,7 +72,7 @@ void run_assignments(i3Window *window) {
Assignment *assignment_for(i3Window *window, int type) {
Assignment *assignment;
TAILQ_FOREACH (assignment, &assignments, assignments) {
TAILQ_FOREACH(assignment, &assignments, assignments) {
if ((type != A_ANY && (assignment->type & type) == 0) ||
!match_matches_window(&(assignment->match), window))
continue;

View File

@ -8,6 +8,8 @@
*/
#include "all.h"
#include <xkbcommon/xkbcommon.h>
pid_t command_error_nagbar_pid = -1;
/*
@ -25,7 +27,7 @@ static struct Mode *mode_from_name(const char *name) {
struct Mode *mode;
/* Try to find the mode in the list of modes and return it */
SLIST_FOREACH (mode, &modes, modes) {
SLIST_FOREACH(mode, &modes, modes) {
if (strcmp(mode->name, name) == 0)
return mode;
}
@ -47,10 +49,11 @@ static struct Mode *mode_from_name(const char *name) {
*
*/
Binding *configure_binding(const char *bindtype, const char *modifiers, const char *input_code,
const char *release, const char *command, const char *modename) {
const char *release, const char *whole_window, const char *command, const char *modename) {
Binding *new_binding = scalloc(sizeof(Binding));
DLOG("bindtype %s, modifiers %s, input code %s, release %s\n", bindtype, modifiers, input_code, release);
new_binding->release = (release != NULL ? B_UPON_KEYRELEASE : B_UPON_KEYPRESS);
new_binding->whole_window = (whole_window != NULL);
if (strcmp(bindtype, "bindsym") == 0) {
new_binding->input_type = (strncasecmp(input_code, "button", (sizeof("button") - 1)) == 0
? B_MOUSE
@ -104,7 +107,7 @@ static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint
*/
void grab_all_keys(xcb_connection_t *conn, bool bind_mode_switch) {
Binding *bind;
TAILQ_FOREACH (bind, bindings, bindings) {
TAILQ_FOREACH(bind, bindings, bindings) {
if (bind->input_type != B_KEYBOARD ||
(bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) == 0) ||
(!bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) != 0))
@ -133,7 +136,7 @@ static Binding *get_binding(uint16_t modifiers, bool is_release, uint16_t input_
if (!is_release) {
/* On a press event, we first reset all B_UPON_KEYRELEASE_IGNORE_MODS
* bindings back to B_UPON_KEYRELEASE */
TAILQ_FOREACH (bind, bindings, bindings) {
TAILQ_FOREACH(bind, bindings, bindings) {
if (bind->input_type != input_type)
continue;
if (bind->release == B_UPON_KEYRELEASE_IGNORE_MODS)
@ -141,7 +144,7 @@ static Binding *get_binding(uint16_t modifiers, bool is_release, uint16_t input_
}
}
TAILQ_FOREACH (bind, bindings, bindings) {
TAILQ_FOREACH(bind, bindings, bindings) {
/* First compare the modifiers (unless this is a
* B_UPON_KEYRELEASE_IGNORE_MODS binding and this is a KeyRelease
* event) */
@ -248,7 +251,7 @@ void translate_keysyms(void) {
min_keycode = xcb_get_setup(conn)->min_keycode;
max_keycode = xcb_get_setup(conn)->max_keycode;
TAILQ_FOREACH (bind, bindings, bindings) {
TAILQ_FOREACH(bind, bindings, bindings) {
if (bind->input_type == B_MOUSE) {
int button = atoi(bind->symbol + (sizeof("button") - 1));
bind->keycode = button;
@ -263,8 +266,8 @@ void translate_keysyms(void) {
continue;
/* We need to translate the symbol to a keycode */
keysym = XStringToKeysym(bind->symbol);
if (keysym == NoSymbol) {
keysym = xkb_keysym_from_name(bind->symbol, XKB_KEYSYM_NO_FLAGS);
if (keysym == XKB_KEY_NoSymbol) {
ELOG("Could not translate string to key symbol: \"%s\"\n",
bind->symbol);
continue;
@ -304,7 +307,7 @@ void switch_mode(const char *new_mode) {
DLOG("Switching to mode %s\n", new_mode);
SLIST_FOREACH (mode, &modes, modes) {
SLIST_FOREACH(mode, &modes, modes) {
if (strcasecmp(mode->name, new_mode) != 0)
continue;
@ -334,8 +337,8 @@ void switch_mode(const char *new_mode) {
*/
void check_for_duplicate_bindings(struct context *context) {
Binding *bind, *current;
TAILQ_FOREACH (current, bindings, bindings) {
TAILQ_FOREACH (bind, bindings, bindings) {
TAILQ_FOREACH(current, bindings, bindings) {
TAILQ_FOREACH(bind, bindings, bindings) {
/* Abort when we reach the current keybinding, only check the
* bindings before */
if (bind == current)
@ -379,18 +382,57 @@ void check_for_duplicate_bindings(struct context *context) {
}
/*
* Runs the given binding and handles parse errors. Returns a CommandResult for
* running the binding's command. Caller should render tree if
* needs_tree_render is true. Free with command_result_free().
* Creates a dynamically allocated copy of bind.
*/
static Binding *binding_copy(Binding *bind) {
Binding *ret = smalloc(sizeof(Binding));
*ret = *bind;
if (bind->symbol != NULL)
ret->symbol = strdup(bind->symbol);
if (bind->command != NULL)
ret->command = strdup(bind->command);
if (bind->translated_to != NULL) {
ret->translated_to = smalloc(sizeof(xcb_keycode_t) * bind->number_keycodes);
memcpy(ret->translated_to, bind->translated_to, sizeof(xcb_keycode_t) * bind->number_keycodes);
}
return ret;
}
/*
* Frees the binding. If bind is null, it simply returns.
*/
void binding_free(Binding *bind) {
if (bind == NULL) {
return;
}
FREE(bind->symbol);
FREE(bind->translated_to);
FREE(bind->command);
FREE(bind);
}
/*
* Runs the given binding and handles parse errors. If con is passed, it will
* execute the command binding with that container selected by criteria.
* Returns a CommandResult for running the binding's command. Caller should
* render tree if needs_tree_render is true. Free with command_result_free().
*
*/
CommandResult *run_binding(Binding *bind) {
/* We need to copy the command since “reload” may be part of the command,
* and then the memory that bind->command points to may not contain the
CommandResult *run_binding(Binding *bind, Con *con) {
char *command;
/* We need to copy the binding and command since “reload” may be part of
* the command, and then the memory that bind points to may not contain the
* same data anymore. */
char *command_copy = sstrdup(bind->command);
CommandResult *result = parse_command(command_copy, NULL);
free(command_copy);
if (con == NULL)
command = sstrdup(bind->command);
else
sasprintf(&command, "[con_id=\"%d\"] %s", con, bind->command);
Binding *bind_cp = binding_copy(bind);
CommandResult *result = parse_command(command, NULL);
free(command);
if (result->needs_tree_render)
tree_render();
@ -414,7 +456,8 @@ CommandResult *run_binding(Binding *bind) {
free(pageraction);
}
/* TODO: emit event for running a binding */
ipc_send_binding_event("run", bind_cp);
binding_free(bind_cp);
return result;
}

View File

@ -177,6 +177,34 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
if (con->parent->type == CT_DOCKAREA)
goto done;
/* if the user has bound an action to this click, it should override the
* default behavior. */
if (dest == CLICK_DECORATION || dest == CLICK_INSIDE) {
Binding *bind = get_binding_from_xcb_event((xcb_generic_event_t *)event);
/* clicks over a window decoration will always trigger the binding and
* clicks on the inside of the window will only trigger a binding if
* the --whole-window flag was given for the binding. */
if (bind && (dest == CLICK_DECORATION || bind->whole_window)) {
CommandResult *result = run_binding(bind, con);
/* ASYNC_POINTER eats the event */
xcb_allow_events(conn, XCB_ALLOW_ASYNC_POINTER, event->time);
xcb_flush(conn);
if (result->needs_tree_render)
tree_render();
command_result_free(result);
return 0;
}
}
/* There is no default behavior for button release events so we are done. */
if (event->response_type == XCB_BUTTON_RELEASE) {
goto done;
}
/* Any click in a workspace should focus that workspace. If the
* workspace is on another output we need to do a workspace_show in
* order for i3bar (and others) to notice the change in workspace. */
@ -191,7 +219,6 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
if (ws != focused_workspace)
workspace_show(ws);
focused_id = XCB_NONE;
/* get the floating con */
Con *floatingcon = con_inside_floating(con);
@ -300,6 +327,7 @@ done:
xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
xcb_flush(conn);
tree_render();
return 0;
}
@ -313,9 +341,10 @@ done:
*/
int handle_button_press(xcb_button_press_event_t *event) {
Con *con;
DLOG("Button %d pressed on window 0x%08x (child 0x%08x) at (%d, %d) (root %d, %d)\n",
event->state, event->event, event->child, event->event_x, event->event_y,
event->root_x, event->root_y);
DLOG("Button %d %s on window 0x%08x (child 0x%08x) at (%d, %d) (root %d, %d)\n",
event->state, (event->response_type == XCB_BUTTON_PRESS ? "press" : "release"),
event->event, event->child, event->event_x, event->event_y, event->root_x,
event->root_y);
last_timestamp = event->time;
@ -328,9 +357,9 @@ int handle_button_press(xcb_button_press_event_t *event) {
if (!(con = con_by_frame_id(event->event))) {
/* If the root window is clicked, find the relevant output from the
* click coordinates and focus the output's active workspace. */
if (event->event == root) {
if (event->event == root && event->response_type == XCB_BUTTON_PRESS) {
Con *output, *ws;
TAILQ_FOREACH (output, &(croot->nodes_head), nodes) {
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
if (con_is_internal(output) ||
!rect_contains(output->rect, event->event_x, event->event_y))
continue;
@ -358,7 +387,7 @@ int handle_button_press(xcb_button_press_event_t *event) {
/* Check if the click was on the decoration of a child */
Con *child;
TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
if (!rect_contains(child->deco_rect, event->event_x, event->event_y))
continue;

View File

@ -273,7 +273,7 @@ void cmd_criteria_init(I3_CMD) {
}
TAILQ_INIT(&owindows);
/* copy all_cons */
TAILQ_FOREACH (con, &all_cons, all_cons) {
TAILQ_FOREACH(con, &all_cons, all_cons) {
ow = smalloc(sizeof(owindow));
ow->con = con;
TAILQ_INSERT_TAIL(&owindows, ow, owindows);
@ -324,7 +324,7 @@ void cmd_criteria_match_windows(I3_CMD) {
}
}
TAILQ_FOREACH (current, &owindows, owindows) {
TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
}
}
@ -448,7 +448,7 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) {
return;
}
TAILQ_FOREACH (current, &owindows, owindows) {
TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
con_move_to_workspace(current->con, ws, true, false);
}
@ -475,7 +475,7 @@ void cmd_move_con_to_workspace_back_and_forth(I3_CMD) {
HANDLE_EMPTY_MATCH;
TAILQ_FOREACH (current, &owindows, owindows) {
TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
con_move_to_workspace(current->con, ws, true, false);
}
@ -513,13 +513,33 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
LOG("should move window to workspace %s\n", name);
/* get the workspace */
Con *ws = workspace_get(name, NULL);
Con *ws = NULL;
Con *output = NULL;
/* first look for a workspace with this name */
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
GREP_FIRST(ws, output_get_content(output), !strcasecmp(child->name, name));
}
/* if the name is plain digits, we interpret this as a "workspace number"
* command */
if (!ws && name_is_digits(name)) {
long parsed_num = ws_name_to_number(name);
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
GREP_FIRST(ws, output_get_content(output),
child->num == parsed_num);
}
}
/* if no workspace was found, make a new one */
if (!ws)
ws = workspace_get(name, NULL);
ws = maybe_auto_back_and_forth_workspace(ws);
HANDLE_EMPTY_MATCH;
TAILQ_FOREACH (current, &owindows, owindows) {
TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
con_move_to_workspace(current->con, ws, true, false);
}
@ -550,19 +570,16 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
/* get the workspace */
Con *output, *workspace = NULL;
char *endptr = NULL;
long parsed_num = strtol(which, &endptr, 10);
if (parsed_num == LONG_MIN ||
parsed_num == LONG_MAX ||
parsed_num < 0 ||
endptr == which) {
long parsed_num = ws_name_to_number(which);
if (parsed_num == -1) {
LOG("Could not parse initial part of \"%s\" as a number.\n", which);
// TODO: better error message
yerror("Could not parse number");
return;
}
TAILQ_FOREACH (output, &(croot->nodes_head), nodes)
TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
GREP_FIRST(workspace, output_get_content(output),
child->num == parsed_num);
@ -574,7 +591,7 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
HANDLE_EMPTY_MATCH;
TAILQ_FOREACH (current, &owindows, owindows) {
TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
con_move_to_workspace(current->con, workspace, true, false);
}
@ -728,7 +745,7 @@ static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, char *way, char
/* Ensure all the other children have a percentage set. */
Con *child;
TAILQ_FOREACH (child, &(current->parent->nodes_head), nodes) {
TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) {
LOG("child->percent = %f (child %p)\n", child->percent, child);
if (child->percent == 0.0)
child->percent = percentage;
@ -740,7 +757,7 @@ static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, char *way, char
LOG("subtract_percent = %f\n", subtract_percent);
/* Ensure that the new percentages are positive and greater than
* 0.05 to have a reasonable minimum size. */
TAILQ_FOREACH (child, &(current->parent->nodes_head), nodes) {
TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) {
if (child == current)
continue;
if (!definitelyGreaterThan(child->percent - subtract_percent, 0.05, DBL_EPSILON)) {
@ -758,7 +775,7 @@ static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, char *way, char
current->percent += ((double)ppt / 100.0);
LOG("current->percent after = %f\n", current->percent);
TAILQ_FOREACH (child, &(current->parent->nodes_head), nodes) {
TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) {
if (child == current)
continue;
child->percent -= subtract_percent;
@ -786,7 +803,7 @@ void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resiz
HANDLE_EMPTY_MATCH;
owindow *current;
TAILQ_FOREACH (current, &owindows, owindows) {
TAILQ_FOREACH(current, &owindows, owindows) {
/* Don't handle dock windows (issue #1201) */
if (current->con->window && current->con->window->dock) {
DLOG("This is a dock window. Not resizing (con = %p)\n)", current->con);
@ -823,7 +840,7 @@ void cmd_border(I3_CMD, char *border_style_str, char *border_width) {
HANDLE_EMPTY_MATCH;
TAILQ_FOREACH (current, &owindows, owindows) {
TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
int border_style = current->con->border_style;
char *end;
@ -927,7 +944,7 @@ void cmd_append_layout(I3_CMD, char *path) {
restore_open_placeholder_windows(parent);
if (content == JSON_CONTENT_WORKSPACE)
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"restored\"}");
ipc_send_workspace_event("restored", parent, NULL);
cmd_output->needs_tree_render = true;
}
@ -941,6 +958,12 @@ void cmd_workspace(I3_CMD, char *which) {
DLOG("which=%s\n", which);
if (con_get_fullscreen_con(croot, CF_GLOBAL)) {
LOG("Cannot switch workspace while in global fullscreen\n");
ysuccess(false);
return;
}
if (strcmp(which, "next") == 0)
ws = workspace_next();
else if (strcmp(which, "prev") == 0)
@ -969,20 +992,22 @@ void cmd_workspace(I3_CMD, char *which) {
void cmd_workspace_number(I3_CMD, char *which) {
Con *output, *workspace = NULL;
char *endptr = NULL;
long parsed_num = strtol(which, &endptr, 10);
if (parsed_num == LONG_MIN ||
parsed_num == LONG_MAX ||
parsed_num < 0 ||
endptr == which) {
LOG("Could not parse initial part of \"%s\" as a number.\n", which);
// TODO: better error message
yerror("Could not parse number");
if (con_get_fullscreen_con(croot, CF_GLOBAL)) {
LOG("Cannot switch workspace while in global fullscreen\n");
ysuccess(false);
return;
}
TAILQ_FOREACH (output, &(croot->nodes_head), nodes)
long parsed_num = ws_name_to_number(which);
if (parsed_num == -1) {
LOG("Could not parse initial part of \"%s\" as a number.\n", which);
// TODO: better error message
yerror("Could not parse number");
return;
}
TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
GREP_FIRST(workspace, output_get_content(output),
child->num == parsed_num);
@ -1007,6 +1032,12 @@ void cmd_workspace_number(I3_CMD, char *which) {
*
*/
void cmd_workspace_back_and_forth(I3_CMD) {
if (con_get_fullscreen_con(croot, CF_GLOBAL)) {
LOG("Cannot switch workspace while in global fullscreen\n");
ysuccess(false);
return;
}
workspace_back_and_forth();
cmd_output->needs_tree_render = true;
@ -1025,10 +1056,39 @@ void cmd_workspace_name(I3_CMD, char *name) {
return;
}
if (con_get_fullscreen_con(croot, CF_GLOBAL)) {
LOG("Cannot switch workspace while in global fullscreen\n");
ysuccess(false);
return;
}
DLOG("should switch to workspace %s\n", name);
if (maybe_back_and_forth(cmd_output, name))
return;
workspace_show_by_name(name);
Con *ws = NULL;
Con *output = NULL;
/* first look for a workspace with this name */
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
GREP_FIRST(ws, output_get_content(output), !strcasecmp(child->name, name));
}
/* if the name is only digits, we interpret this as a "workspace number"
* command */
if (!ws && name_is_digits(name)) {
long parsed_num = ws_name_to_number(name);
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
GREP_FIRST(ws, output_get_content(output),
child->num == parsed_num);
}
}
/* if no workspace was found, make a new one */
if (!ws)
ws = workspace_get(name, NULL);
workspace_show(ws);
cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply
@ -1043,7 +1103,7 @@ void cmd_mark(I3_CMD, char *mark) {
DLOG("Clearing all windows which have that mark first\n");
Con *con;
TAILQ_FOREACH (con, &all_cons, all_cons) {
TAILQ_FOREACH(con, &all_cons, all_cons) {
if (con->mark && strcmp(con->mark, mark) == 0)
FREE(con->mark);
}
@ -1053,7 +1113,7 @@ void cmd_mark(I3_CMD, char *mark) {
HANDLE_EMPTY_MATCH;
TAILQ_FOREACH (current, &owindows, owindows) {
TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
current->con->mark = sstrdup(mark);
}
@ -1070,13 +1130,13 @@ void cmd_mark(I3_CMD, char *mark) {
void cmd_unmark(I3_CMD, char *mark) {
if (mark == NULL) {
Con *con;
TAILQ_FOREACH (con, &all_cons, all_cons) {
TAILQ_FOREACH(con, &all_cons, all_cons) {
FREE(con->mark);
}
DLOG("removed all window marks");
} else {
Con *con;
TAILQ_FOREACH (con, &all_cons, all_cons) {
TAILQ_FOREACH(con, &all_cons, all_cons) {
if (con->mark && strcmp(con->mark, mark) == 0)
FREE(con->mark);
}
@ -1116,7 +1176,7 @@ void cmd_move_con_to_output(I3_CMD, char *name) {
Output *output;
// TODO: fix the handling of criteria
TAILQ_FOREACH (current, &owindows, owindows)
TAILQ_FOREACH(current, &owindows, owindows)
current_output = get_output_of_con(current->con);
assert(current_output != NULL);
@ -1147,7 +1207,7 @@ void cmd_move_con_to_output(I3_CMD, char *name) {
return;
}
TAILQ_FOREACH (current, &owindows, owindows) {
TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
con_move_to_workspace(current->con, ws, true, false);
}
@ -1168,7 +1228,7 @@ void cmd_floating(I3_CMD, char *floating_mode) {
HANDLE_EMPTY_MATCH;
TAILQ_FOREACH (current, &owindows, owindows) {
TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
if (strcmp(floating_mode, "toggle") == 0) {
DLOG("should toggle mode\n");
@ -1198,7 +1258,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
HANDLE_EMPTY_MATCH;
owindow *current;
TAILQ_FOREACH (current, &owindows, owindows) {
TAILQ_FOREACH(current, &owindows, owindows) {
Output *current_output = get_output_of_con(current->con);
if (!current_output) {
ELOG("Cannot get current output. This is a bug in i3.\n");
@ -1228,13 +1288,13 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
/* check if we can find a workspace assigned to this output */
bool used_assignment = false;
struct Workspace_Assignment *assignment;
TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) {
TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
if (strcmp(assignment->output, current_output->name) != 0)
continue;
/* check if this workspace is already attached to the tree */
Con *workspace = NULL, *out;
TAILQ_FOREACH (out, &(croot->nodes_head), nodes)
TAILQ_FOREACH(out, &(croot->nodes_head), nodes)
GREP_FIRST(workspace, output_get_content(out),
!strcasecmp(child->name, assignment->name));
if (workspace != NULL)
@ -1253,7 +1313,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
create_workspace_on_output(current_output, ws->parent);
/* notify the IPC listeners */
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
ipc_send_workspace_event("init", ws, NULL);
}
DLOG("Detaching\n");
@ -1271,10 +1331,10 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
/* fix the coordinates of the floating containers */
Con *floating_con;
TAILQ_FOREACH (floating_con, &(ws->floating_head), floating_windows)
TAILQ_FOREACH(floating_con, &(ws->floating_head), floating_windows)
floating_fix_coordinates(floating_con, &(old_content->rect), &(content->rect));
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"move\"}");
ipc_send_workspace_event("move", ws, NULL);
if (workspace_was_visible) {
/* Focus the moved workspace on the destination output. */
workspace_show(ws);
@ -1285,7 +1345,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
* focus order/number of other workspaces on the output.
* Instead, we loop through the available workspaces and only work with
* previously_visible_ws if we still find it. */
TAILQ_FOREACH (ws, &(content->nodes_head), nodes) {
TAILQ_FOREACH(ws, &(content->nodes_head), nodes) {
if (ws != previously_visible_ws)
continue;
@ -1313,7 +1373,7 @@ void cmd_split(I3_CMD, char *direction) {
if (match_is_empty(current_match))
tree_split(focused, (direction[0] == 'v' ? VERT : HORIZ));
else {
TAILQ_FOREACH (current, &owindows, owindows) {
TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
tree_split(current->con, (direction[0] == 'v' ? VERT : HORIZ));
}
@ -1350,7 +1410,7 @@ void cmd_kill(I3_CMD, char *kill_mode_str) {
if (match_is_empty(current_match))
tree_close_con(kill_mode);
else {
TAILQ_FOREACH (current, &owindows, owindows) {
TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
tree_close(current->con, kill_mode, false, false);
}
@ -1418,7 +1478,7 @@ void cmd_focus_window_mode(I3_CMD, char *window_mode) {
else
window_mode = "floating";
}
TAILQ_FOREACH (current, &(ws->focus_head), focused) {
TAILQ_FOREACH(current, &(ws->focus_head), focused) {
if ((strcmp(window_mode, "floating") == 0 && current->type != CT_FLOATING_CON) ||
(strcmp(window_mode, "tiling") == 0 && current->type == CT_FLOATING_CON))
continue;
@ -1480,7 +1540,7 @@ void cmd_focus(I3_CMD) {
Con *__i3_scratch = workspace_get("__i3_scratch", NULL);
int count = 0;
owindow *current;
TAILQ_FOREACH (current, &owindows, owindows) {
TAILQ_FOREACH(current, &owindows, owindows) {
Con *ws = con_get_workspace(current->con);
/* If no workspace could be found, this was a dock window.
* Just skip it, you cannot focus dock windows. */
@ -1538,20 +1598,26 @@ void cmd_focus(I3_CMD) {
}
/*
* Implementation of 'fullscreen [global]'.
* Implementation of 'fullscreen enable|toggle [global]' and
* 'fullscreen disable'
*
*/
void cmd_fullscreen(I3_CMD, char *fullscreen_mode) {
if (fullscreen_mode == NULL)
fullscreen_mode = "output";
DLOG("toggling fullscreen, mode = %s\n", fullscreen_mode);
void cmd_fullscreen(I3_CMD, char *action, char *fullscreen_mode) {
fullscreen_mode_t mode = strcmp(fullscreen_mode, "global") == 0 ? CF_GLOBAL : CF_OUTPUT;
DLOG("%s fullscreen, mode = %s\n", action, fullscreen_mode);
owindow *current;
HANDLE_EMPTY_MATCH;
TAILQ_FOREACH (current, &owindows, owindows) {
TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
con_toggle_fullscreen(current->con, (strcmp(fullscreen_mode, "global") == 0 ? CF_GLOBAL : CF_OUTPUT));
if (strcmp(action, "toggle") == 0) {
con_toggle_fullscreen(current->con, mode);
} else if (strcmp(action, "enable") == 0) {
con_enable_fullscreen(current->con, mode);
} else if (strcmp(action, "disable") == 0) {
con_disable_fullscreen(current->con);
}
}
cmd_output->needs_tree_render = true;
@ -1567,11 +1633,16 @@ void cmd_move_direction(I3_CMD, char *direction, char *move_px) {
// TODO: We could either handle this in the parser itself as a separate token (and make the stack typed) or we need a better way to convert a string to a number with error checking
int px = atoi(move_px);
/* TODO: make 'move' work with criteria. */
owindow *current;
HANDLE_EMPTY_MATCH;
Con *initially_focused = focused;
TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("moving in direction %s, px %s\n", direction, move_px);
if (con_is_floating(focused)) {
if (con_is_floating(current->con)) {
DLOG("floating move with %d pixels\n", px);
Rect newrect = focused->parent->rect;
Rect newrect = current->con->parent->rect;
if (strcmp(direction, "left") == 0) {
newrect.x -= px;
} else if (strcmp(direction, "right") == 0) {
@ -1581,11 +1652,16 @@ void cmd_move_direction(I3_CMD, char *direction, char *move_px) {
} else if (strcmp(direction, "down") == 0) {
newrect.y += px;
}
floating_reposition(focused->parent, newrect);
floating_reposition(current->con->parent, newrect);
} else {
tree_move((strcmp(direction, "right") == 0 ? D_RIGHT : (strcmp(direction, "left") == 0 ? D_LEFT : (strcmp(direction, "up") == 0 ? D_UP : D_DOWN))));
tree_move(current->con, (strcmp(direction, "right") == 0 ? D_RIGHT : (strcmp(direction, "left") == 0 ? D_LEFT : (strcmp(direction, "up") == 0 ? D_UP : D_DOWN))));
cmd_output->needs_tree_render = true;
}
}
/* the move command should not disturb focus */
if (focused != initially_focused)
con_focus(initially_focused);
// XXX: default reply for now, make this a better reply
ysuccess(true);
@ -1622,7 +1698,7 @@ void cmd_layout(I3_CMD, char *layout_str) {
if (match_is_empty(current_match))
con_set_layout(focused, layout);
else {
TAILQ_FOREACH (current, &owindows, owindows) {
TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
con_set_layout(current->con, layout);
}
@ -1649,7 +1725,7 @@ void cmd_layout_toggle(I3_CMD, char *toggle_mode) {
if (match_is_empty(current_match))
con_toggle_layout(focused, toggle_mode);
else {
TAILQ_FOREACH (current, &owindows, owindows) {
TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
con_toggle_layout(current->con, toggle_mode);
}
@ -1685,7 +1761,7 @@ void cmd_reload(I3_CMD) {
load_configuration(conn, NULL, true);
x_set_i3_atoms();
/* Send an IPC event just in case the ws names have changed */
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"reload\"}");
ipc_send_workspace_event("reload", NULL, NULL);
/* Send an update event for the barconfig just in case it has changed */
update_barconfig();
@ -1745,7 +1821,7 @@ void cmd_focus_output(I3_CMD, char *name) {
Output *current_output = NULL;
Output *output;
TAILQ_FOREACH (current, &owindows, owindows)
TAILQ_FOREACH(current, &owindows, owindows)
current_output = get_output_of_con(current->con);
assert(current_output != NULL);
@ -1779,33 +1855,45 @@ void cmd_focus_output(I3_CMD, char *name) {
void cmd_move_window_to_position(I3_CMD, char *method, char *cx, char *cy) {
int x = atoi(cx);
int y = atoi(cy);
bool has_error = false;
if (!con_is_floating(focused)) {
owindow *current;
HANDLE_EMPTY_MATCH;
TAILQ_FOREACH(current, &owindows, owindows) {
if (!con_is_floating(current->con)) {
ELOG("Cannot change position. The window/container is not floating\n");
yerror("Cannot change position. The window/container is not floating.");
return;
if (!has_error) {
yerror("Cannot change position of a window/container because it is not floating.");
has_error = true;
}
continue;
}
if (strcmp(method, "absolute") == 0) {
focused->parent->rect.x = x;
focused->parent->rect.y = y;
current->con->parent->rect.x = x;
current->con->parent->rect.y = y;
DLOG("moving to absolute position %d %d\n", x, y);
floating_maybe_reassign_ws(focused->parent);
floating_maybe_reassign_ws(current->con->parent);
cmd_output->needs_tree_render = true;
}
if (strcmp(method, "position") == 0) {
Rect newrect = focused->parent->rect;
Rect newrect = current->con->parent->rect;
DLOG("moving to position %d %d\n", x, y);
newrect.x = x;
newrect.y = y;
floating_reposition(focused->parent, newrect);
floating_reposition(current->con->parent, newrect);
}
}
// XXX: default reply for now, make this a better reply
if (!has_error)
ysuccess(true);
}
@ -1856,7 +1944,7 @@ void cmd_move_scratchpad(I3_CMD) {
HANDLE_EMPTY_MATCH;
TAILQ_FOREACH (current, &owindows, owindows) {
TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
scratchpad_move(current->con);
}
@ -1877,7 +1965,7 @@ void cmd_scratchpad_show(I3_CMD) {
if (match_is_empty(current_match)) {
scratchpad_show(NULL);
} else {
TAILQ_FOREACH (current, &owindows, owindows) {
TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
scratchpad_show(current->con);
}
@ -1906,7 +1994,7 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
Con *output, *workspace = NULL;
if (old_name) {
TAILQ_FOREACH (output, &(croot->nodes_head), nodes)
TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
GREP_FIRST(workspace, output_get_content(output),
!strcasecmp(child->name, old_name));
} else {
@ -1922,7 +2010,7 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
}
Con *check_dest = NULL;
TAILQ_FOREACH (output, &(croot->nodes_head), nodes)
TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
GREP_FIRST(check_dest, output_get_content(output),
!strcasecmp(child->name, new_name));
@ -1937,15 +2025,8 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
/* Change the name and try to parse it as a number. */
FREE(workspace->name);
workspace->name = sstrdup(new_name);
char *endptr = NULL;
long parsed_num = strtol(new_name, &endptr, 10);
if (parsed_num == LONG_MIN ||
parsed_num == LONG_MAX ||
parsed_num < 0 ||
endptr == new_name)
workspace->num = -1;
else
workspace->num = parsed_num;
workspace->num = ws_name_to_number(new_name);
LOG("num = %d\n", workspace->num);
/* By re-attaching, the sort order will be correct afterwards. */
@ -1959,7 +2040,10 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
cmd_output->needs_tree_render = true;
ysuccess(true);
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"rename\"}");
ipc_send_workspace_event("rename", workspace, NULL);
ewmh_update_desktop_names();
ewmh_update_desktop_viewport();
ewmh_update_current_desktop();
}
/*
@ -1984,7 +2068,7 @@ bool cmd_bar_mode(char *bar_mode, char *bar_id) {
bool changed_sth = false;
Barconfig *current = NULL;
TAILQ_FOREACH (current, &barconfigs, configs) {
TAILQ_FOREACH(current, &barconfigs, configs) {
if (bar_id && strcmp(current->id, bar_id) != 0)
continue;
@ -2027,7 +2111,7 @@ bool cmd_bar_hidden_state(char *bar_hidden_state, char *bar_id) {
bool changed_sth = false;
Barconfig *current = NULL;
TAILQ_FOREACH (current, &barconfigs, configs) {
TAILQ_FOREACH(current, &barconfigs, configs) {
if (bar_id && strcmp(current->id, bar_id) != 0)
continue;

View File

@ -204,6 +204,61 @@ static void next_state(const cmdp_token *token) {
}
}
/*
* Parses a string (or word, if as_word is true). Extracted out of
* parse_command so that it can be used in src/workspace.c for interpreting
* workspace commands.
*
*/
char *parse_string(const char **walk, bool as_word) {
const char *beginning = *walk;
/* Handle quoted strings (or words). */
if (**walk == '"') {
beginning++;
(*walk)++;
while (**walk != '\0' && (**walk != '"' || *(*walk - 1) == '\\'))
(*walk)++;
} else {
if (!as_word) {
/* For a string (starting with 's'), the delimiters are
* comma (,) and semicolon (;) which introduce a new
* operation or command, respectively. Also, newlines
* end a command. */
while (**walk != ';' && **walk != ',' &&
**walk != '\0' && **walk != '\r' &&
**walk != '\n')
(*walk)++;
} else {
/* For a word, the delimiters are white space (' ' or
* '\t'), closing square bracket (]), comma (,) and
* semicolon (;). */
while (**walk != ' ' && **walk != '\t' &&
**walk != ']' && **walk != ',' &&
**walk != ';' && **walk != '\r' &&
**walk != '\n' && **walk != '\0')
(*walk)++;
}
}
if (*walk == beginning)
return NULL;
char *str = scalloc(*walk - beginning + 1);
/* We copy manually to handle escaping of characters. */
int inpos, outpos;
for (inpos = 0, outpos = 0;
inpos < (*walk - beginning);
inpos++, outpos++) {
/* We only handle escaped double quotes to not break
* backwards compatibility with people using \w in
* regular expressions etc. */
if (beginning[inpos] == '\\' && beginning[inpos + 1] == '"')
inpos++;
str[outpos] = beginning[inpos];
}
return str;
}
/*
* Parses and executes the given command. If a caller-allocated yajl_gen is
* passed, a json reply will be generated in the format specified by the ipc
@ -262,48 +317,8 @@ CommandResult *parse_command(const char *input, yajl_gen gen) {
if (strcmp(token->name, "string") == 0 ||
strcmp(token->name, "word") == 0) {
const char *beginning = walk;
/* Handle quoted strings (or words). */
if (*walk == '"') {
beginning++;
walk++;
while (*walk != '\0' && (*walk != '"' || *(walk - 1) == '\\'))
walk++;
} else {
if (token->name[0] == 's') {
/* For a string (starting with 's'), the delimiters are
* comma (,) and semicolon (;) which introduce a new
* operation or command, respectively. Also, newlines
* end a command. */
while (*walk != ';' && *walk != ',' &&
*walk != '\0' && *walk != '\r' &&
*walk != '\n')
walk++;
} else {
/* For a word, the delimiters are white space (' ' or
* '\t'), closing square bracket (]), comma (,) and
* semicolon (;). */
while (*walk != ' ' && *walk != '\t' &&
*walk != ']' && *walk != ',' &&
*walk != ';' && *walk != '\r' &&
*walk != '\n' && *walk != '\0')
walk++;
}
}
if (walk != beginning) {
char *str = scalloc(walk - beginning + 1);
/* We copy manually to handle escaping of characters. */
int inpos, outpos;
for (inpos = 0, outpos = 0;
inpos < (walk - beginning);
inpos++, outpos++) {
/* We only handle escaped double quotes to not break
* backwards compatibility with people using \w in
* regular expressions etc. */
if (beginning[inpos] == '\\' && beginning[inpos + 1] == '"')
inpos++;
str[outpos] = beginning[inpos];
}
char *str = parse_string(&walk, (token->name[0] != 's'));
if (str != NULL) {
if (token->identifier)
push_string(token->identifier, str);
/* If we are at the end of a quoted string, skip the ending

199
src/con.c
View File

@ -12,18 +12,7 @@
*
*/
#include "all.h"
char *colors[] = {
"#ff0000",
"#00FF00",
"#0000FF",
"#ff00ff",
"#00ffff",
"#ffff00",
"#aa0000",
"#00aa00",
"#0000aa",
"#aa00aa"};
#include "yajl_utils.h"
static void con_on_remove_child(Con *con);
@ -31,7 +20,7 @@ static void con_on_remove_child(Con *con);
* force parent split containers to be redrawn
*
*/
static void con_force_split_parents_redraw(Con *con) {
void con_force_split_parents_redraw(Con *con) {
Con *parent = con;
while (parent && parent->type != CT_WORKSPACE && parent->type != CT_DOCKAREA) {
@ -59,16 +48,7 @@ Con *con_new_skeleton(Con *parent, i3Window *window) {
new->depth = window->depth;
else
new->depth = XCB_COPY_FROM_PARENT;
static int cnt = 0;
DLOG("opening window %d\n", cnt);
/* TODO: remove window coloring after test-phase */
DLOG("color %s\n", colors[cnt]);
new->name = strdup(colors[cnt]);
//uint32_t cp = get_colorpixel(colors[cnt]);
cnt++;
if ((cnt % (sizeof(colors) / sizeof(char *))) == 0)
cnt = 0;
DLOG("opening window\n");
TAILQ_INIT(&(new->floating_head));
TAILQ_INIT(&(new->nodes_head));
@ -142,7 +122,7 @@ void con_attach(Con *con, Con *parent, bool ignore_focus) {
} else {
if (!ignore_focus) {
/* Get the first tiling container in focus stack */
TAILQ_FOREACH (loop, &(parent->focus_head), focused) {
TAILQ_FOREACH(loop, &(parent->focus_head), focused) {
if (loop->type == CT_FLOATING_CON)
continue;
current = loop;
@ -231,6 +211,7 @@ void con_focus(Con *con) {
con->urgent = false;
con_update_parents_urgency(con);
workspace_update_urgent_flag(con_get_workspace(con));
ipc_send_window_event("urgent", con);
}
}
@ -388,13 +369,13 @@ Con *con_get_fullscreen_con(Con *con, fullscreen_mode_t fullscreen_mode) {
TAILQ_REMOVE(&bfs_head, entry, entries);
free(entry);
TAILQ_FOREACH (child, &(current->nodes_head), nodes) {
TAILQ_FOREACH(child, &(current->nodes_head), nodes) {
entry = smalloc(sizeof(struct bfs_entry));
entry->con = child;
TAILQ_INSERT_TAIL(&bfs_head, entry, entries);
}
TAILQ_FOREACH (child, &(current->floating_head), floating_windows) {
TAILQ_FOREACH(child, &(current->floating_head), floating_windows) {
entry = smalloc(sizeof(struct bfs_entry));
entry->con = child;
TAILQ_INSERT_TAIL(&bfs_head, entry, entries);
@ -460,7 +441,7 @@ bool con_inside_focused(Con *con) {
*/
Con *con_by_window_id(xcb_window_t window) {
Con *con;
TAILQ_FOREACH (con, &all_cons, all_cons)
TAILQ_FOREACH(con, &all_cons, all_cons)
if (con->window != NULL && con->window->id == window)
return con;
return NULL;
@ -473,7 +454,7 @@ Con *con_by_window_id(xcb_window_t window) {
*/
Con *con_by_frame_id(xcb_window_t frame) {
Con *con;
TAILQ_FOREACH (con, &all_cons, all_cons)
TAILQ_FOREACH(con, &all_cons, all_cons)
if (con->frame == frame)
return con;
return NULL;
@ -490,8 +471,8 @@ Con *con_for_window(Con *con, i3Window *window, Match **store_match) {
//DLOG("searching con for window %p starting at con %p\n", window, con);
//DLOG("class == %s\n", window->class_class);
TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
TAILQ_FOREACH (match, &(child->swallow_head), matches) {
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
TAILQ_FOREACH(match, &(child->swallow_head), matches) {
if (!match_matches_window(match, window))
continue;
if (store_match != NULL)
@ -503,8 +484,8 @@ Con *con_for_window(Con *con, i3Window *window, Match **store_match) {
return result;
}
TAILQ_FOREACH (child, &(con->floating_head), floating_windows) {
TAILQ_FOREACH (match, &(child->swallow_head), matches) {
TAILQ_FOREACH(child, &(con->floating_head), floating_windows) {
TAILQ_FOREACH(match, &(child->swallow_head), matches) {
if (!match_matches_window(match, window))
continue;
if (store_match != NULL)
@ -527,7 +508,7 @@ int con_num_children(Con *con) {
Con *child;
int children = 0;
TAILQ_FOREACH (child, &(con->nodes_head), nodes)
TAILQ_FOREACH(child, &(con->nodes_head), nodes)
children++;
return children;
@ -547,7 +528,7 @@ void con_fix_percent(Con *con) {
// with a percentage set we have
double total = 0.0;
int children_with_percent = 0;
TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
if (child->percent > 0.0) {
total += child->percent;
++children_with_percent;
@ -557,7 +538,7 @@ void con_fix_percent(Con *con) {
// if there were children without a percentage set, set to a value that
// will make those children proportional to all others
if (children_with_percent != children) {
TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
if (child->percent <= 0.0) {
if (children_with_percent == 0)
total += (child->percent = 1.0);
@ -570,10 +551,10 @@ void con_fix_percent(Con *con) {
// if we got a zero, just distribute the space equally, otherwise
// distribute according to the proportions we got
if (total == 0.0) {
TAILQ_FOREACH (child, &(con->nodes_head), nodes)
TAILQ_FOREACH(child, &(con->nodes_head), nodes)
child->percent = 1.0 / children;
} else if (total != 1.0) {
TAILQ_FOREACH (child, &(con->nodes_head), nodes)
TAILQ_FOREACH(child, &(con->nodes_head), nodes)
child->percent /= total;
}
}
@ -585,37 +566,27 @@ void con_fix_percent(Con *con) {
*
*/
void con_toggle_fullscreen(Con *con, int fullscreen_mode) {
Con *workspace, *fullscreen;
if (con->type == CT_WORKSPACE) {
DLOG("You cannot make a workspace fullscreen.\n");
return;
}
DLOG("toggling fullscreen for %p / %s\n", con, con->name);
if (con->fullscreen_mode == CF_NONE) {
/* 1: check if there already is a fullscreen con */
if (fullscreen_mode == CF_GLOBAL)
fullscreen = con_get_fullscreen_con(croot, CF_GLOBAL);
else {
workspace = con_get_workspace(con);
fullscreen = con_get_fullscreen_con(workspace, CF_OUTPUT);
}
if (fullscreen != NULL) {
/* Disable fullscreen for the currently fullscreened
* container and enable it for the one the user wants
* to have in fullscreen mode. */
LOG("Disabling fullscreen for (%p/%s) upon user request\n",
fullscreen, fullscreen->name);
fullscreen->fullscreen_mode = CF_NONE;
}
/* 2: enable fullscreen */
if (con->fullscreen_mode == CF_NONE)
con_enable_fullscreen(con, fullscreen_mode);
else
con_disable_fullscreen(con);
}
/*
* Sets the specified fullscreen mode for the given container, sends the
* fullscreen_mode event and changes the XCB fullscreen property of the
* containers window, if any.
*
*/
static void con_set_fullscreen_mode(Con *con, fullscreen_mode_t fullscreen_mode) {
con->fullscreen_mode = fullscreen_mode;
} else {
/* 1: disable fullscreen */
con->fullscreen_mode = CF_NONE;
}
DLOG("mode now: %d\n", con->fullscreen_mode);
@ -638,6 +609,79 @@ void con_toggle_fullscreen(Con *con, int fullscreen_mode) {
A__NET_WM_STATE, XCB_ATOM_ATOM, 32, num, values);
}
/*
* Enables fullscreen mode for the given container, if necessary.
*
* If the containers mode is already CF_OUTPUT or CF_GLOBAL, the container is
* kept fullscreen but its mode is set to CF_GLOBAL and CF_OUTPUT,
* respectively.
*
* Other fullscreen containers will be disabled first, if they hide the new
* one.
*
*/
void con_enable_fullscreen(Con *con, fullscreen_mode_t fullscreen_mode) {
if (con->type == CT_WORKSPACE) {
DLOG("You cannot make a workspace fullscreen.\n");
return;
}
assert(fullscreen_mode == CF_GLOBAL || fullscreen_mode == CF_OUTPUT);
if (fullscreen_mode == CF_GLOBAL)
DLOG("enabling global fullscreen for %p / %s\n", con, con->name);
else
DLOG("enabling fullscreen for %p / %s\n", con, con->name);
if (con->fullscreen_mode == fullscreen_mode) {
DLOG("fullscreen already enabled for %p / %s\n", con, con->name);
return;
}
Con *con_ws = con_get_workspace(con);
/* Disable any fullscreen container that would conflict the new one. */
Con *fullscreen = con_get_fullscreen_con(croot, CF_GLOBAL);
if (fullscreen == NULL)
fullscreen = con_get_fullscreen_con(con_ws, CF_OUTPUT);
if (fullscreen != NULL)
con_disable_fullscreen(fullscreen);
/* Set focus to new fullscreen container. Unless in global fullscreen mode
* and on another workspace restore focus afterwards.
* Switch to the containers workspace if mode is global. */
Con *cur_ws = con_get_workspace(focused);
Con *old_focused = focused;
if (fullscreen_mode == CF_GLOBAL && cur_ws != con_ws)
workspace_show(con_ws);
con_focus(con);
if (fullscreen_mode != CF_GLOBAL && cur_ws != con_ws)
con_focus(old_focused);
con_set_fullscreen_mode(con, fullscreen_mode);
}
/*
* Disables fullscreen mode for the given container regardless of the mode, if
* necessary.
*
*/
void con_disable_fullscreen(Con *con) {
if (con->type == CT_WORKSPACE) {
DLOG("You cannot make a workspace fullscreen.\n");
return;
}
DLOG("disabling fullscreen for %p / %s\n", con, con->name);
if (con->fullscreen_mode == CF_NONE) {
DLOG("fullscreen already disabled for %p / %s\n", con, con->name);
return;
}
con_set_fullscreen_mode(con, CF_NONE);
}
/*
* Moves the given container to the currently focused container on the given
* workspace.
@ -807,7 +851,7 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
if (!con_is_leaf(con)) {
Con *child;
TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
if (!child->window)
continue;
@ -832,6 +876,8 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
}
CALL(parent, on_remove_child);
ipc_send_window_event("move", con);
}
/*
@ -1003,7 +1049,7 @@ Con *con_descend_tiling_focused(Con *con) {
return next;
do {
before = next;
TAILQ_FOREACH (child, &(next->focus_head), focused) {
TAILQ_FOREACH(child, &(next->focus_head), focused) {
if (child->type == CT_FLOATING_CON)
continue;
@ -1038,7 +1084,7 @@ Con *con_descend_direction(Con *con, direction_t direction) {
/* Wrong orientation. We use the last focused con. Within that con,
* we recurse to chose the left/right con or at least the last
* focused one. */
TAILQ_FOREACH (current, &(con->focus_head), focused) {
TAILQ_FOREACH(current, &(con->focus_head), focused) {
if (current->type != CT_FLOATING_CON) {
most = current;
break;
@ -1063,7 +1109,7 @@ Con *con_descend_direction(Con *con, direction_t direction) {
/* Wrong orientation. We use the last focused con. Within that con,
* we recurse to chose the top/bottom con or at least the last
* focused one. */
TAILQ_FOREACH (current, &(con->focus_head), focused) {
TAILQ_FOREACH(current, &(con->focus_head), focused) {
if (current->type != CT_FLOATING_CON) {
most = current;
break;
@ -1390,8 +1436,15 @@ static void con_on_remove_child(Con *con) {
if (con->type == CT_WORKSPACE) {
if (TAILQ_EMPTY(&(con->focus_head)) && !workspace_is_visible(con)) {
LOG("Closing old workspace (%p / %s), it is empty\n", con, con->name);
yajl_gen gen = ipc_marshal_workspace_event("empty", con, NULL);
tree_close(con, DONT_KILL_WINDOW, false, false);
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}");
const unsigned char *payload;
ylength length;
y(get_buf, &payload, &length);
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
y(free);
}
return;
}
@ -1432,7 +1485,7 @@ Rect con_minimum_size(Con *con) {
if (con->layout == L_STACKED || con->layout == L_TABBED) {
uint32_t max_width = 0, max_height = 0, deco_height = 0;
Con *child;
TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
Rect min = con_minimum_size(child);
deco_height += child->deco_rect.height;
max_width = max(max_width, min.width);
@ -1449,7 +1502,7 @@ Rect con_minimum_size(Con *con) {
if (con_is_split(con)) {
uint32_t width = 0, height = 0;
Con *child;
TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
Rect min = con_minimum_size(child);
if (con->layout == L_SPLITH) {
width += min.width;
@ -1544,7 +1597,7 @@ bool con_has_urgent_child(Con *con) {
/* We are not interested in floating windows since they can only be
* attached to a workspace nodes_head instead of focus_head */
TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
if (con_has_urgent_child(child))
return true;
}
@ -1603,14 +1656,16 @@ void con_set_urgency(Con *con, bool urgent) {
con_update_parents_urgency(con);
if (con->urgent == urgent)
LOG("Urgency flag changed to %d\n", con->urgent);
Con *ws;
/* Set the urgency flag on the workspace, if a workspace could be found
* (for dock clients, that is not the case). */
if ((ws = con_get_workspace(con)) != NULL)
workspace_update_urgent_flag(ws);
if (con->urgent == urgent) {
LOG("Urgency flag changed to %d\n", con->urgent);
ipc_send_window_event("urgent", con);
}
}
/*
@ -1657,7 +1712,7 @@ char *con_get_tree_representation(Con *con) {
/* 2) append representation of children */
Con *child;
TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
char *child_txt = con_get_tree_representation(child);
char *tmp_buf;

View File

@ -11,9 +11,7 @@
*
*/
#include "all.h"
/* We need Xlib for XStringToKeysym */
#include <X11/Xlib.h>
#include <xkbcommon/xkbcommon.h>
char *current_configpath = NULL;
Config config;
@ -36,7 +34,7 @@ void ungrab_all_keys(xcb_connection_t *conn) {
*/
void update_barconfig() {
Barconfig *current;
TAILQ_FOREACH (current, &barconfigs, configs) {
TAILQ_FOREACH(current, &barconfigs, configs) {
ipc_send_barconfig_update_event(current);
}
}
@ -114,12 +112,19 @@ static char *get_config_path(const char *override_configpath) {
* parse_file().
*
*/
static void parse_configuration(const char *override_configpath) {
bool parse_configuration(const char *override_configpath, bool use_nagbar) {
char *path = get_config_path(override_configpath);
LOG("Parsing configfile %s\n", path);
FREE(current_configpath);
current_configpath = path;
parse_file(path);
/* initialize default bindings if we're just validating the config file */
if (!use_nagbar && bindings == NULL) {
bindings = scalloc(sizeof(struct bindings_head));
TAILQ_INIT(bindings);
}
return parse_file(path, use_nagbar);
}
/*
@ -142,9 +147,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
while (!TAILQ_EMPTY(bindings)) {
bind = TAILQ_FIRST(bindings);
TAILQ_REMOVE(bindings, bind, bindings);
FREE(bind->translated_to);
FREE(bind->command);
FREE(bind);
binding_free(bind);
}
FREE(bindings);
SLIST_REMOVE(&modes, mode, Mode, modes);
@ -204,7 +207,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
/* Invalidate pixmap caches in case font or colors changed */
Con *con;
TAILQ_FOREACH (con, &all_cons, all_cons)
TAILQ_FOREACH(con, &all_cons, all_cons)
FREE(con->deco_render_params);
/* Get rid of the current font */
@ -262,7 +265,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
if (config.workspace_urgency_timer == 0)
config.workspace_urgency_timer = 0.5;
parse_configuration(override_configpath);
parse_configuration(override_configpath, true);
if (reload) {
translate_keysyms();

View File

@ -171,8 +171,8 @@ CFGFUN(font, const char *font) {
font_pattern = sstrdup(font);
}
CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *command) {
configure_binding(bindtype, modifiers, key, release, command, DEFAULT_BINDING_MODE);
CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *whole_window, const char *command) {
configure_binding(bindtype, modifiers, key, release, whole_window, command, DEFAULT_BINDING_MODE);
}
/*******************************************************************************
@ -181,8 +181,8 @@ CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, co
static char *current_mode;
CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *command) {
configure_binding(bindtype, modifiers, key, release, command, current_mode);
CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *whole_window, const char *command) {
configure_binding(bindtype, modifiers, key, release, whole_window, command, current_mode);
}
CFGFUN(enter_mode, const char *modename) {
@ -271,13 +271,15 @@ CFGFUN(new_window, const char *windowtype, const char *border, const long width)
}
if (strcmp(windowtype, "new_window") == 0) {
DLOG("default tiled border style = %d and border width = %d\n", border_style, border_width);
DLOG("default tiled border style = %d and border width = %d (%d physical px)\n",
border_style, border_width, logical_px(border_width));
config.default_border = border_style;
config.default_border_width = border_width;
config.default_border_width = logical_px(border_width);
} else {
DLOG("default floating border style = %d and border width = %d\n", border_style, border_width);
DLOG("default floating border style = %d and border width = %d (%d physical px)\n",
border_style, border_width, logical_px(border_width));
config.default_floating_border = border_style;
config.default_floating_border_width = border_width;
config.default_floating_border_width = logical_px(border_width);
}
}
@ -334,7 +336,7 @@ CFGFUN(workspace, const char *workspace, const char *output) {
* outputs */
struct Workspace_Assignment *assignment;
bool duplicate = false;
TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) {
TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
if (strcasecmp(assignment->name, workspace) == 0) {
ELOG("You have a duplicate workspace assignment for workspace \"%s\"\n",
workspace);
@ -460,6 +462,16 @@ CFGFUN(bar_modifier, const char *modifier) {
current_bar.modifier = M_SHIFT;
}
CFGFUN(bar_wheel_up_cmd, const char *command) {
FREE(current_bar.wheel_up_cmd);
current_bar.wheel_up_cmd = sstrdup(command);
}
CFGFUN(bar_wheel_down_cmd, const char *command) {
FREE(current_bar.wheel_down_cmd);
current_bar.wheel_down_cmd = sstrdup(command);
}
CFGFUN(bar_position, const char *position) {
current_bar.position = (strcmp(position, "top") == 0 ? P_TOP : P_BOTTOM);
}

View File

@ -840,7 +840,7 @@ static char *migrate_config(char *input, off_t size) {
* parse_config and possibly launching i3-nagbar.
*
*/
void parse_file(const char *f) {
bool parse_file(const char *f, bool use_nagbar) {
SLIST_HEAD(variables_head, Variable) variables = SLIST_HEAD_INITIALIZER(&variables);
int fd, ret, read_bytes = 0;
struct stat stbuf;
@ -917,7 +917,7 @@ void parse_file(const char *f) {
* variables (otherwise we will count them twice, which is bad when
* 'extra' is negative) */
char *bufcopy = sstrdup(buf);
SLIST_FOREACH (current, &variables, variables) {
SLIST_FOREACH(current, &variables, variables) {
int extra = (strlen(current->value) - strlen(current->key));
char *next;
for (next = bufcopy;
@ -937,11 +937,11 @@ void parse_file(const char *f) {
destwalk = new;
while (walk < (buf + stbuf.st_size)) {
/* Find the next variable */
SLIST_FOREACH (current, &variables, variables)
SLIST_FOREACH(current, &variables, variables)
current->next_match = strcasestr(walk, current->key);
nearest = NULL;
int distance = stbuf.st_size;
SLIST_FOREACH (current, &variables, variables) {
SLIST_FOREACH(current, &variables, variables) {
if (current->next_match == NULL)
continue;
if ((current->next_match - walk) < distance) {
@ -1000,7 +1000,7 @@ void parse_file(const char *f) {
check_for_duplicate_bindings(context);
if (context->has_errors || context->has_warnings) {
if (use_nagbar && (context->has_errors || context->has_warnings)) {
ELOG("FYI: You are using i3 version " I3_VERSION "\n");
if (version == 3)
ELOG("Please convert your configfile first, then fix any remaining errors (see above).\n");
@ -1030,6 +1030,8 @@ void parse_file(const char *f) {
free(pageraction);
}
bool has_errors = context->has_errors;
FREE(context->line_copy);
free(context);
free(new);
@ -1042,6 +1044,8 @@ void parse_file(const char *f) {
SLIST_REMOVE_HEAD(&variables, variables);
FREE(current);
}
return !has_errors;
}
#endif

View File

@ -24,9 +24,9 @@ void ewmh_update_current_desktop(void) {
uint32_t idx = 0;
/* We count to get the index of this workspace because named workspaces
* dont have the ->num property */
TAILQ_FOREACH (output, &(croot->nodes_head), nodes) {
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
Con *ws;
TAILQ_FOREACH (ws, &(output_get_content(output)->nodes_head), nodes) {
TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
if (STARTS_WITH(ws->name, "__"))
continue;
@ -40,6 +40,102 @@ void ewmh_update_current_desktop(void) {
}
}
/*
* Updates _NET_NUMBER_OF_DESKTOPS which we interpret as the number of
* noninternal workspaces.
*/
void ewmh_update_number_of_desktops(void) {
Con *output;
uint32_t idx = 0;
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
Con *ws;
TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
if (STARTS_WITH(ws->name, "__"))
continue;
++idx;
}
}
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
A__NET_NUMBER_OF_DESKTOPS, XCB_ATOM_CARDINAL, 32, 1, &idx);
}
/*
* Updates _NET_DESKTOP_NAMES: "The names of all virtual desktops. This is a
* list of NULL-terminated strings in UTF-8 encoding"
*/
void ewmh_update_desktop_names(void) {
Con *output;
int msg_length = 0;
/* count the size of the property message to set */
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
Con *ws;
TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
if (STARTS_WITH(ws->name, "__"))
continue;
msg_length += strlen(ws->name) + 1;
}
}
char desktop_names[msg_length];
int current_position = 0;
/* fill the buffer with the names of the i3 workspaces */
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
Con *ws;
TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
if (STARTS_WITH(ws->name, "__"))
continue;
for (size_t i = 0; i < strlen(ws->name) + 1; i++) {
desktop_names[current_position++] = ws->name[i];
}
}
}
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
A__NET_DESKTOP_NAMES, A_UTF8_STRING, 8, msg_length, desktop_names);
}
/*
* Updates _NET_DESKTOP_VIEWPORT, which is an array of pairs of cardinals that
* define the top left corner of each desktop's viewport.
*/
void ewmh_update_desktop_viewport(void) {
Con *output;
int num_desktops = 0;
/* count number of desktops */
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
Con *ws;
TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
if (STARTS_WITH(ws->name, "__"))
continue;
num_desktops++;
}
}
uint32_t viewports[num_desktops * 2];
int current_position = 0;
/* fill the viewport buffer */
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
Con *ws;
TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
if (STARTS_WITH(ws->name, "__"))
continue;
viewports[current_position++] = output->rect.x;
viewports[current_position++] = output->rect.y;
}
}
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
A__NET_DESKTOP_VIEWPORT, XCB_ATOM_CARDINAL, 32, current_position, &viewports);
}
/*
* Updates _NET_ACTIVE_WINDOW with the currently focused window.
*
@ -138,5 +234,6 @@ void ewmh_setup_hints(void) {
/* Im not entirely sure if we need to keep _NET_WM_NAME on root. */
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3");
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 19, supported_atoms);
/* only send the first 24 atoms (last one is _NET_CLOSE_WINDOW) increment that number when adding supported atoms */
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 24, supported_atoms);
}

View File

@ -20,7 +20,7 @@ static int num_screens;
*/
static Output *get_screen_at(unsigned int x, unsigned int y) {
Output *output;
TAILQ_FOREACH (output, &outputs, outputs)
TAILQ_FOREACH(output, &outputs, outputs)
if (output->rect.x == x && output->rect.y == y)
return output;

View File

@ -21,7 +21,7 @@ static Rect total_outputs_dimensions(void) {
Output *output;
/* Use Rect to encapsulate dimensions, ignoring x/y */
Rect outputs_dimensions = {0, 0, 0, 0};
TAILQ_FOREACH (output, &outputs, outputs) {
TAILQ_FOREACH(output, &outputs, outputs) {
outputs_dimensions.height += output->rect.height;
outputs_dimensions.width += output->rect.width;
}
@ -205,7 +205,7 @@ void floating_enable(Con *con, bool automatic) {
if (memcmp(&(nc->rect), &zero, sizeof(Rect)) == 0) {
DLOG("Geometry not set, combining children\n");
Con *child;
TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
DLOG("child geometry: %d x %d\n", child->geometry.width, child->geometry.height);
nc->rect.width += child->geometry.width;
nc->rect.height = max(nc->rect.height, child->geometry.height);
@ -298,16 +298,22 @@ void floating_enable(Con *con, bool automatic) {
/* Check if we need to re-assign it to a different workspace because of its
* coordinates and exit if that was done successfully. */
if (floating_maybe_reassign_ws(nc))
if (floating_maybe_reassign_ws(nc)) {
ipc_send_window_event("floating", con);
return;
}
/* Sanitize coordinates: Check if they are on any output */
if (get_output_containing(nc->rect.x, nc->rect.y) != NULL)
if (get_output_containing(nc->rect.x, nc->rect.y) != NULL) {
ipc_send_window_event("floating", con);
return;
}
ELOG("No output found at destination coordinates, centering floating window on current ws\n");
nc->rect.x = ws->rect.x + (ws->rect.width / 2) - (nc->rect.width / 2);
nc->rect.y = ws->rect.y + (ws->rect.height / 2) - (nc->rect.height / 2);
ipc_send_window_event("floating", con);
}
void floating_disable(Con *con, bool automatic) {
@ -351,6 +357,8 @@ void floating_disable(Con *con, bool automatic) {
if (set_focus)
con_focus(con);
ipc_send_window_event("floating", con);
}
/*

View File

@ -21,6 +21,8 @@
#include <libsn/sn-monitor.h>
int randr_base = -1;
int xkb_base = -1;
int xkb_current_group;
/* After mapping/unmapping windows, a notify event is generated. However, we dont want it,
since itd trigger an infinite loop of switching between the different windows when
@ -62,7 +64,7 @@ bool event_is_ignored(const int sequence, const int response_type) {
event = SLIST_NEXT(event, ignore_events);
}
SLIST_FOREACH (event, &ignore_events, ignore_events) {
SLIST_FOREACH(event, &ignore_events, ignore_events) {
if (event->sequence != sequence)
continue;
@ -163,7 +165,7 @@ static void handle_enter_notify(xcb_enter_notify_event_t *event) {
layout_t layout = (enter_child ? con->parent->layout : con->layout);
if (layout == L_DEFAULT) {
Con *child;
TAILQ_FOREACH (child, &(con->nodes_head), nodes)
TAILQ_FOREACH(child, &(con->nodes_head), nodes)
if (rect_contains(child->deco_rect, event->event_x, event->event_y)) {
LOG("using child %p / %s instead!\n", child, child->name);
con = child;
@ -231,7 +233,7 @@ static void handle_motion_notify(xcb_motion_notify_event_t *event) {
/* see over which rect the user is */
Con *current;
TAILQ_FOREACH (current, &(con->nodes_head), nodes) {
TAILQ_FOREACH(current, &(con->nodes_head), nodes) {
if (!rect_contains(current->deco_rect, event->event_x, event->event_y))
continue;
@ -282,7 +284,6 @@ static void handle_map_request(xcb_map_request_event_t *event) {
add_ignore_event(event->sequence, -1);
manage_window(event->window, cookie, false);
x_push_changes(croot);
return;
}
@ -487,7 +488,6 @@ static void handle_unmap_notify_event(xcb_unmap_notify_event_t *event) {
tree_close(con, DONT_KILL_WINDOW, false, false);
tree_render();
x_push_changes(croot);
ignore_end:
/* If the client (as opposed to i3) destroyed or unmapped a window, an
@ -651,6 +651,19 @@ static void handle_expose_event(xcb_expose_event_t *event) {
return;
}
#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0
#define _NET_WM_MOVERESIZE_SIZE_TOP 1
#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2
#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3
#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4
#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5
#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6
#define _NET_WM_MOVERESIZE_SIZE_LEFT 7
#define _NET_WM_MOVERESIZE_MOVE 8 /* movement only */
#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 /* size via keyboard */
#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */
#define _NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */
/*
* Handle client messages (EWMH)
*
@ -791,6 +804,98 @@ static void handle_client_message(xcb_client_message_event_t *event) {
XCB_ATOM_CARDINAL, 32, 4,
&r);
xcb_flush(conn);
} else if (event->type == A_WM_CHANGE_STATE) {
/* http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.4 */
Con *con = con_by_window_id(event->window);
if (con && event->data.data32[0] == 3) {
/* this request is so we can play some animiation showing the
* window physically moving to the tray before we close it (I
* think) */
DLOG("Client has requested iconic state. Closing this con. (con = %p)\n", con);
tree_close(con, DONT_KILL_WINDOW, false, false);
tree_render();
} else {
DLOG("Not handling WM_CHANGE_STATE request. (window = %d, state = %d)\n", event->window, event->data.data32[0]);
}
} else if (event->type == A__NET_CURRENT_DESKTOP) {
/* This request is used by pagers and bars to change the current
* desktop likely as a result of some user action. We interpret this as
* a request to focus the given workspace. See
* http://standards.freedesktop.org/wm-spec/latest/ar01s03.html#idm140251368135008
* */
Con *output;
uint32_t idx = 0;
DLOG("Request to change current desktop to index %d\n", event->data.data32[0]);
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
Con *ws;
TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
if (STARTS_WITH(ws->name, "__"))
continue;
if (idx == event->data.data32[0]) {
/* data32[1] is a timestamp used to prevent focus race conditions */
if (event->data.data32[1])
last_timestamp = event->data.data32[1];
DLOG("Handling request to focus workspace %s\n", ws->name);
workspace_show(ws);
tree_render();
return;
}
++idx;
}
}
} else if (event->type == A__NET_CLOSE_WINDOW) {
/*
* Pagers wanting to close a window MUST send a _NET_CLOSE_WINDOW
* client message request to the root window.
* http://standards.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472668896
*/
Con *con = con_by_window_id(event->window);
if (con) {
DLOG("Handling _NET_CLOSE_WINDOW request (con = %p)\n", con);
if (event->data.data32[0])
last_timestamp = event->data.data32[0];
tree_close(con, KILL_WINDOW, false, false);
tree_render();
} else {
DLOG("Couldn't find con for _NET_CLOSE_WINDOW request. (window = %d)\n", event->window);
}
} else if (event->type == A__NET_WM_MOVERESIZE) {
/*
* Client-side decorated Gtk3 windows emit this signal when being
* dragged by their GtkHeaderBar
*/
Con *con = con_by_window_id(event->window);
if (!con || !con_is_floating(con)) {
DLOG("Couldn't find con for _NET_WM_MOVERESIZE request, or con not floating (window = %d)\n", event->window);
return;
}
DLOG("Handling _NET_WM_MOVERESIZE request (con = %p)\n", con);
uint32_t direction = event->data.data32[2];
uint32_t x_root = event->data.data32[0];
uint32_t y_root = event->data.data32[1];
/* construct fake xcb_button_press_event_t */
xcb_button_press_event_t fake = {
.root_x = x_root,
.root_y = y_root,
.event_x = x_root - (con->rect.x),
.event_y = y_root - (con->rect.y)};
if (direction == _NET_WM_MOVERESIZE_MOVE) {
floating_drag_window(con->parent, &fake);
} else if (direction >= _NET_WM_MOVERESIZE_SIZE_TOPLEFT && direction <= _NET_WM_MOVERESIZE_SIZE_LEFT) {
floating_resize_window(con->parent, FALSE, &fake);
} else {
DLOG("_NET_WM_MOVERESIZE direction %d not implemented\n", direction);
}
} else {
DLOG("unhandled clientmessage\n");
return;
@ -1045,6 +1150,30 @@ static void handle_focus_in(xcb_focus_in_event_t *event) {
return;
}
/*
* Handles the WM_CLASS property for assignments and criteria selection.
*
*/
static bool handle_class_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
xcb_atom_t name, xcb_get_property_reply_t *prop) {
Con *con;
if ((con = con_by_window_id(window)) == NULL || con->window == NULL)
return false;
if (prop == NULL) {
prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn,
false, window, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 0, 32),
NULL);
if (prop == NULL)
return false;
}
window_update_class(con->window, prop, false);
return true;
}
/* Returns false if the event could not be processed (e.g. the window could not
* be found), true otherwise */
typedef bool (*cb_property_handler_t)(void *data, xcb_connection_t *c, uint8_t state, xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *property);
@ -1062,7 +1191,8 @@ static struct property_handler_t property_handlers[] = {
{0, UINT_MAX, handle_normal_hints},
{0, UINT_MAX, handle_clientleader_change},
{0, UINT_MAX, handle_transient_for},
{0, 128, handle_windowrole_change}};
{0, 128, handle_windowrole_change},
{0, 128, handle_class_change}};
#define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t))
/*
@ -1080,6 +1210,7 @@ void property_handlers_init(void) {
property_handlers[4].atom = A_WM_CLIENT_LEADER;
property_handlers[5].atom = XCB_ATOM_WM_TRANSIENT_FOR;
property_handlers[6].atom = A_WM_WINDOW_ROLE;
property_handlers[7].atom = XCB_ATOM_WM_CLASS;
}
static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) {
@ -1115,12 +1246,50 @@ static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom)
*
*/
void handle_event(int type, xcb_generic_event_t *event) {
DLOG("event type %d, xkb_base %d\n", type, xkb_base);
if (randr_base > -1 &&
type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
handle_screen_change(event);
return;
}
if (xkb_base > -1 && type == xkb_base) {
DLOG("xkb event, need to handle it.\n");
xcb_xkb_state_notify_event_t *state = (xcb_xkb_state_notify_event_t *)event;
if (state->xkbType == XCB_XKB_MAP_NOTIFY) {
if (event_is_ignored(event->sequence, type)) {
DLOG("Ignoring map notify event for sequence %d.\n", state->sequence);
} else {
DLOG("xkb map notify, sequence %d, time %d\n", state->sequence, state->time);
add_ignore_event(event->sequence, type);
ungrab_all_keys(conn);
translate_keysyms();
grab_all_keys(conn, false);
}
} else if (state->xkbType == XCB_XKB_STATE_NOTIFY) {
DLOG("xkb state group = %d\n", state->group);
/* See The XKB Extension: Library Specification, section 14.1 */
/* We check if the current group (each group contains
* two levels) has been changed. Mode_switch activates
* group XkbGroup2Index */
if (xkb_current_group == state->group)
return;
xkb_current_group = state->group;
if (state->group == XCB_XKB_GROUP_1) {
DLOG("Mode_switch disabled\n");
ungrab_all_keys(conn);
grab_all_keys(conn, false);
} else {
DLOG("Mode_switch enabled\n");
grab_all_keys(conn, false);
}
}
return;
}
switch (type) {
case XCB_KEY_PRESS:
case XCB_KEY_RELEASE:
@ -1128,6 +1297,7 @@ void handle_event(int type, xcb_generic_event_t *event) {
break;
case XCB_BUTTON_PRESS:
case XCB_BUTTON_RELEASE:
handle_button_press((xcb_button_press_event_t *)event);
break;

View File

@ -5,8 +5,8 @@ CLEAN_TARGETS += clean-i3
i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c))
i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h)
i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h))
i3_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(X11_CFLAGS) $(XCURSOR_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS)
i3_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(X11_LIBS) $(XCURSOR_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm -lpthread
i3_CFLAGS = $(XKB_COMMON_CFLAGS) $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(XCURSOR_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS)
i3_LIBS = $(XKB_COMMON_LIBS) $(XCB_LIBS) $(XCB_XKB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(XCURSOR_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm -lpthread
# When using clang, we use pre-compiled headers to speed up the build. With
# gcc, this actually makes the build slower.

160
src/ipc.c
View File

@ -74,7 +74,7 @@ bool mkdirp(const char *path) {
*/
void ipc_send_event(const char *event, uint32_t message_type, const char *payload) {
ipc_client *current;
TAILQ_FOREACH (current, &all_clients, clients) {
TAILQ_FOREACH(current, &all_clients, clients) {
/* see if this client is interested in this event */
bool interested = false;
for (int i = 0; i < current->num_events; i++) {
@ -151,6 +151,60 @@ static void dump_rect(yajl_gen gen, const char *name, Rect r) {
y(map_close);
}
static void dump_binding(yajl_gen gen, Binding *bind) {
y(map_open);
ystr("input_code");
y(integer, bind->keycode);
ystr("input_type");
ystr((const char*)(bind->input_type == B_KEYBOARD ? "keyboard" : "mouse"));
ystr("symbol");
if (bind->symbol == NULL)
y(null);
else
ystr(bind->symbol);
ystr("command");
ystr(bind->command);
ystr("mods");
y(array_open);
for (int i = 0; i < 8; i++) {
if (bind->mods & (1 << i)) {
switch (1 << i) {
case XCB_MOD_MASK_SHIFT:
ystr("shift");
break;
case XCB_MOD_MASK_LOCK:
ystr("lock");
break;
case XCB_MOD_MASK_CONTROL:
ystr("ctrl");
break;
case XCB_MOD_MASK_1:
ystr("Mod1");
break;
case XCB_MOD_MASK_2:
ystr("Mod2");
break;
case XCB_MOD_MASK_3:
ystr("Mod3");
break;
case XCB_MOD_MASK_4:
ystr("Mod4");
break;
case XCB_MOD_MASK_5:
ystr("Mod5");
break;
}
}
}
y(array_close);
y(map_close);
}
void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
y(map_open);
ystr("id");
@ -293,14 +347,17 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
y(integer, con->current_border_width);
dump_rect(gen, "rect", con->rect);
dump_rect(gen, "deco_rect", con->deco_rect);
dump_rect(gen, "window_rect", con->window_rect);
dump_rect(gen, "geometry", con->geometry);
ystr("name");
if (con->window && con->window->name)
ystr(i3string_as_utf8(con->window->name));
else
else if (con->name != NULL)
ystr(con->name);
else
y(null);
if (con->type == CT_WORKSPACE) {
ystr("num");
@ -337,6 +394,11 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
ystr(i3string_as_utf8(con->window->name));
}
ystr("transient_for");
if (con->window->transient_for == XCB_NONE)
y(null);
else y(integer, con->window->transient_for);
y(map_close);
}
@ -344,7 +406,7 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
y(array_open);
Con *node;
if (con->type != CT_DOCKAREA || !inplace_restart) {
TAILQ_FOREACH (node, &(con->nodes_head), nodes) {
TAILQ_FOREACH(node, &(con->nodes_head), nodes) {
dump_node(gen, node, inplace_restart);
}
}
@ -352,14 +414,14 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
ystr("floating_nodes");
y(array_open);
TAILQ_FOREACH (node, &(con->floating_head), floating_windows) {
TAILQ_FOREACH(node, &(con->floating_head), floating_windows) {
dump_node(gen, node, inplace_restart);
}
y(array_close);
ystr("focus");
y(array_open);
TAILQ_FOREACH (node, &(con->focus_head), focused) {
TAILQ_FOREACH(node, &(con->focus_head), focused) {
y(integer, (long int)node);
}
y(array_close);
@ -386,7 +448,7 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
ystr("swallows");
y(array_open);
Match *match;
TAILQ_FOREACH (match, &(con->swallow_head), matches) {
TAILQ_FOREACH(match, &(con->swallow_head), matches) {
y(map_open);
if (match->dock != -1) {
ystr("dock");
@ -512,6 +574,16 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
break;
}
if (config->wheel_up_cmd) {
ystr("wheel_up_cmd");
ystr(config->wheel_up_cmd);
}
if (config->wheel_down_cmd) {
ystr("wheel_down_cmd");
ystr(config->wheel_down_cmd);
}
ystr("position");
if (config->position == P_BOTTOM)
ystr("bottom");
@ -591,18 +663,15 @@ IPC_HANDLER(get_workspaces) {
Con *focused_ws = con_get_workspace(focused);
Con *output;
TAILQ_FOREACH (output, &(croot->nodes_head), nodes) {
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
if (con_is_internal(output))
continue;
Con *ws;
TAILQ_FOREACH (ws, &(output_get_content(output)->nodes_head), nodes) {
TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
assert(ws->type == CT_WORKSPACE);
y(map_open);
ystr("num");
if (ws->num == -1)
y(null);
else
y(integer, ws->num);
ystr("name");
@ -656,7 +725,7 @@ IPC_HANDLER(get_outputs) {
y(array_open);
Output *output;
TAILQ_FOREACH (output, &outputs, outputs) {
TAILQ_FOREACH(output, &outputs, outputs) {
y(map_open);
ystr("name");
@ -710,7 +779,7 @@ IPC_HANDLER(get_marks) {
y(array_open);
Con *con;
TAILQ_FOREACH (con, &all_cons, all_cons)
TAILQ_FOREACH(con, &all_cons, all_cons)
if (con->mark != NULL)
ystr(con->mark);
@ -766,7 +835,7 @@ IPC_HANDLER(get_bar_config) {
if (message_size == 0) {
y(array_open);
Barconfig *current;
TAILQ_FOREACH (current, &barconfigs, configs) {
TAILQ_FOREACH(current, &barconfigs, configs) {
ystr(current->id);
}
y(array_close);
@ -786,7 +855,7 @@ IPC_HANDLER(get_bar_config) {
strncpy(bar_id, (const char *)message, message_size);
LOG("IPC: looking for config for bar ID \"%s\"\n", bar_id);
Barconfig *current, *config = NULL;
TAILQ_FOREACH (current, &barconfigs, configs) {
TAILQ_FOREACH(current, &barconfigs, configs) {
if (strcmp(current->id, bar_id) != 0)
continue;
@ -852,7 +921,7 @@ IPC_HANDLER(subscribe) {
ipc_client *current, *client = NULL;
/* Search the ipc_client structure for this connection */
TAILQ_FOREACH (current, &all_clients, clients) {
TAILQ_FOREACH(current, &all_clients, clients) {
if (current->fd != fd)
continue;
@ -932,7 +1001,7 @@ static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) {
/* Delete the client from the list of clients */
ipc_client *current;
TAILQ_FOREACH (current, &all_clients, clients) {
TAILQ_FOREACH(current, &all_clients, clients) {
if (current->fd != w->fd)
continue;
@ -1051,20 +1120,22 @@ int ipc_create_socket(const char *filename) {
}
/*
* For the workspace "focus" event we send, along the usual "change" field,
* also the current and previous workspace, in "current" and "old"
* respectively.
* Generates a json workspace event. Returns a dynamically allocated yajl
* generator. Free with yajl_gen_free().
*/
void ipc_send_workspace_focus_event(Con *current, Con *old) {
yajl_gen ipc_marshal_workspace_event(const char *change, Con *current, Con *old) {
setlocale(LC_NUMERIC, "C");
yajl_gen gen = ygenalloc();
y(map_open);
ystr("change");
ystr("focus");
ystr(change);
ystr("current");
if (current == NULL)
y(null);
else
dump_node(gen, current, false);
ystr("old");
@ -1075,13 +1146,26 @@ void ipc_send_workspace_focus_event(Con *current, Con *old) {
y(map_close);
setlocale(LC_NUMERIC, "");
return gen;
}
/*
* For the workspace events we send, along with the usual "change" field, also
* the workspace container in "current". For focus events, we send the
* previously focused workspace in "old".
*/
void ipc_send_workspace_event(const char *change, Con *current, Con *old) {
yajl_gen gen = ipc_marshal_workspace_event(change, current, old);
const unsigned char *payload;
ylength length;
y(get_buf, &payload, &length);
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
y(free);
setlocale(LC_NUMERIC, "");
}
/**
@ -1132,3 +1216,33 @@ void ipc_send_barconfig_update_event(Barconfig *barconfig) {
y(free);
setlocale(LC_NUMERIC, "");
}
/*
* For the binding events, we send the serialized binding struct.
*/
void ipc_send_binding_event(const char *event_type, Binding *bind) {
DLOG("Issue IPC binding %s event (sym = %s, code = %d)\n", event_type, bind->symbol, bind->keycode);
setlocale(LC_NUMERIC, "C");
yajl_gen gen = ygenalloc();
y(map_open);
ystr("change");
ystr(event_type);
ystr("binding");
dump_binding(gen, bind);
y(map_close);
const unsigned char *payload;
ylength length;
y(get_buf, &payload, &length);
ipc_send_event("binding", I3_IPC_EVENT_BINDING, (const char *)payload);
y(free);
setlocale(LC_NUMERIC, "");
}

View File

@ -30,7 +30,7 @@ void handle_key_press(xcb_key_press_event_t *event) {
if (bind == NULL)
return;
CommandResult *result = run_binding(bind);
CommandResult *result = run_binding(bind, NULL);
if (result->needs_tree_render)
tree_render();

View File

@ -24,6 +24,7 @@ static Con *json_node;
static Con *to_focus;
static bool parsing_swallows;
static bool parsing_rect;
static bool parsing_deco_rect;
static bool parsing_window_rect;
static bool parsing_geometry;
static bool parsing_focus;
@ -47,7 +48,7 @@ static int json_start_map(void *ctx) {
match_init(current_swallow);
TAILQ_INSERT_TAIL(&(json_node->swallow_head), current_swallow, matches);
} else {
if (!parsing_rect && !parsing_window_rect && !parsing_geometry) {
if (!parsing_rect && !parsing_deco_rect && !parsing_window_rect && !parsing_geometry) {
if (last_key && strcasecmp(last_key, "floating_nodes") == 0) {
DLOG("New floating_node\n");
Con *ws = con_get_workspace(json_node);
@ -68,7 +69,7 @@ static int json_start_map(void *ctx) {
static int json_end_map(void *ctx) {
LOG("end of map\n");
if (!parsing_swallows && !parsing_rect && !parsing_window_rect && !parsing_geometry) {
if (!parsing_swallows && !parsing_rect && !parsing_deco_rect && !parsing_window_rect && !parsing_geometry) {
/* Set a few default values to simplify manually crafted layout files. */
if (json_node->layout == L_DEFAULT) {
DLOG("Setting layout = L_SPLITH\n");
@ -98,7 +99,7 @@ static int json_end_map(void *ctx) {
* workspace called 1. */
Con *output;
Con *workspace = NULL;
TAILQ_FOREACH (output, &(croot->nodes_head), nodes)
TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, json_node->name));
char *base = sstrdup(json_node->name);
int cnt = 1;
@ -106,18 +107,13 @@ static int json_end_map(void *ctx) {
FREE(json_node->name);
asprintf(&(json_node->name), "%s_%d", base, cnt++);
workspace = NULL;
TAILQ_FOREACH (output, &(croot->nodes_head), nodes)
TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, json_node->name));
}
free(base);
/* Set num accordingly so that i3bar will properly sort it. */
json_node->num = ws_name_to_number(json_node->name);
} else {
// TODO: remove this in the “next” branch.
if (json_node->name == NULL || strcmp(json_node->name, "") == 0) {
json_node->name = sstrdup("#ff0000");
}
}
LOG("attaching\n");
@ -126,11 +122,10 @@ static int json_end_map(void *ctx) {
x_con_init(json_node, json_node->depth);
json_node = json_node->parent;
}
if (parsing_rect)
parsing_rect = false;
if (parsing_window_rect)
parsing_deco_rect = false;
parsing_window_rect = false;
if (parsing_geometry)
parsing_geometry = false;
return 1;
}
@ -146,10 +141,10 @@ static int json_end_array(void *ctx) {
if (parsing_focus) {
/* Clear the list of focus mappings */
struct focus_mapping *mapping;
TAILQ_FOREACH_REVERSE (mapping, &focus_mappings, focus_mappings_head, focus_mappings) {
TAILQ_FOREACH_REVERSE(mapping, &focus_mappings, focus_mappings_head, focus_mappings) {
LOG("focus (reverse) %d\n", mapping->old_id);
Con *con;
TAILQ_FOREACH (con, &(json_node->focus_head), focused) {
TAILQ_FOREACH(con, &(json_node->focus_head), focused) {
if (con->old_id != mapping->old_id)
continue;
LOG("got it! %p\n", con);
@ -180,6 +175,9 @@ static int json_key(void *ctx, const unsigned char *val, size_t len) {
if (strcasecmp(last_key, "rect") == 0)
parsing_rect = true;
if (strcasecmp(last_key, "deco_rect") == 0)
parsing_deco_rect = true;
if (strcasecmp(last_key, "window_rect") == 0)
parsing_window_rect = true;
@ -553,6 +551,7 @@ void tree_append_json(Con *con, const char *filename, char **errormsg) {
to_focus = NULL;
parsing_swallows = false;
parsing_rect = false;
parsing_deco_rect = false;
parsing_window_rect = false;
parsing_geometry = false;
parsing_focus = false;

View File

@ -36,10 +36,6 @@ int listen_fds;
* temporarily for drag_pointer(). */
static struct ev_check *xcb_check;
static int xkb_event_base;
int xkb_current_group;
extern Con *focused;
char **start_argv;
@ -70,9 +66,6 @@ struct ev_loop *main_loop;
xcb_key_symbols_t *keysyms;
/* Those are our connections to X11 for use with libXcursor and XKB */
Display *xlibdpy, *xkbdpy;
/* Default shmlog size if not set by user. */
const int default_shmlog_size = 25 * 1024 * 1024;
@ -94,12 +87,6 @@ struct ws_assignments_head ws_assignments = TAILQ_HEAD_INITIALIZER(ws_assignment
/* We hope that those are supported and set them to true */
bool xcursor_supported = true;
bool xkb_supported = true;
/* This will be set to true when -C is used so that functions can behave
* slightly differently. We dont want i3-nagbar to be started when validating
* the config, for example. */
bool only_check_config = false;
/*
* This callback is only a dummy, see xcb_prepare_cb and xcb_check_cb.
@ -166,73 +153,6 @@ void main_set_x11_cb(bool enable) {
}
}
/*
* When using xmodmap to change the keyboard mapping, this event
* is only sent via XKB. Therefore, we need this special handler.
*
*/
static void xkb_got_event(EV_P_ struct ev_io *w, int revents) {
DLOG("Handling XKB event\n");
XkbEvent ev;
/* When using xmodmap, every change (!) gets an own event.
* Therefore, we just read all events and only handle the
* mapping_notify once. */
bool mapping_changed = false;
while (XPending(xkbdpy)) {
XNextEvent(xkbdpy, (XEvent *)&ev);
/* While we should never receive a non-XKB event,
* better do sanity checking */
if (ev.type != xkb_event_base)
continue;
if (ev.any.xkb_type == XkbMapNotify) {
mapping_changed = true;
continue;
}
if (ev.any.xkb_type != XkbStateNotify) {
ELOG("Unknown XKB event received (type %d)\n", ev.any.xkb_type);
continue;
}
/* See The XKB Extension: Library Specification, section 14.1 */
/* We check if the current group (each group contains
* two levels) has been changed. Mode_switch activates
* group XkbGroup2Index */
if (xkb_current_group == ev.state.group)
continue;
xkb_current_group = ev.state.group;
if (ev.state.group == XkbGroup2Index) {
DLOG("Mode_switch enabled\n");
grab_all_keys(conn, true);
}
if (ev.state.group == XkbGroup1Index) {
DLOG("Mode_switch disabled\n");
ungrab_all_keys(conn);
grab_all_keys(conn, false);
}
}
if (!mapping_changed)
return;
DLOG("Keyboard mapping changed, updating keybindings\n");
xcb_key_symbols_free(keysyms);
keysyms = xcb_key_symbols_alloc(conn);
xcb_numlock_mask = aio_get_mod_mask_for(XCB_NUM_LOCK, keysyms);
ungrab_all_keys(conn);
DLOG("Re-grabbing...\n");
translate_keysyms();
grab_all_keys(conn, (xkb_current_group == XkbGroup2Index));
DLOG("Done\n");
}
/*
* Exit handler which destroys the main_loop. Will trigger cleanup handlers.
*
@ -276,6 +196,7 @@ int main(int argc, char *argv[]) {
bool force_xinerama = false;
char *fake_outputs = NULL;
bool disable_signalhandler = false;
bool only_check_config = false;
static struct option long_options[] = {
{"no-autostart", no_argument, 0, 'a'},
{"config", required_argument, 0, 'c'},
@ -441,10 +362,14 @@ int main(int argc, char *argv[]) {
}
}
if (only_check_config) {
exit(parse_configuration(override_configpath, false) ? 0 : 1);
}
/* If the user passes more arguments, we act like i3-msg would: Just send
* the arguments as an IPC message to i3. This allows for nice semantic
* commands such as 'i3 border none'. */
if (!only_check_config && optind < argc) {
if (optind < argc) {
/* We enable verbose mode so that the user knows whats going on.
* This should make it easier to find mistakes when the user passes
* arguments by mistake. */
@ -567,10 +492,6 @@ int main(int argc, char *argv[]) {
xcb_query_pointer_cookie_t pointercookie = xcb_query_pointer(conn, root);
load_configuration(conn, override_configpath, false);
if (only_check_config) {
LOG("Done checking configuration file. Exiting.\n");
exit(0);
}
if (config.ipc_socket_path == NULL) {
/* Fall back to a file name in /tmp/ based on the PID */
@ -597,21 +518,7 @@ int main(int argc, char *argv[]) {
#include "atoms.xmacro"
#undef xmacro
/* Initialize the Xlib connection */
xlibdpy = xkbdpy = XOpenDisplay(NULL);
/* Try to load the X cursors and initialize the XKB extension */
if (xlibdpy == NULL) {
ELOG("ERROR: XOpenDisplay() failed, disabling libXcursor/XKB support\n");
xcursor_supported = false;
xkb_supported = false;
} else if (fcntl(ConnectionNumber(xlibdpy), F_SETFD, FD_CLOEXEC) == -1) {
ELOG("Could not set FD_CLOEXEC on xkbdpy\n");
return 1;
} else {
xcursor_load_cursors();
/*init_xkb();*/
}
/* Set a cursor for the root window (otherwise the root window will show no
cursor until the first client is launched). */
@ -620,27 +527,22 @@ int main(int argc, char *argv[]) {
else
xcb_set_root_cursor(XCURSOR_CURSOR_POINTER);
if (xkb_supported) {
int errBase,
major = XkbMajorVersion,
minor = XkbMinorVersion;
if (fcntl(ConnectionNumber(xkbdpy), F_SETFD, FD_CLOEXEC) == -1) {
fprintf(stderr, "Could not set FD_CLOEXEC on xkbdpy\n");
return 1;
}
int i1;
if (!XkbQueryExtension(xkbdpy, &i1, &xkb_event_base, &errBase, &major, &minor)) {
fprintf(stderr, "XKB not supported by X-server\n");
xkb_supported = false;
}
/* end of ugliness */
if (xkb_supported && !XkbSelectEvents(xkbdpy, XkbUseCoreKbd, XkbMapNotifyMask | XkbStateNotifyMask, XkbMapNotifyMask | XkbStateNotifyMask)) {
fprintf(stderr, "Could not set XKB event mask\n");
return 1;
}
const xcb_query_extension_reply_t *extreply;
extreply = xcb_get_extension_data(conn, &xcb_xkb_id);
if (!extreply->present) {
DLOG("xkb is not present on this server\n");
} else {
DLOG("initializing xcb-xkb\n");
xcb_xkb_use_extension(conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION);
xcb_xkb_select_events(conn,
XCB_XKB_ID_USE_CORE_KBD,
XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY,
0,
XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY,
0xff,
0xff,
NULL);
xkb_base = extreply->first_event;
}
restore_connect();
@ -769,25 +671,19 @@ int main(int argc, char *argv[]) {
x_set_i3_atoms();
ewmh_update_workarea();
/* Set the _NET_CURRENT_DESKTOP property. */
/* Set the ewmh desktop properties. */
ewmh_update_current_desktop();
ewmh_update_number_of_desktops();
ewmh_update_desktop_names();
ewmh_update_desktop_viewport();
struct ev_io *xcb_watcher = scalloc(sizeof(struct ev_io));
struct ev_io *xkb = scalloc(sizeof(struct ev_io));
xcb_check = scalloc(sizeof(struct ev_check));
struct ev_prepare *xcb_prepare = scalloc(sizeof(struct ev_prepare));
ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ);
ev_io_start(main_loop, xcb_watcher);
if (xkb_supported) {
ev_io_init(xkb, xkb_got_event, ConnectionNumber(xkbdpy), EV_READ);
ev_io_start(main_loop, xkb);
/* Flush the buffer so that libev can properly get new events */
XFlush(xkbdpy);
}
ev_check_init(xcb_check, xcb_check_cb);
ev_check_start(main_loop, xcb_check);
@ -888,7 +784,7 @@ int main(int argc, char *argv[]) {
/* Autostarting exec-lines */
if (autostart) {
struct Autostart *exec;
TAILQ_FOREACH (exec, &autostarts, autostarts) {
TAILQ_FOREACH(exec, &autostarts, autostarts) {
LOG("auto-starting %s\n", exec->command);
start_application(exec->command, exec->no_startup_id);
}
@ -896,14 +792,14 @@ int main(int argc, char *argv[]) {
/* Autostarting exec_always-lines */
struct Autostart *exec_always;
TAILQ_FOREACH (exec_always, &autostarts_always, autostarts_always) {
TAILQ_FOREACH(exec_always, &autostarts_always, autostarts_always) {
LOG("auto-starting (always!) %s\n", exec_always->command);
start_application(exec_always->command, exec_always->no_startup_id);
}
/* Start i3bar processes for all configured bars */
Barconfig *barconfig;
TAILQ_FOREACH (barconfig, &barconfigs, configs) {
TAILQ_FOREACH(barconfig, &barconfigs, configs) {
char *command = NULL;
sasprintf(&command, "%s --bar_id=%s --socket=\"%s\"",
barconfig->i3bar_command ? barconfig->i3bar_command : "i3bar",

View File

@ -56,7 +56,7 @@ void restore_geometry(void) {
DLOG("Restoring geometry\n");
Con *con;
TAILQ_FOREACH (con, &all_cons, all_cons)
TAILQ_FOREACH(con, &all_cons, all_cons)
if (con->window) {
DLOG("Re-adding X11 border of %d px\n", con->border_width);
con->window_rect.width += (2 * con->border_width);
@ -436,11 +436,6 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
if (nc->geometry.width == 0)
nc->geometry = (Rect) {geom->x, geom->y, geom->width, geom->height};
if (want_floating) {
DLOG("geometry = %d x %d\n", nc->geometry.width, nc->geometry.height);
floating_enable(nc, true);
}
if (motif_border_style != BS_NORMAL) {
DLOG("MOTIF_WM_HINTS specifies decorations (border_style = %d)\n", motif_border_style);
if (want_floating) {
@ -450,12 +445,18 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
}
}
if (nc->border_style == BS_PIXEL) {
/* if the border style is BS_PIXEL, explicitly set the border width of
* the new container */
nc->current_border_width = (want_floating ? config.default_floating_border_width : config.default_border_width);
if (want_floating) {
DLOG("geometry = %d x %d\n", nc->geometry.width, nc->geometry.height);
/* automatically set the border to the default value if a motif border
* was not specified */
bool automatic_border = (motif_border_style == BS_NORMAL);
floating_enable(nc, automatic_border);
}
/* explicitly set the border width to the default */
nc->current_border_width = (want_floating ? config.default_floating_border_width : config.default_border_width);
/* to avoid getting an UnmapNotify event due to reparenting, we temporarily
* declare no interest in any state change event of this window */
values[0] = XCB_NONE;
@ -511,7 +512,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
/* Defer setting focus after the 'new' event has been sent to ensure the
* proper window event sequence. */
if (set_focus && nc->mapped) {
if (set_focus && !nc->window->doesnt_accept_focus && nc->mapped) {
DLOG("Now setting focus.\n");
con_focus(nc);
}

View File

@ -136,7 +136,7 @@ bool match_matches_window(Match *match, i3Window *window) {
return false;
}
/* if we find a window that is newer than this one, bail */
TAILQ_FOREACH (con, &all_cons, all_cons) {
TAILQ_FOREACH(con, &all_cons, all_cons) {
if ((con->window != NULL) &&
_i3_timercmp(con->window->urgent, window->urgent, > )) {
return false;
@ -151,7 +151,7 @@ bool match_matches_window(Match *match, i3Window *window) {
return false;
}
/* if we find a window that is older than this one (and not 0), bail */
TAILQ_FOREACH (con, &all_cons, all_cons) {
TAILQ_FOREACH(con, &all_cons, all_cons) {
if ((con->window != NULL) &&
(con->window->urgent.tv_sec != 0) &&
_i3_timercmp(con->window->urgent, window->urgent, < )) {

View File

@ -128,22 +128,21 @@ static void move_to_output_directed(Con *con, direction_t direction) {
tree_flatten(croot);
ipc_send_workspace_focus_event(ws, old_ws);
ipc_send_workspace_event("focus", ws, old_ws);
}
/*
* Moves the current container in the given direction (D_LEFT, D_RIGHT,
* Moves the given container in the given direction (D_LEFT, D_RIGHT,
* D_UP, D_DOWN).
*
*/
void tree_move(int direction) {
void tree_move(Con *con, int direction) {
position_t position;
Con *target;
DLOG("Moving in direction %d\n", direction);
/* 1: get the first parent with the same orientation */
Con *con = focused;
if (con->type == CT_WORKSPACE) {
DLOG("Not moving workspace\n");
@ -206,6 +205,7 @@ void tree_move(int direction) {
TAILQ_INSERT_HEAD(&(swap->parent->focus_head), con, focused);
DLOG("Swapped.\n");
ipc_send_window_event("move", con);
return;
}
@ -213,6 +213,7 @@ void tree_move(int direction) {
/* If we couldn't find a place to move it on this workspace,
* try to move it to a workspace on a different output */
move_to_output_directed(con, direction);
ipc_send_window_event("move", con);
return;
}
@ -264,4 +265,5 @@ end:
FREE(con->deco_render_params);
tree_flatten(croot);
ipc_send_window_event("move", con);
}

View File

@ -18,7 +18,7 @@
Con *output_get_content(Con *output) {
Con *child;
TAILQ_FOREACH (child, &(output->nodes_head), nodes)
TAILQ_FOREACH(child, &(output->nodes_head), nodes)
if (child->type == CT_CON)
return child;

View File

@ -37,7 +37,7 @@ static bool randr_disabled = false;
*/
static Output *get_output_by_id(xcb_randr_output_t id) {
Output *output;
TAILQ_FOREACH (output, &outputs, outputs)
TAILQ_FOREACH(output, &outputs, outputs)
if (output->id == id)
return output;
@ -50,7 +50,7 @@ static Output *get_output_by_id(xcb_randr_output_t id) {
*/
Output *get_output_by_name(const char *name) {
Output *output;
TAILQ_FOREACH (output, &outputs, outputs)
TAILQ_FOREACH(output, &outputs, outputs)
if (output->active &&
strcasecmp(output->name, name) == 0)
return output;
@ -65,7 +65,7 @@ Output *get_output_by_name(const char *name) {
Output *get_first_output(void) {
Output *output;
TAILQ_FOREACH (output, &outputs, outputs)
TAILQ_FOREACH(output, &outputs, outputs)
if (output->active)
return output;
@ -79,7 +79,7 @@ Output *get_first_output(void) {
*/
Output *get_output_containing(unsigned int x, unsigned int y) {
Output *output;
TAILQ_FOREACH (output, &outputs, outputs) {
TAILQ_FOREACH(output, &outputs, outputs) {
if (!output->active)
continue;
DLOG("comparing x=%d y=%d with x=%d and y=%d width %d height %d\n",
@ -104,7 +104,7 @@ bool contained_by_output(Rect rect) {
Output *output;
int lx = rect.x, uy = rect.y;
int rx = rect.x + rect.width, by = rect.y + rect.height;
TAILQ_FOREACH (output, &outputs, outputs) {
TAILQ_FOREACH(output, &outputs, outputs) {
if (!output->active)
continue;
DLOG("comparing x=%d y=%d with x=%d and y=%d width %d height %d\n",
@ -163,7 +163,7 @@ Output *get_output_next(direction_t direction, Output *current, output_close_far
*other;
Output *output,
*best = NULL;
TAILQ_FOREACH (output, &outputs, outputs) {
TAILQ_FOREACH(output, &outputs, outputs) {
if (!output->active)
continue;
@ -256,7 +256,7 @@ void output_init_con(Output *output) {
/* Search for a Con with that name directly below the root node. There
* might be one from a restored layout. */
TAILQ_FOREACH (current, &(croot->nodes_head), nodes) {
TAILQ_FOREACH(current, &(croot->nodes_head), nodes) {
if (strcmp(current->name, output->name) != 0)
continue;
@ -355,13 +355,13 @@ void output_init_con(Output *output) {
void init_ws_for_output(Output *output, Con *content) {
/* go through all assignments and move the existing workspaces to this output */
struct Workspace_Assignment *assignment;
TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) {
TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
if (strcmp(assignment->output, output->name) != 0)
continue;
/* check if this workspace actually exists */
Con *workspace = NULL, *out;
TAILQ_FOREACH (out, &(croot->nodes_head), nodes)
TAILQ_FOREACH(out, &(croot->nodes_head), nodes)
GREP_FIRST(workspace, output_get_content(out),
!strcasecmp(child->name, assignment->name));
if (workspace == NULL)
@ -401,7 +401,7 @@ void init_ws_for_output(Output *output, Con *content) {
Con *ws_out_content = output_get_content(workspace_out);
Con *floating_con;
TAILQ_FOREACH (floating_con, &(workspace->floating_head), floating_windows)
TAILQ_FOREACH(floating_con, &(workspace->floating_head), floating_windows)
/* NB: We use output->con here because content is not yet rendered,
* so it has a rect of {0, 0, 0, 0}. */
floating_fix_coordinates(floating_con, &(ws_out_content->rect), &(output->con->rect));
@ -436,7 +436,7 @@ void init_ws_for_output(Output *output, Con *content) {
}
/* otherwise, we create the first assigned ws for this output */
TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) {
TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
if (strcmp(assignment->output, output->name) != 0)
continue;
@ -478,8 +478,8 @@ static void output_change_mode(xcb_connection_t *conn, Output *output) {
/* Fix the position of all floating windows on this output.
* The 'rect' of each workspace will be updated in src/render.c. */
TAILQ_FOREACH (workspace, &(content->nodes_head), nodes) {
TAILQ_FOREACH (child, &(workspace->floating_head), floating_windows) {
TAILQ_FOREACH(workspace, &(content->nodes_head), nodes) {
TAILQ_FOREACH(child, &(workspace->floating_head), floating_windows) {
floating_fix_coordinates(child, &(workspace->rect), &(output->con->rect));
}
}
@ -488,7 +488,7 @@ static void output_change_mode(xcb_connection_t *conn, Output *output) {
* the workspaces and their childs depending on output resolution. This is
* only done for workspaces with maximum one child. */
if (config.default_orientation == NO_ORIENTATION) {
TAILQ_FOREACH (workspace, &(content->nodes_head), nodes) {
TAILQ_FOREACH(workspace, &(content->nodes_head), nodes) {
/* Workspaces with more than one child are left untouched because
* we do not want to change an existing layout. */
if (con_num_children(workspace) > 1)
@ -640,7 +640,7 @@ void randr_query_outputs(void) {
/* Check for clones, disable the clones and reduce the mode to the
* lowest common mode */
TAILQ_FOREACH (output, &outputs, outputs) {
TAILQ_FOREACH(output, &outputs, outputs) {
if (!output->active || output->to_be_disabled)
continue;
DLOG("output %p / %s, position (%d, %d), checking for clones\n",
@ -681,7 +681,7 @@ void randr_query_outputs(void) {
* necessary because in the next step, a clone might get disabled. Example:
* LVDS1 active, VGA1 gets activated as a clone of LVDS1 (has no con).
* LVDS1 gets disabled. */
TAILQ_FOREACH (output, &outputs, outputs) {
TAILQ_FOREACH(output, &outputs, outputs) {
if (output->active && output->con == NULL) {
DLOG("Need to initialize a Con for output %s\n", output->name);
output_init_con(output);
@ -691,7 +691,7 @@ void randr_query_outputs(void) {
/* Handle outputs which have a new mode or are disabled now (either
* because the user disabled them or because they are clones) */
TAILQ_FOREACH (output, &outputs, outputs) {
TAILQ_FOREACH(output, &outputs, outputs) {
if (output->to_be_disabled) {
output->active = false;
DLOG("Output %s disabled, re-assigning workspaces/docks\n", output->name);
@ -731,7 +731,7 @@ void randr_query_outputs(void) {
con_attach(current, first_content, false);
DLOG("Fixing the coordinates of floating containers\n");
Con *floating_con;
TAILQ_FOREACH (floating_con, &(current->floating_head), floating_windows)
TAILQ_FOREACH(floating_con, &(current->floating_head), floating_windows)
floating_fix_coordinates(floating_con, &(output->con->rect), &(first->con->rect));
DLOG("Done, next\n");
}
@ -745,7 +745,7 @@ void randr_query_outputs(void) {
/* 3: move the dock clients to the first output */
Con *child;
TAILQ_FOREACH (child, &(output->con->nodes_head), nodes) {
TAILQ_FOREACH(child, &(output->con->nodes_head), nodes) {
if (child->type != CT_DOCKAREA)
continue;
DLOG("Handling dock con %p\n", child);
@ -788,7 +788,7 @@ void randr_query_outputs(void) {
get_first_output();
/* Just go through each active output and assign one workspace */
TAILQ_FOREACH (output, &outputs, outputs) {
TAILQ_FOREACH(output, &outputs, outputs) {
if (!output->active)
continue;
Con *content = output_get_content(output->con);
@ -799,7 +799,7 @@ void randr_query_outputs(void) {
}
/* Focus the primary screen, if possible */
TAILQ_FOREACH (output, &outputs, outputs) {
TAILQ_FOREACH(output, &outputs, outputs) {
if (!output->primary || !output->con)
continue;

View File

@ -41,7 +41,7 @@ static void render_l_output(Con *con) {
/* Find the content container and ensure that there is exactly one. Also
* check for any non-CT_DOCKAREA clients. */
Con *content = NULL;
TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
if (child->type == CT_CON) {
if (content != NULL) {
DLOG("More than one CT_CON on output container\n");
@ -77,19 +77,19 @@ static void render_l_output(Con *con) {
/* First pass: determine the height of all CT_DOCKAREAs (the sum of their
* children) and figure out how many pixels we have left for the rest */
TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
if (child->type != CT_DOCKAREA)
continue;
child->rect.height = 0;
TAILQ_FOREACH (dockchild, &(child->nodes_head), nodes)
TAILQ_FOREACH(dockchild, &(child->nodes_head), nodes)
child->rect.height += dockchild->geometry.height;
height -= child->rect.height;
}
/* Second pass: Set the widths/heights */
TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
if (child->type == CT_CON) {
child->rect.x = x;
child->rect.y = y;
@ -232,7 +232,7 @@ void render_con(Con *con, bool render_fullscreen) {
Con *child;
int i = 0, assigned = 0;
int total = con_orientation(con) == HORIZ ? rect.width : rect.height;
TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
double percentage = child->percent > 0.0 ? child->percent : 1.0 / children;
assigned += sizes[i++] = percentage * total;
}
@ -256,7 +256,7 @@ void render_con(Con *con, bool render_fullscreen) {
} else if (con->type == CT_ROOT) {
Con *output;
if (!fullscreen) {
TAILQ_FOREACH (output, &(con->nodes_head), nodes) {
TAILQ_FOREACH(output, &(con->nodes_head), nodes) {
render_con(output, false);
}
}
@ -266,7 +266,7 @@ void render_con(Con *con, bool render_fullscreen) {
* all times. This is important when the user places floating
* windows/containers so that they overlap on another output. */
DLOG("Rendering floating windows:\n");
TAILQ_FOREACH (output, &(con->nodes_head), nodes) {
TAILQ_FOREACH(output, &(con->nodes_head), nodes) {
if (con_is_internal(output))
continue;
/* Get the active workspace of that output */
@ -278,7 +278,7 @@ void render_con(Con *con, bool render_fullscreen) {
Con *workspace = TAILQ_FIRST(&(content->focus_head));
Con *fullscreen = con_get_fullscreen_con(workspace, CF_OUTPUT);
Con *child;
TAILQ_FOREACH (child, &(workspace->floating_head), floating_windows) {
TAILQ_FOREACH(child, &(workspace->floating_head), floating_windows) {
/* Dont render floating windows when there is a fullscreen window
* on that workspace. Necessary to make floating fullscreen work
* correctly (ticket #564). */
@ -298,6 +298,8 @@ void render_con(Con *con, bool render_fullscreen) {
while (transient_con != NULL &&
transient_con->window != NULL &&
transient_con->window->transient_for != XCB_NONE) {
DLOG("transient_con = 0x%08x, transient_con->window->transient_for = 0x%08x, fullscreen_id = 0x%08x\n",
transient_con->window->id, transient_con->window->transient_for, fullscreen->window->id);
if (transient_con->window->transient_for == fullscreen->window->id) {
is_transient_for = true;
break;
@ -331,7 +333,7 @@ void render_con(Con *con, bool render_fullscreen) {
} else {
/* FIXME: refactor this into separate functions: */
Con *child;
TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
assert(children > 0);
/* default layout */
@ -438,7 +440,7 @@ void render_con(Con *con, bool render_fullscreen) {
/* in a stacking or tabbed container, we ensure the focused client is raised */
if (con->layout == L_STACKED || con->layout == L_TABBED) {
TAILQ_FOREACH_REVERSE (child, &(con->focus_head), focus_head, focused)
TAILQ_FOREACH_REVERSE(child, &(con->focus_head), focus_head, focused)
x_raise_con(child);
if ((child = TAILQ_FIRST(&(con->focus_head)))) {
/* By rendering the stacked container again, we handle the case

View File

@ -137,7 +137,7 @@ static void update_placeholder_contents(placeholder_state *state) {
Match *swallows;
int n = 0;
TAILQ_FOREACH (swallows, &(state->con->swallow_head), matches) {
TAILQ_FOREACH(swallows, &(state->con->swallow_head), matches) {
char *serialized = NULL;
#define APPEND_REGEX(re_name) \
@ -197,6 +197,7 @@ static void open_placeholder_window(Con *con) {
/* Set the same name as was stored in the layout file. While perhaps
* slightly confusing in the first instant, this brings additional
* clarity to which placeholder is waiting for which actual window. */
if (con->name != NULL)
xcb_change_property(restore_conn, XCB_PROP_MODE_REPLACE, placeholder,
A__NET_WM_NAME, A_UTF8_STRING, 8, strlen(con->name), con->name);
DLOG("Created placeholder window 0x%08x for leaf container %p / %s\n",
@ -222,10 +223,10 @@ static void open_placeholder_window(Con *con) {
}
Con *child;
TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
open_placeholder_window(child);
}
TAILQ_FOREACH (child, &(con->floating_head), floating_windows) {
TAILQ_FOREACH(child, &(con->floating_head), floating_windows) {
open_placeholder_window(child);
}
}
@ -239,10 +240,10 @@ static void open_placeholder_window(Con *con) {
*/
void restore_open_placeholder_windows(Con *parent) {
Con *child;
TAILQ_FOREACH (child, &(parent->nodes_head), nodes) {
TAILQ_FOREACH(child, &(parent->nodes_head), nodes) {
open_placeholder_window(child);
}
TAILQ_FOREACH (child, &(parent->floating_head), floating_windows) {
TAILQ_FOREACH(child, &(parent->floating_head), floating_windows) {
open_placeholder_window(child);
}
@ -258,7 +259,7 @@ void restore_open_placeholder_windows(Con *parent) {
*/
bool restore_kill_placeholder(xcb_window_t placeholder) {
placeholder_state *state;
TAILQ_FOREACH (state, &state_head, state) {
TAILQ_FOREACH(state, &state_head, state) {
if (state->window != placeholder)
continue;
@ -277,7 +278,7 @@ bool restore_kill_placeholder(xcb_window_t placeholder) {
static void expose_event(xcb_expose_event_t *event) {
placeholder_state *state;
TAILQ_FOREACH (state, &state_head, state) {
TAILQ_FOREACH(state, &state_head, state) {
if (state->window != event->window)
continue;
@ -305,7 +306,7 @@ static void expose_event(xcb_expose_event_t *event) {
*/
static void configure_notify(xcb_configure_notify_event_t *event) {
placeholder_state *state;
TAILQ_FOREACH (state, &state_head, state) {
TAILQ_FOREACH(state, &state_head, state) {
if (state->window != event->window)
continue;

Some files were not shown because too many files have changed in this diff Show More