Merge branch 'next'
This commit is contained in:
commit
fe482cf193
@ -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
20
CONTRIBUTING.md
Normal 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
22
DEPENDS
@ -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.
|
||||
|
@ -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: don’t 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
|
||||
• don’t 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
|
||||
• don’t 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
|
||||
• don’t overwrite the original size of floating windows when changing border
|
||||
• don’t errnously render floating fullscreen windows during restart
|
||||
• ensure floating windows don’t drop out of fullscreen when restarting
|
||||
• don’t overwrite the window’s 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
124
RELEASE-NOTES-4.9
Normal 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 :).
|
||||
|
||||
It’s 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
|
||||
• don’t set input focus if not accepted (fixes problems with xfce4-notifyd)
|
||||
• don’t 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 doesn’t
|
||||
have one
|
||||
• don’t 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
|
@ -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
19
debian/changelog
vendored
@ -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
7
debian/control
vendored
@ -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
|
||||
|
1
debian/i3-wm.manpages
vendored
1
debian/i3-wm.manpages
vendored
@ -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
|
||||
|
20
debian/patches/manpage-x-terminal-emulator.patch
vendored
20
debian/patches/manpage-x-terminal-emulator.patch
vendored
@ -1,20 +0,0 @@
|
||||
Description: list x-terminal-emulator as one of i3-sensible-terminal’s 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
|
2
debian/patches/series
vendored
2
debian/patches/series
vendored
@ -1,2 +0,0 @@
|
||||
use-x-terminal-emulator.patch
|
||||
manpage-x-terminal-emulator.patch
|
25
debian/patches/use-x-terminal-emulator.patch
vendored
25
debian/patches/use-x-terminal-emulator.patch
vendored
@ -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
|
@ -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
|
||||
|
@ -956,10 +956,10 @@ accepted. There are a few things which we don’t 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 don’t submit
|
||||
them in the bugtracker, since all reviews should be done in public at
|
||||
|
@ -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 that’s 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::
|
||||
|
84
docs/ipc
84
docs/ipc
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -92,6 +92,7 @@ my %allowed_keys = map { ($_, 1) } qw(
|
||||
name
|
||||
geometry
|
||||
window_properties
|
||||
mark
|
||||
);
|
||||
|
||||
sub strip_containers {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -5,3 +5,4 @@ Exec=i3
|
||||
TryExec=i3
|
||||
Type=Application
|
||||
X-LightDM-DesktopName=i3
|
||||
DesktopNames=i3
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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 doesn’t 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) {
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
386
i3bar/src/xcb.c
386
i3bar/src/xcb.c
@ -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, don’t even send the workspace
|
||||
* command, otherwise (with workspace auto_back_and_forth) we’d 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, don’t even send the workspace
|
||||
* command, otherwise (with workspace auto_back_and_forth) we’d 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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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]]'.
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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 user’s 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.
|
||||
*
|
||||
|
@ -24,7 +24,7 @@ uint32_t modifiers_from_str(const char *str);
|
||||
* using 'call cfg_foo()' in parser-specs/.*.spec. Useful so that we don’t 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);
|
||||
|
@ -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);
|
||||
|
@ -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. */
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
258
include/queue.h
258
include/queue.h
@ -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)
|
||||
|
@ -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
|
||||
|
@ -21,8 +21,8 @@
|
||||
* (immediately), the application is reparented to init (process-id 1), which
|
||||
* correctly handles childs, so we don’t 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.
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
|
15
libi3/dpi.c
15
libi3/dpi.c
@ -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, let’s do it for them.
|
||||
*/
|
||||
if ((dpi / 96.0) < 1.25)
|
||||
return logical;
|
||||
return ceil((dpi / 96.0) * logical);
|
||||
}
|
||||
|
41
libi3/font.c
41
libi3/font.c
@ -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);
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
43
src/click.c
43
src/click.c
@ -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;
|
||||
|
||||
|
264
src/commands.c
264
src/commands.c
@ -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;
|
||||
|
||||
|
@ -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
199
src/con.c
@ -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
|
||||
* container’s 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 container’s 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 container’s 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;
|
||||
|
25
src/config.c
25
src/config.c
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
103
src/ewmh.c
103
src/ewmh.c
@ -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
|
||||
* don’t 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) {
|
||||
/* I’m 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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
182
src/handlers.c
182
src/handlers.c
@ -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 don’t want it,
|
||||
since it’d 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;
|
||||
|
||||
|
@ -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
160
src/ipc.c
@ -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, "");
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
162
src/main.c
162
src/main.c
@ -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 don’t 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 what’s 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",
|
||||
|
23
src/manage.c
23
src/manage.c
@ -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);
|
||||
}
|
||||
|
@ -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, < )) {
|
||||
|
10
src/move.c
10
src/move.c
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
42
src/randr.c
42
src/randr.c
@ -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;
|
||||
|
||||
|
22
src/render.c
22
src/render.c
@ -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) {
|
||||
/* Don’t 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
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user