Merge branch 'next'
This commit is contained in:
commit
fe482cf193
@ -7,5 +7,4 @@ AlwaysBreakBeforeMultilineStrings: false
|
|||||||
IndentWidth: 4
|
IndentWidth: 4
|
||||||
PointerBindsToType: false
|
PointerBindsToType: false
|
||||||
ColumnLimit: 0
|
ColumnLimit: 0
|
||||||
ForEachMacros: [ TAILQ_FOREACH, TAILQ_FOREACH_REVERSE, SLIST_FOREACH, CIRCLEQ_FOREACH, CIRCLEQ_FOREACH_REVERSE, NODES_FOREACH, NODES_FOREACH_REVERSE ]
|
|
||||||
SpaceBeforeParens: ControlStatements
|
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
|
"min" means minimum required version
|
||||||
"lkgv" means last known good version
|
"lkgv" means last known good version
|
||||||
|
|
||||||
┌─────────────┬────────┬────────┬────────────────────────────────────────┐
|
┌──────────────┬────────┬────────┬────────────────────────────────────────┐
|
||||||
│ dependency │ min. │ lkgv │ URL │
|
│ dependency │ min. │ lkgv │ URL │
|
||||||
├─────────────┼────────┼────────┼────────────────────────────────────────┤
|
├──────────────┼────────┼────────┼────────────────────────────────────────┤
|
||||||
│ pkg-config │ 0.25 │ 0.26 │ http://pkgconfig.freedesktop.org/ │
|
│ pkg-config │ 0.25 │ 0.26 │ http://pkgconfig.freedesktop.org/ │
|
||||||
│ libxcb │ 1.1.93 │ 1.7 │ http://xcb.freedesktop.org/dist/ │
|
│ libxcb │ 1.1.93 │ 1.10 │ http://xcb.freedesktop.org/dist/ │
|
||||||
│ xcb-util │ 0.3.3 │ 0.3.8 │ http://xcb.freedesktop.org/dist/ │
|
│ xcb-util │ 0.3.3 │ 0.4.1 │ http://xcb.freedesktop.org/dist/ │
|
||||||
│ util-cursor³│ 0.0.99 │ 0.0.99 │ 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/ │
|
│ libev │ 4.0 │ 4.11 │ http://libev.schmorp.de/ │
|
||||||
│ yajl │ 2.0.1 │ 2.0.4 │ http://lloyd.github.com/yajl/ │
|
│ 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/ │
|
│ 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/ │
|
│ 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/ │
|
│ 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/ │
|
│ Xlib │ 1.3.3 │ 1.4.3 │ http://ftp.x.org/pub/current/src/lib/ │
|
||||||
│ PCRE │ 8.12 │ 8.12 │ http://www.pcre.org/ │
|
│ PCRE │ 8.12 │ 8.12 │ http://www.pcre.org/ │
|
||||||
│ libsn¹ │ 0.10 │ 0.12 │ http://freedesktop.org/wiki/Software/startup-notification
|
│ libsn¹ │ 0.10 │ 0.12 │ http://freedesktop.org/wiki/Software/startup-notification
|
||||||
│ pango │ 1.30.0 | 1.30.0 │ http://www.pango.org/ │
|
│ pango │ 1.30.0 | 1.30.0 │ http://www.pango.org/ │
|
||||||
│ cairo │ 1.12.2 │ 1.12.2 │ http://cairographics.org/ │
|
│ cairo │ 1.12.2 │ 1.12.2 │ http://cairographics.org/ │
|
||||||
└─────────────┴────────┴────────┴────────────────────────────────────────┘
|
└──────────────┴────────┴────────┴────────────────────────────────────────┘
|
||||||
¹ libsn = libstartup-notification
|
¹ libsn = libstartup-notification
|
||||||
² Pod::Simple is a Perl module required for converting the testsuite
|
² Pod::Simple is a Perl module required for converting the testsuite
|
||||||
documentation to HTML. See http://michael.stapelberg.de/cpan/#Pod::Simple
|
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
|
i3-migrate-config-to-v4 and i3-dmenu-desktop are implemented in Perl, but have
|
||||||
no dependencies besides Perl 5.10.
|
no dependencies besides Perl 5.10.
|
||||||
|
|
||||||
i3-save-tree is also implemented in Perl and needs AnyEvent::I3 and JSON::XS.
|
i3-save-tree is also implemented in Perl and needs AnyEvent::I3 (>= 0.12) and
|
||||||
While i3-save-tree is not required for running i3 itself, it is strongly
|
JSON::XS. While i3-save-tree is not required for running i3 itself, it is
|
||||||
recommended to provide it in distribution packages.
|
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_CFLAGS += $(call cflags_for_lib, xcb-util)
|
||||||
XCB_LIBS += $(call ldflags_for_lib, xcb-util)
|
XCB_LIBS += $(call ldflags_for_lib, xcb-util)
|
||||||
endif
|
endif
|
||||||
|
XCB_XKB_LIBS := $(call ldflags_for_lib, xcb-xkb,xcb-xkb)
|
||||||
|
|
||||||
# XCB keyboard stuff
|
# XCB keyboard stuff
|
||||||
XCB_KBD_CFLAGS := $(call cflags_for_lib, xcb-keysyms)
|
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-xinerama,xcb-xinerama)
|
||||||
XCB_WM_LIBS += $(call ldflags_for_lib, xcb-randr,xcb-randr)
|
XCB_WM_LIBS += $(call ldflags_for_lib, xcb-randr,xcb-randr)
|
||||||
|
|
||||||
# Xlib
|
XKB_COMMON_CFLAGS := $(call cflags_for_lib, xkbcommon,xkbcommon)
|
||||||
X11_CFLAGS := $(call cflags_for_lib, x11)
|
XKB_COMMON_LIBS := $(call ldflags_for_lib, xkbcommon,xkbcommon)
|
||||||
X11_LIBS := $(call ldflags_for_lib, x11,X11)
|
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
|
||||||
XCURSOR_CFLAGS := $(call cflags_for_lib, xcb-cursor)
|
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
|
* 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
|
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-randr0-dev,
|
||||||
libxcb-icccm4-dev,
|
libxcb-icccm4-dev,
|
||||||
libxcb-cursor-dev,
|
libxcb-cursor-dev,
|
||||||
|
libxcb-xkb-dev,
|
||||||
|
libxkbcommon-dev (>= 0.4.0),
|
||||||
|
libxkbcommon-x11-dev (>= 0.4.0),
|
||||||
asciidoc (>= 8.4.4),
|
asciidoc (>= 8.4.4),
|
||||||
xmlto,
|
xmlto,
|
||||||
docbook-xml,
|
docbook-xml,
|
||||||
@ -21,7 +24,7 @@ Build-Depends: debhelper (>= 7.0.50~),
|
|||||||
libcairo2-dev,
|
libcairo2-dev,
|
||||||
libpango1.0-dev,
|
libpango1.0-dev,
|
||||||
libpod-simple-perl
|
libpod-simple-perl
|
||||||
Standards-Version: 3.9.4
|
Standards-Version: 3.9.5
|
||||||
Homepage: http://i3wm.org/
|
Homepage: http://i3wm.org/
|
||||||
|
|
||||||
Package: i3
|
Package: i3
|
||||||
@ -38,7 +41,7 @@ Architecture: any
|
|||||||
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, x11-utils
|
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, x11-utils
|
||||||
Provides: x-window-manager
|
Provides: x-window-manager
|
||||||
Suggests: rxvt-unicode | x-terminal-emulator
|
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
|
Description: improved dynamic tiling window manager
|
||||||
Key features of i3 are good documentation, reasonable defaults (changeable in
|
Key features of i3 are good documentation, reasonable defaults (changeable in
|
||||||
a simple configuration file) and good multi-monitor support. The user
|
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-editor.1
|
||||||
man/i3-sensible-terminal.1
|
man/i3-sensible-terminal.1
|
||||||
man/i3-dmenu-desktop.1
|
man/i3-dmenu-desktop.1
|
||||||
|
man/i3-save-tree.1
|
||||||
man/i3bar.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
|
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.
|
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
|
To upload a compressed version of the logfile (for a bugreport), use:
|
||||||
bugreport), use:
|
------------------------------------------------------------------------------
|
||||||
--------------------------------------------------------------------
|
DISPLAY=:0 i3-dump-log | bzip2 -c | curl --data-binary @- http://logs.i3wm.org
|
||||||
DISPLAY=:0 i3-dump-log | bzip2 -c > /tmp/i3.log.bz2
|
------------------------------------------------------------------------------
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
This command does not depend on i3 (it also works while i3 displays
|
This command does not depend on i3 (it also works while i3 displays
|
||||||
the crash dialog), but it requires a working X11 connection.
|
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
|
== On crashes: Obtaining a backtrace
|
||||||
|
|
||||||
When i3 crashes, it will display a dialog stating “i3 just crashed”, offering
|
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.
|
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
|
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
|
it in the corresponding bugreport at https://github.com/i3/i3/issues In case
|
||||||
bugreport yet, please create one.
|
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
|
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
|
them in the bugtracker, since all reviews should be done in public at
|
||||||
|
@ -119,7 +119,8 @@ click_events::
|
|||||||
full_text::
|
full_text::
|
||||||
The most simple block you can think of is one which just includes the
|
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
|
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::
|
short_text::
|
||||||
Where appropriate, the +short_text+ (string) entry should also be
|
Where appropriate, the +short_text+ (string) entry should also be
|
||||||
provided. It will be used in case the status line needs to be shortened
|
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
|
when you want to set a sensible minimum width regardless of which font you
|
||||||
are using, and at what particular size.
|
are using, and at what particular size.
|
||||||
align::
|
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
|
the minimum width of the latter, specified by the +min_width+ key, is not
|
||||||
reached.
|
reached.
|
||||||
name and instance::
|
name and instance::
|
||||||
|
84
docs/ipc
84
docs/ipc
@ -1,7 +1,7 @@
|
|||||||
IPC interface (interprocess communication)
|
IPC interface (interprocess communication)
|
||||||
==========================================
|
==========================================
|
||||||
Michael Stapelberg <michael@i3wm.org>
|
Michael Stapelberg <michael@i3wm.org>
|
||||||
February 2014
|
October 2014
|
||||||
|
|
||||||
This document describes how to interface with i3 from a separate process. This
|
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
|
is useful for example to remote-control i3 (to write test cases for example) or
|
||||||
@ -156,7 +156,7 @@ following properties:
|
|||||||
|
|
||||||
num (integer)::
|
num (integer)::
|
||||||
The logical number of the workspace. Corresponds to the command
|
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)::
|
name (string)::
|
||||||
The name of this workspace (by default num+1), as changed by the
|
The name of this workspace (by default num+1), as changed by the
|
||||||
user. Encoded in UTF-8.
|
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
|
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,
|
each side, making the window_rect +{ "x": 2, "y": 0, "width": 632,
|
||||||
"height": 366 }+ (for example).
|
"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)::
|
geometry (map)::
|
||||||
The original geometry the window specified when i3 mapped it. Used when
|
The original geometry the window specified when i3 mapped it. Used when
|
||||||
switching a window to floating mode, for example.
|
switching a window to floating mode, for example.
|
||||||
@ -613,7 +617,7 @@ you can register to an event.
|
|||||||
*Example:*
|
*Example:*
|
||||||
---------------------------------
|
---------------------------------
|
||||||
type: SUBSCRIBE
|
type: SUBSCRIBE
|
||||||
payload: [ "workspace", "focus" ]
|
payload: [ "workspace", "output" ]
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
|
|
||||||
@ -638,6 +642,9 @@ window (3)::
|
|||||||
barconfig_update (4)::
|
barconfig_update (4)::
|
||||||
Sent when the hidden_state or mode field in the barconfig of any bar
|
Sent when the hidden_state or mode field in the barconfig of any bar
|
||||||
instance was updated and when the config is reloaded.
|
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:*
|
*Example:*
|
||||||
--------------------------------------------------------------------
|
--------------------------------------------------------------------
|
||||||
@ -661,15 +668,16 @@ if ($is_event) {
|
|||||||
|
|
||||||
This event consists of a single serialized map containing a property
|
This event consists of a single serialized map containing a property
|
||||||
+change (string)+ which indicates the type of the change ("focus", "init",
|
+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
|
When the change is "focus", an +old (object)+ property will be present with the
|
||||||
(object)+ properties will be present with the previous and current
|
previous workspace. When the first switch occurs (when i3 focuses the
|
||||||
workspace respectively. When the first switch occurs (when i3 focuses
|
workspace visible at the beginning) there is no previous workspace, and the
|
||||||
the workspace visible at the beginning) there is no previous
|
+old+ property will be set to +null+. Also note that if the previous is empty
|
||||||
workspace, and the +old+ property will be set to +null+. Also note
|
it will get destroyed when switching, but will still be present in the "old"
|
||||||
that if the previous is empty it will get destroyed when switching,
|
property.
|
||||||
but will still be present in the "old" property.
|
|
||||||
|
|
||||||
*Example:*
|
*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
|
+change (string)+ which indicates the type of the change
|
||||||
|
|
||||||
* +new+ - the window has become managed by i3
|
* +new+ - the window has become managed by i3
|
||||||
|
* +close+ - the window has closed
|
||||||
* +focus+ - the window has received input focus
|
* +focus+ - the window has received input focus
|
||||||
* +title+ - the window's title has changed
|
* +title+ - the window's title has changed
|
||||||
* +fullscreen_mode+ - the window has entered or exited fullscreen mode
|
* +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
|
Additionally a +container (object)+ field will be present, which consists
|
||||||
of the window's parent container. Be aware that for the "new" event, the
|
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
|
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.
|
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)
|
== See also (existing libraries)
|
||||||
|
|
||||||
[[libraries]]
|
[[libraries]]
|
||||||
@ -754,15 +807,14 @@ all this on your own). This list names some (if you wrote one, please let me
|
|||||||
know):
|
know):
|
||||||
|
|
||||||
C::
|
C::
|
||||||
i3 includes a headerfile +i3/ipc.h+ which provides you all constants.
|
* i3 includes a headerfile +i3/ipc.h+ which provides you all constants.
|
||||||
|
* https://github.com/acrisci/i3ipc-glib
|
||||||
https://github.com/acrisci/i3ipc-glib
|
|
||||||
Go::
|
Go::
|
||||||
* https://github.com/proxypoke/i3ipc
|
* https://github.com/proxypoke/i3ipc
|
||||||
JavaScript::
|
JavaScript::
|
||||||
* https://github.com/acrisci/i3ipc-gjs
|
* https://github.com/acrisci/i3ipc-gjs
|
||||||
Lua::
|
Lua::
|
||||||
* https:/github.com/acrisci/i3ipc-lua
|
* https://github.com/acrisci/i3ipc-lua
|
||||||
Perl::
|
Perl::
|
||||||
* https://metacpan.org/module/AnyEvent::I3
|
* https://metacpan.org/module/AnyEvent::I3
|
||||||
Python::
|
Python::
|
||||||
@ -770,4 +822,4 @@ Python::
|
|||||||
* https://github.com/whitelynx/i3ipc (not maintained)
|
* https://github.com/whitelynx/i3ipc (not maintained)
|
||||||
* https://github.com/ziberna/i3-py (not maintained)
|
* https://github.com/ziberna/i3-py (not maintained)
|
||||||
Ruby::
|
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
|
used to install the testsuite. Many users prefer to use the more modern
|
||||||
+cpanminus+ instead, though (because it asks no questions and just works):
|
+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)
|
.Installing testsuite dependencies using cpanminus (preferred)
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
$ cd ~/i3/testcases
|
$ 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
|
with an appropriate configuration file and creates a folder for each run
|
||||||
containing the appropriate i3 logfile for each testcase. The latest folder can
|
containing the appropriate i3 logfile for each testcase. The latest folder can
|
||||||
always be found under the symlink +latest/+. Unless told differently, it will
|
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+
|
.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:
|
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
|
This will show the output of Xephyr, which is the X server implementation we
|
||||||
installed. Under Debian and Ubuntu this is the package
|
use for testing.
|
||||||
+xserver-xorg-video-dummy+.
|
|
||||||
|
|
||||||
==== IPC interface
|
==== IPC interface
|
||||||
|
|
||||||
@ -175,10 +182,9 @@ manager.
|
|||||||
=== Filesystem structure
|
=== Filesystem structure
|
||||||
|
|
||||||
In the git root of i3, the testcases live in the folder +testcases+. This
|
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
|
folder contains the +complete-run.pl+ and a base configuration file which will
|
||||||
configuration file which will be used for the tests. The different testcases
|
be used for the tests. The different testcases (their file extension is .t, not
|
||||||
(their file extension is .t, not .pl) themselves can be found in the
|
.pl) themselves can be found in the conventionally named subfolder +t+:
|
||||||
conventionally named subfolder +t+:
|
|
||||||
|
|
||||||
.Filesystem structure
|
.Filesystem structure
|
||||||
--------------------------------------------
|
--------------------------------------------
|
||||||
@ -197,7 +203,6 @@ conventionally named subfolder +t+:
|
|||||||
│ │ ├── omitted for brevity
|
│ │ ├── omitted for brevity
|
||||||
│ │ ├── ...
|
│ │ ├── ...
|
||||||
│ │ └── 74-regress-focus-toggle.t
|
│ │ └── 74-regress-focus-toggle.t
|
||||||
│ └── Xdummy
|
|
||||||
--------------------------------------------
|
--------------------------------------------
|
||||||
|
|
||||||
== Anatomy of a testcase
|
== 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+.
|
press +$mod+f+.
|
||||||
|
|
||||||
There is also a global fullscreen mode in i3 in which the client will span all
|
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
|
=== Opening other applications
|
||||||
|
|
||||||
@ -153,6 +153,7 @@ to upgrade to a newer version of i3) you can use +$mod+Shift+r+.
|
|||||||
=== Exiting i3
|
=== Exiting i3
|
||||||
|
|
||||||
To cleanly exit i3 without killing your X server, you can use +$mod+Shift+e+.
|
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
|
=== Floating
|
||||||
|
|
||||||
@ -366,7 +367,7 @@ bindcode [--release] [Modifiers+]keycode command
|
|||||||
*Examples*:
|
*Examples*:
|
||||||
--------------------------------
|
--------------------------------
|
||||||
# Fullscreen
|
# Fullscreen
|
||||||
bindsym $mod+f fullscreen
|
bindsym $mod+f fullscreen toggle
|
||||||
|
|
||||||
# Restart
|
# Restart
|
||||||
bindsym $mod+Shift+r 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
|
bindings. For example, when typing, capslock+1 or capslock+2 for switching
|
||||||
workspaces is totally convenient. Try it :-).
|
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]]
|
[[floating_modifier]]
|
||||||
|
|
||||||
=== The floating modifier
|
=== The floating modifier
|
||||||
@ -1069,6 +1105,27 @@ bar {
|
|||||||
|
|
||||||
Available modifiers are Mod1-Mod5, Shift, Control (see +xmodmap(1)+).
|
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
|
=== Bar ID
|
||||||
|
|
||||||
Specifies the bar ID for the configured bar instance. If this option is missing,
|
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,
|
or +layout splith+ to change the current container layout to splith/splitv,
|
||||||
stacking, tabbed layout, splitv or splith, respectively.
|
stacking, tabbed layout, splitv or splith, respectively.
|
||||||
|
|
||||||
To make the current window (!) fullscreen, use +fullscreen+, to make
|
To make the current window (!) fullscreen, use +fullscreen enable+ (or
|
||||||
it floating (or tiling again) use +floating enable+ respectively +floating disable+
|
+fullscreen enable global+ for the global mode), to leave either fullscreen
|
||||||
(or +floating toggle+):
|
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*:
|
*Syntax*:
|
||||||
--------------
|
--------------
|
||||||
@ -1469,7 +1530,7 @@ bindsym $mod+x layout toggle
|
|||||||
bindsym $mod+x layout toggle all
|
bindsym $mod+x layout toggle all
|
||||||
|
|
||||||
# Toggle fullscreen
|
# Toggle fullscreen
|
||||||
bindsym $mod+f fullscreen
|
bindsym $mod+f fullscreen toggle
|
||||||
|
|
||||||
# Toggle floating/tiling
|
# Toggle floating/tiling
|
||||||
bindsym $mod+t floating toggle
|
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
|
See <<move_to_outputs>> for how to move a container/workspace to a different
|
||||||
RandR output.
|
RandR output.
|
||||||
|
|
||||||
|
Workspace names are parsed as
|
||||||
|
https://developer.gnome.org/pango/stable/PangoMarkupFormat.html[Pango markup]
|
||||||
|
by i3bar.
|
||||||
|
|
||||||
[[back_and_forth]]
|
[[back_and_forth]]
|
||||||
To switch back to the previously focused workspace, use +workspace
|
To switch back to the previously focused workspace, use +workspace
|
||||||
back_and_forth+; likewise, you can move containers to the previously focused
|
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+1 workspace 1
|
||||||
bindsym $mod+2 workspace 2
|
bindsym $mod+2 workspace 2
|
||||||
|
bindsym $mod+3 workspace 3:<span foreground="red">vim</span>
|
||||||
...
|
...
|
||||||
|
|
||||||
bindsym $mod+Shift+1 move container to workspace 1
|
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_SOURCES := $(wildcard i3-config-wizard/*.c)
|
||||||
i3_config_wizard_HEADERS := $(wildcard i3-config-wizard/*.h)
|
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_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(PANGO_CFLAGS) $(XKB_COMMON_CFLAGS) $(XKB_COMMON_X11_CFLAGS)
|
||||||
i3_config_wizard_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(X11_LIBS) $(PANGO_LIBS)
|
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)
|
i3_config_wizard_OBJECTS := $(i3_config_wizard_SOURCES:.c=.o)
|
||||||
|
|
||||||
|
@ -43,6 +43,9 @@
|
|||||||
#include <xcb/xcb_event.h>
|
#include <xcb/xcb_event.h>
|
||||||
#include <xcb/xcb_keysyms.h>
|
#include <xcb/xcb_keysyms.h>
|
||||||
|
|
||||||
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
#include <xkbcommon/xkbcommon-x11.h>
|
||||||
|
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#include <X11/keysym.h>
|
#include <X11/keysym.h>
|
||||||
#include <X11/XKBlib.h>
|
#include <X11/XKBlib.h>
|
||||||
@ -83,7 +86,9 @@ static xcb_pixmap_t pixmap;
|
|||||||
static xcb_gcontext_t pixmap_gc;
|
static xcb_gcontext_t pixmap_gc;
|
||||||
static xcb_key_symbols_t *symbols;
|
static xcb_key_symbols_t *symbols;
|
||||||
xcb_window_t root;
|
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();
|
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
|
* This reduces a lot of confusion for users who switch keyboard
|
||||||
* layouts from qwerty to qwertz or other slight variations of
|
* layouts from qwerty to qwertz or other slight variations of
|
||||||
* qwerty (yes, that happens quite often). */
|
* qwerty (yes, that happens quite often). */
|
||||||
KeySym sym = XkbKeycodeToKeysym(dpy, keycode, 0, 0);
|
const xkb_keysym_t *syms;
|
||||||
if (!keysym_used_on_other_key(sym, keycode))
|
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;
|
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");
|
const char *release = get_string("release");
|
||||||
char *res;
|
char *res;
|
||||||
char *modrep = (modifiers == NULL ? sstrdup("") : sstrdup(modifiers));
|
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() {
|
static void finish() {
|
||||||
printf("creating \"%s\"...\n", config_path);
|
printf("creating \"%s\"...\n", config_path);
|
||||||
|
|
||||||
if (!(dpy = XOpenDisplay(NULL)))
|
struct xkb_context *xkb_context;
|
||||||
errx(1, "Could not connect to X11");
|
|
||||||
|
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");
|
FILE *kc_config = fopen(SYSCONFDIR "/i3/config.keycodes", "r");
|
||||||
if (kc_config == NULL)
|
if (kc_config == NULL)
|
||||||
@ -797,6 +820,16 @@ int main(int argc, char *argv[]) {
|
|||||||
xcb_connection_has_error(conn))
|
xcb_connection_has_error(conn))
|
||||||
errx(1, "Cannot open display\n");
|
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)
|
if (socket_path == NULL)
|
||||||
socket_path = root_atom_contents("I3_SOCKET_PATH", conn, screen);
|
socket_path = root_atom_contents("I3_SOCKET_PATH", conn, screen);
|
||||||
|
|
||||||
|
@ -3,6 +3,6 @@
|
|||||||
/* from X11/keysymdef.h */
|
/* from X11/keysymdef.h */
|
||||||
#define XCB_NUM_LOCK 0xff7f
|
#define XCB_NUM_LOCK 0xff7f
|
||||||
|
|
||||||
#define xmacro(atom) xcb_atom_t A_ ## atom;
|
#define xmacro(atom) xcb_atom_t A_##atom;
|
||||||
#include "atoms.xmacro"
|
#include "atoms.xmacro"
|
||||||
#undef xmacro
|
#undef xmacro
|
||||||
|
@ -3,12 +3,12 @@
|
|||||||
#include <err.h>
|
#include <err.h>
|
||||||
|
|
||||||
#define die(...) errx(EXIT_FAILURE, __VA_ARGS__);
|
#define die(...) errx(EXIT_FAILURE, __VA_ARGS__);
|
||||||
#define FREE(pointer) do { \
|
#define FREE(pointer) \
|
||||||
|
do { \
|
||||||
if (pointer != NULL) { \
|
if (pointer != NULL) { \
|
||||||
free(pointer); \
|
free(pointer); \
|
||||||
pointer = NULL; \
|
pointer = NULL; \
|
||||||
} \
|
} \
|
||||||
} \
|
} while (0)
|
||||||
while (0)
|
|
||||||
|
|
||||||
extern xcb_window_t root;
|
extern xcb_window_t root;
|
||||||
|
@ -3,15 +3,15 @@
|
|||||||
#include <err.h>
|
#include <err.h>
|
||||||
|
|
||||||
#define die(...) errx(EXIT_FAILURE, __VA_ARGS__);
|
#define die(...) errx(EXIT_FAILURE, __VA_ARGS__);
|
||||||
#define FREE(pointer) do { \
|
#define FREE(pointer) \
|
||||||
|
do { \
|
||||||
if (pointer != NULL) { \
|
if (pointer != NULL) { \
|
||||||
free(pointer); \
|
free(pointer); \
|
||||||
pointer = NULL; \
|
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"
|
#include "atoms.xmacro"
|
||||||
#undef xmacro
|
#undef xmacro
|
||||||
|
|
||||||
|
@ -92,6 +92,7 @@ my %allowed_keys = map { ($_, 1) } qw(
|
|||||||
name
|
name
|
||||||
geometry
|
geometry
|
||||||
window_properties
|
window_properties
|
||||||
|
mark
|
||||||
);
|
);
|
||||||
|
|
||||||
sub strip_containers {
|
sub strip_containers {
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
# Hopefully one of these is installed (no flamewars about preference please!):
|
# 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
|
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 "$@"
|
exec $editor "$@"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
# We don't use 'more' because it will exit if the file is too short.
|
# 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.
|
# Worst case scenario we'll open the file in your editor.
|
||||||
for pager in $PAGER less most w3m i3-sensible-editor; do
|
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 "$@"
|
exec $pager "$@"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
# This script tries to exec a terminal emulator by trying some known terminal
|
# This script tries to exec a terminal emulator by trying some known terminal
|
||||||
# emulators.
|
# emulators.
|
||||||
#
|
#
|
||||||
# Distributions/packagers should enhance this script with a
|
# We welcome patches that add distribution-specific mechanisms to find the
|
||||||
# distribution-specific mechanism to find the preferred terminal emulator. On
|
# preferred terminal emulator. On Debian, there is the x-terminal-emulator
|
||||||
# Debian, there is the x-terminal-emulator symlink for example.
|
# 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 termite lxterminal; do
|
||||||
if which $terminal > /dev/null 2>&1; then
|
if command -v $terminal > /dev/null 2>&1; then
|
||||||
exec $terminal "$@"
|
exec $terminal "$@"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
@ -75,7 +75,7 @@ bindsym Mod1+h split h
|
|||||||
bindsym Mod1+v split v
|
bindsym Mod1+v split v
|
||||||
|
|
||||||
# enter fullscreen mode for the focused container
|
# enter fullscreen mode for the focused container
|
||||||
bindsym Mod1+f fullscreen
|
bindsym Mod1+f fullscreen toggle
|
||||||
|
|
||||||
# change container layout (stacked, tabbed, toggle split)
|
# change container layout (stacked, tabbed, toggle split)
|
||||||
bindsym Mod1+s layout stacking
|
bindsym Mod1+s layout stacking
|
||||||
|
@ -69,7 +69,7 @@ bindcode $mod+43 split h
|
|||||||
bindcode $mod+55 split v
|
bindcode $mod+55 split v
|
||||||
|
|
||||||
# enter fullscreen mode for the focused container
|
# enter fullscreen mode for the focused container
|
||||||
bindcode $mod+41 fullscreen
|
bindcode $mod+41 fullscreen toggle
|
||||||
|
|
||||||
# change container layout (stacked, tabbed, toggle split)
|
# change container layout (stacked, tabbed, toggle split)
|
||||||
bindcode $mod+39 layout stacking
|
bindcode $mod+39 layout stacking
|
||||||
|
@ -5,3 +5,4 @@ Exec=i3
|
|||||||
TryExec=i3
|
TryExec=i3
|
||||||
Type=Application
|
Type=Application
|
||||||
X-LightDM-DesktopName=i3
|
X-LightDM-DesktopName=i3
|
||||||
|
DesktopNames=i3
|
||||||
|
@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3bar
|
|||||||
|
|
||||||
i3bar_SOURCES := $(wildcard i3bar/src/*.c)
|
i3bar_SOURCES := $(wildcard i3bar/src/*.c)
|
||||||
i3bar_HEADERS := $(wildcard i3bar/include/*.h)
|
i3bar_HEADERS := $(wildcard i3bar/include/*.h)
|
||||||
i3bar_CFLAGS = $(XCB_CFLAGS) $(X11_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS)
|
i3bar_CFLAGS = $(XCB_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS)
|
||||||
i3bar_LIBS = $(XCB_LIBS) $(X11_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS)
|
i3bar_LIBS = $(XCB_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(XCB_XKB_LIBS)
|
||||||
|
|
||||||
i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o)
|
i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o)
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
typedef struct rect_t rect;
|
typedef struct rect_t rect;
|
||||||
|
|
||||||
struct ev_loop* main_loop;
|
struct ev_loop *main_loop;
|
||||||
char *statusline;
|
char *statusline;
|
||||||
char *statusline_buffer;
|
char *statusline_buffer;
|
||||||
|
|
||||||
@ -27,6 +27,7 @@ struct rect_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
/* First value to make it the default. */
|
||||||
ALIGN_LEFT,
|
ALIGN_LEFT,
|
||||||
ALIGN_CENTER,
|
ALIGN_CENTER,
|
||||||
ALIGN_RIGHT
|
ALIGN_RIGHT
|
||||||
|
@ -18,10 +18,14 @@ typedef enum {
|
|||||||
} position_t;
|
} position_t;
|
||||||
|
|
||||||
/* Bar display mode (hide unless modifier is pressed or show in dock mode or always hide in invisible mode) */
|
/* 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 {
|
typedef struct config_t {
|
||||||
int modifier;
|
int modifier;
|
||||||
|
char *wheel_up_cmd;
|
||||||
|
char *wheel_down_cmd;
|
||||||
position_t position;
|
position_t position;
|
||||||
int verbose;
|
int verbose;
|
||||||
struct xcb_color_strings_t colors;
|
struct xcb_color_strings_t colors;
|
||||||
@ -38,7 +42,8 @@ typedef struct config_t {
|
|||||||
bar_display_mode_t hide_on_modifier;
|
bar_display_mode_t hide_on_modifier;
|
||||||
|
|
||||||
/* The current hidden_state of the bar, which indicates whether it is hidden or shown */
|
/* 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_t config;
|
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)
|
* 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
|
* Subscribe to all the i3-events, we need
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
typedef struct i3_output i3_output;
|
typedef struct i3_output i3_output;
|
||||||
|
|
||||||
SLIST_HEAD(outputs_head, i3_output);
|
SLIST_HEAD(outputs_head, i3_output);
|
||||||
struct outputs_head *outputs;
|
struct outputs_head* outputs;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start parsing the received json-string
|
* Start parsing the received json-string
|
||||||
@ -40,6 +40,7 @@ struct i3_output {
|
|||||||
char* name; /* Name of the output */
|
char* name; /* Name of the output */
|
||||||
bool active; /* If the output is active */
|
bool active; /* If the output is active */
|
||||||
bool primary; /* If it is the primary output */
|
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 */
|
int ws; /* The number of the currently visible ws */
|
||||||
rect rect; /* The rect (relative to the root-win) */
|
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_pixmap_t buffer; /* An extra pixmap for double-buffering */
|
||||||
xcb_gcontext_t bargc; /* The graphical context of the bar */
|
xcb_gcontext_t bargc; /* The graphical context of the bar */
|
||||||
|
|
||||||
struct ws_head *workspaces; /* The workspaces on this output */
|
struct ws_head* workspaces; /* The workspaces on this output */
|
||||||
struct tc_head *trayclients; /* The tray clients on this output */
|
struct tc_head* trayclients; /* The tray clients on this output */
|
||||||
|
|
||||||
SLIST_ENTRY(i3_output) slist; /* Pointer for the SLIST-Macro */
|
SLIST_ENTRY(i3_output) slist; /* Pointer for the SLIST-Macro */
|
||||||
};
|
};
|
||||||
|
@ -11,55 +11,60 @@
|
|||||||
|
|
||||||
/* Get the maximum/minimum of x and y */
|
/* Get the maximum/minimum of x and y */
|
||||||
#undef MAX
|
#undef MAX
|
||||||
#define MAX(x,y) ((x) > (y) ? (x) : (y))
|
#define MAX(x, y) ((x) > (y) ? (x) : (y))
|
||||||
#undef MIN
|
#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)
|
#define STARTS_WITH(string, len, needle) ((len >= strlen(needle)) && strncasecmp(string, needle, strlen(needle)) == 0)
|
||||||
|
|
||||||
/* Securely free p */
|
/* Securely free p */
|
||||||
#define FREE(p) do { \
|
#define FREE(p) \
|
||||||
|
do { \
|
||||||
if (p != NULL) { \
|
if (p != NULL) { \
|
||||||
free(p); \
|
free(p); \
|
||||||
p = NULL; \
|
p = NULL; \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/* Securely fee single-linked list */
|
/* Securely fee single-linked list */
|
||||||
#define FREE_SLIST(l, type) do { \
|
#define FREE_SLIST(l, type) \
|
||||||
|
do { \
|
||||||
type *walk = SLIST_FIRST(l); \
|
type *walk = SLIST_FIRST(l); \
|
||||||
while (!SLIST_EMPTY(l)) { \
|
while (!SLIST_EMPTY(l)) { \
|
||||||
SLIST_REMOVE_HEAD(l, slist); \
|
SLIST_REMOVE_HEAD(l, slist); \
|
||||||
FREE(walk); \
|
FREE(walk); \
|
||||||
walk = SLIST_FIRST(l); \
|
walk = SLIST_FIRST(l); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/* Securely fee tail-queues */
|
/* Securely fee tail-queues */
|
||||||
#define FREE_TAILQ(l, type) do { \
|
#define FREE_TAILQ(l, type) \
|
||||||
|
do { \
|
||||||
type *walk = TAILQ_FIRST(l); \
|
type *walk = TAILQ_FIRST(l); \
|
||||||
while (!TAILQ_EMPTY(l)) { \
|
while (!TAILQ_EMPTY(l)) { \
|
||||||
TAILQ_REMOVE(l, TAILQ_FIRST(l), tailq); \
|
TAILQ_REMOVE(l, TAILQ_FIRST(l), tailq); \
|
||||||
FREE(walk); \
|
FREE(walk); \
|
||||||
walk = TAILQ_FIRST(l); \
|
walk = TAILQ_FIRST(l); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#if defined(DLOG)
|
#if defined(DLOG)
|
||||||
#undef DLOG
|
#undef DLOG
|
||||||
#endif
|
#endif
|
||||||
/* Use cool logging-macros */
|
/* Use cool logging-macros */
|
||||||
#define DLOG(fmt, ...) do { \
|
#define DLOG(fmt, ...) \
|
||||||
|
do { \
|
||||||
if (config.verbose) { \
|
if (config.verbose) { \
|
||||||
printf("[%s:%d] " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
|
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 will include libi3.h which define its own version of ELOG.
|
||||||
* We want *our* version, so we undef the libi3 one. */
|
* We want *our* version, so we undef the libi3 one. */
|
||||||
#if defined(ELOG)
|
#if defined(ELOG)
|
||||||
#undef ELOG
|
#undef ELOG
|
||||||
#endif
|
#endif
|
||||||
#define ELOG(fmt, ...) do { \
|
#define ELOG(fmt, ...) \
|
||||||
|
do { \
|
||||||
fprintf(stderr, "[%s:%d] ERROR: " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
|
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) {
|
static int stdin_string(void *context, const unsigned char *val, size_t len) {
|
||||||
parser_ctx *ctx = context;
|
parser_ctx *ctx = context;
|
||||||
if (strcasecmp(ctx->last_map_key, "full_text") == 0) {
|
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) {
|
if (strcasecmp(ctx->last_map_key, "color") == 0) {
|
||||||
sasprintf(&(ctx->block.color), "%.*s", len, val);
|
sasprintf(&(ctx->block.color), "%.*s", len, val);
|
||||||
}
|
}
|
||||||
if (strcasecmp(ctx->last_map_key, "align") == 0) {
|
if (strcasecmp(ctx->last_map_key, "align") == 0) {
|
||||||
if (len == strlen("left") && !strncmp((const char *)val, "left", strlen("left"))) {
|
if (len == strlen("center") && !strncmp((const char *)val, "center", strlen("center"))) {
|
||||||
ctx->block.align = ALIGN_LEFT;
|
ctx->block.align = ALIGN_CENTER;
|
||||||
} else if (len == strlen("right") && !strncmp((const char *)val, "right", strlen("right"))) {
|
} else if (len == strlen("right") && !strncmp((const char *)val, "right", strlen("right"))) {
|
||||||
ctx->block.align = ALIGN_RIGHT;
|
ctx->block.align = ALIGN_RIGHT;
|
||||||
} else {
|
} else {
|
||||||
ctx->block.align = ALIGN_CENTER;
|
ctx->block.align = ALIGN_LEFT;
|
||||||
}
|
}
|
||||||
} else if (strcasecmp(ctx->last_map_key, "min_width") == 0) {
|
} 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);
|
ctx->block.min_width = (uint32_t)predict_text_width(text);
|
||||||
i3string_free(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),
|
/* 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. */
|
* i3bar doesn’t crash and the user gets an annoying message. */
|
||||||
if (!new_block->full_text)
|
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)
|
if (new_block->urgent)
|
||||||
ctx->has_urgent = true;
|
ctx->has_urgent = true;
|
||||||
TAILQ_INSERT_TAIL(&statusline_head, new_block, blocks);
|
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) {
|
static int stdin_end_array(void *context) {
|
||||||
DLOG("dumping statusline:\n");
|
DLOG("dumping statusline:\n");
|
||||||
struct status_block *current;
|
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("full_text = %s\n", i3string_as_utf8(current->full_text));
|
||||||
DLOG("color = %s\n", current->color);
|
DLOG("color = %s\n", current->color);
|
||||||
}
|
}
|
||||||
@ -304,7 +304,7 @@ static void read_flat_input(char *buffer, int length) {
|
|||||||
buffer[length - 1] = '\0';
|
buffer[length - 1] = '\0';
|
||||||
else
|
else
|
||||||
buffer[length] = '\0';
|
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) {
|
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;
|
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")) {
|
if (!strcmp(cur_key, "position")) {
|
||||||
DLOG("position = %.*s\n", len, val);
|
DLOG("position = %.*s\n", len, val);
|
||||||
config.position = (len == 3 && !strncmp((const char *)val, "top", strlen("top")) ? POS_TOP : POS_BOT);
|
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);
|
reconfig_windows(false);
|
||||||
|
|
||||||
i3_output *o_walk;
|
i3_output *o_walk;
|
||||||
SLIST_FOREACH (o_walk, outputs, slist) {
|
SLIST_FOREACH(o_walk, outputs, slist) {
|
||||||
kick_tray_clients(o_walk);
|
kick_tray_clients(o_walk);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,6 +159,9 @@ void got_bar_config_update(char *event) {
|
|||||||
if (found_id == NULL)
|
if (found_id == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* reconfigure the bar based on the current outputs */
|
||||||
|
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
|
||||||
|
|
||||||
free_colors(&(config.colors));
|
free_colors(&(config.colors));
|
||||||
|
|
||||||
/* update the configuration with the received settings */
|
/* update the configuration with the received settings */
|
||||||
@ -169,6 +172,8 @@ void got_bar_config_update(char *event) {
|
|||||||
reconfig_windows(true);
|
reconfig_windows(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* update fonts and colors */
|
||||||
|
init_xcb_late(config.fontname);
|
||||||
init_colors(&(config.colors));
|
init_colors(&(config.colors));
|
||||||
realloc_sl_buffer();
|
realloc_sl_buffer();
|
||||||
|
|
||||||
|
@ -296,7 +296,7 @@ i3_output *get_output_by_name(char *name) {
|
|||||||
if (name == NULL) {
|
if (name == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
SLIST_FOREACH (walk, outputs, slist) {
|
SLIST_FOREACH(walk, outputs, slist) {
|
||||||
if (!strcmp(walk->name, name)) {
|
if (!strcmp(walk->name, name)) {
|
||||||
break;
|
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 */
|
/* Offset may be equal to length, in which case display the number */
|
||||||
params->workspaces_walk->name = (offset < len
|
params->workspaces_walk->name = (offset < len
|
||||||
? i3string_from_utf8_with_length(ws_name + offset, len - offset)
|
? i3string_from_markup_with_length(ws_name + offset, len - offset)
|
||||||
: i3string_from_utf8(ws_num));
|
: i3string_from_markup(ws_num));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
/* Default case: just save the name */
|
/* 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 */
|
/* Save its rendered width */
|
||||||
@ -266,9 +266,9 @@ void free_workspaces(void) {
|
|||||||
}
|
}
|
||||||
i3_ws *ws_walk;
|
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)) {
|
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);
|
I3STRING_FREE(ws_walk->name);
|
||||||
FREE(ws_walk->canonical_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/xcb.h>
|
||||||
|
#include <xcb/xkb.h>
|
||||||
#include <xcb/xproto.h>
|
#include <xcb/xproto.h>
|
||||||
#include <xcb/xcb_aux.h>
|
#include <xcb/xcb_aux.h>
|
||||||
|
|
||||||
@ -63,8 +64,7 @@ static i3Font font;
|
|||||||
int bar_height;
|
int bar_height;
|
||||||
|
|
||||||
/* These are only relevant for XKB, which we only need for grabbing modifiers */
|
/* These are only relevant for XKB, which we only need for grabbing modifiers */
|
||||||
Display *xkb_dpy;
|
int xkb_base;
|
||||||
int xkb_event_base;
|
|
||||||
int mod_pressed = 0;
|
int mod_pressed = 0;
|
||||||
|
|
||||||
/* Because the statusline is the same on all outputs, we have
|
/* 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;
|
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
|
* Redraws the statusline to the buffer
|
||||||
*
|
*
|
||||||
@ -128,7 +134,7 @@ void refresh_statusline(void) {
|
|||||||
statusline_width = 0;
|
statusline_width = 0;
|
||||||
|
|
||||||
/* Predict the text width of all blocks (in pixels). */
|
/* 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)
|
if (i3string_get_num_bytes(block->full_text) == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -148,15 +154,15 @@ void refresh_statusline(void) {
|
|||||||
block->x_offset = padding_width;
|
block->x_offset = padding_width;
|
||||||
break;
|
break;
|
||||||
case ALIGN_CENTER:
|
case ALIGN_CENTER:
|
||||||
block->x_offset = padding_width / logical_px(2);
|
block->x_offset = padding_width / 2;
|
||||||
block->x_append = padding_width / logical_px(2) + padding_width % logical_px(2);
|
block->x_append = padding_width / 2 + padding_width % 2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If this is not the last block, add some pixels for a separator. */
|
/* If this is not the last block, add some pixels for a separator. */
|
||||||
if (TAILQ_NEXT(block, blocks) != NULL)
|
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;
|
statusline_width += block->width + block->x_offset + block->x_append;
|
||||||
}
|
}
|
||||||
@ -168,30 +174,49 @@ void refresh_statusline(void) {
|
|||||||
realloc_sl_buffer();
|
realloc_sl_buffer();
|
||||||
|
|
||||||
/* Clear the statusline pixmap. */
|
/* 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);
|
xcb_poly_fill_rectangle(xcb_connection, statusline_pm, statusline_clear, 1, &rect);
|
||||||
|
|
||||||
/* Draw the text of each block. */
|
/* Draw the text of each block. */
|
||||||
uint32_t x = 0;
|
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)
|
if (i3string_get_num_bytes(block->full_text) == 0)
|
||||||
continue;
|
continue;
|
||||||
|
uint32_t fg_color;
|
||||||
|
|
||||||
uint32_t colorpixel = (block->color ? get_colorpixel(block->color) : colors.bar_fg);
|
/* If this block is urgent, draw it with the defined color and border. */
|
||||||
set_font_colors(statusline_ctx, colorpixel, colors.bar_bg);
|
if (block->urgent) {
|
||||||
draw_text(block->full_text, statusline_pm, statusline_ctx, x + block->x_offset, 1, block->width);
|
fg_color = colors.urgent_ws_fg;
|
||||||
x += block->width + block->x_offset + block->x_append;
|
|
||||||
|
|
||||||
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. */
|
/* 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 mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_LINE_WIDTH;
|
||||||
uint32_t values[] = {colors.sep_fg, colors.bar_bg, logical_px(1)};
|
uint32_t values[] = {colors.sep_fg, colors.bar_bg, logical_px(1)};
|
||||||
xcb_change_gc(xcb_connection, statusline_ctx, mask, values);
|
xcb_change_gc(xcb_connection, statusline_ctx, mask, values);
|
||||||
xcb_poly_line(xcb_connection, XCB_COORD_MODE_ORIGIN, statusline_pm,
|
xcb_poly_line(xcb_connection, XCB_COORD_MODE_ORIGIN, statusline_pm,
|
||||||
statusline_ctx, 2,
|
statusline_ctx, 2,
|
||||||
(xcb_point_t[]) {{x - sep_offset, 2},
|
(xcb_point_t[]) { { x - sep_offset, logical_px(4) },
|
||||||
{x - sep_offset, font.height - 2}});
|
{ x - sep_offset, bar_height - logical_px(4) } });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -206,7 +231,7 @@ void hide_bars(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
i3_output *walk;
|
i3_output *walk;
|
||||||
SLIST_FOREACH (walk, outputs, slist) {
|
SLIST_FOREACH(walk, outputs, slist) {
|
||||||
if (!walk->active) {
|
if (!walk->active) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -231,7 +256,7 @@ void unhide_bars(void) {
|
|||||||
|
|
||||||
cont_child();
|
cont_child();
|
||||||
|
|
||||||
SLIST_FOREACH (walk, outputs, slist) {
|
SLIST_FOREACH(walk, outputs, slist) {
|
||||||
if (walk->bar == XCB_NONE) {
|
if (walk->bar == XCB_NONE) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -303,7 +328,7 @@ void handle_button(xcb_button_press_event_t *event) {
|
|||||||
/* Determine, which bar was clicked */
|
/* Determine, which bar was clicked */
|
||||||
i3_output *walk;
|
i3_output *walk;
|
||||||
xcb_window_t bar = event->event;
|
xcb_window_t bar = event->event;
|
||||||
SLIST_FOREACH (walk, outputs, slist) {
|
SLIST_FOREACH(walk, outputs, slist) {
|
||||||
if (walk->bar == bar) {
|
if (walk->bar == bar) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -326,27 +351,36 @@ void handle_button(xcb_button_press_event_t *event) {
|
|||||||
/* First calculate width of tray area */
|
/* First calculate width of tray area */
|
||||||
trayclient *trayclient;
|
trayclient *trayclient;
|
||||||
int tray_width = 0;
|
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)
|
if (!trayclient->mapped)
|
||||||
continue;
|
continue;
|
||||||
tray_width += (font.height + logical_px(2));
|
tray_width += (font.height + logical_px(2));
|
||||||
}
|
}
|
||||||
|
if (tray_width > 0)
|
||||||
|
tray_width += logical_px(2);
|
||||||
|
|
||||||
int block_x = 0, last_block_x;
|
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;
|
x = original_x - offset;
|
||||||
if (x >= 0) {
|
if (x >= 0) {
|
||||||
struct status_block *block;
|
struct status_block *block;
|
||||||
|
int sep_offset_remainder = 0;
|
||||||
|
|
||||||
TAILQ_FOREACH (block, &statusline_head, blocks) {
|
TAILQ_FOREACH (block, &statusline_head, blocks) {
|
||||||
|
if (i3string_get_num_bytes(block->full_text) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
last_block_x = block_x;
|
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) {
|
if (x <= block_x && x >= last_block_x) {
|
||||||
send_block_clicked(event->detail, block->name, block->instance, event->root_x, event->root_y);
|
send_block_clicked(event->detail, block->name, block->instance, event->root_x, event->root_y);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sep_offset_remainder = block->sep_block_width - get_sep_offset(block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
x = original_x;
|
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
|
* If there is no more workspace, don’t even send the workspace
|
||||||
* command, otherwise (with workspace auto_back_and_forth) we’d end
|
* command, otherwise (with workspace auto_back_and_forth) we’d end
|
||||||
* up on the wrong workspace. */
|
* 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))
|
if (cur_ws == TAILQ_FIRST(walk->workspaces))
|
||||||
return;
|
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
|
* If there is no more workspace, don’t even send the workspace
|
||||||
* command, otherwise (with workspace auto_back_and_forth) we’d end
|
* command, otherwise (with workspace auto_back_and_forth) we’d end
|
||||||
* up on the wrong workspace. */
|
* 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))
|
if (cur_ws == TAILQ_LAST(walk->workspaces, ws_head))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -387,7 +437,7 @@ void handle_button(xcb_button_press_event_t *event) {
|
|||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
/* Check if this event regards a workspace button */
|
/* 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);
|
DLOG("x = %d\n", x);
|
||||||
if (x >= 0 && x < cur_ws->name_width + logical_px(10)) {
|
if (x >= 0 && x < cur_ws->name_width + logical_px(10)) {
|
||||||
break;
|
break;
|
||||||
@ -398,7 +448,7 @@ void handle_button(xcb_button_press_event_t *event) {
|
|||||||
/* Otherwise, focus our currently visible workspace if it is not
|
/* Otherwise, focus our currently visible workspace if it is not
|
||||||
* already focused */
|
* already focused */
|
||||||
if (cur_ws == NULL) {
|
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)
|
if (cur_ws->visible && !cur_ws->focused)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -446,6 +496,39 @@ void handle_button(xcb_button_press_event_t *event) {
|
|||||||
free(buffer);
|
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
|
* 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
|
* 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) {
|
static void configure_trayclients(void) {
|
||||||
trayclient *trayclient;
|
trayclient *trayclient;
|
||||||
i3_output *output;
|
i3_output *output;
|
||||||
SLIST_FOREACH (output, outputs, slist) {
|
SLIST_FOREACH(output, outputs, slist) {
|
||||||
if (!output->active)
|
if (!output->active)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int clients = 0;
|
int clients = 0;
|
||||||
TAILQ_FOREACH_REVERSE (trayclient, output->trayclients, tc_head, tailq) {
|
TAILQ_FOREACH_REVERSE(trayclient, output->trayclients, tc_head, tailq) {
|
||||||
if (!trayclient->mapped)
|
if (!trayclient->mapped)
|
||||||
continue;
|
continue;
|
||||||
clients++;
|
clients++;
|
||||||
@ -544,7 +627,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
|
|||||||
|
|
||||||
DLOG("X window %08x requested docking\n", client);
|
DLOG("X window %08x requested docking\n", client);
|
||||||
i3_output *walk, *output = NULL;
|
i3_output *walk, *output = NULL;
|
||||||
SLIST_FOREACH (walk, outputs, slist) {
|
SLIST_FOREACH(walk, outputs, slist) {
|
||||||
if (!walk->active)
|
if (!walk->active)
|
||||||
continue;
|
continue;
|
||||||
if (config.tray_output) {
|
if (config.tray_output) {
|
||||||
@ -562,7 +645,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
|
|||||||
if (output == NULL &&
|
if (output == NULL &&
|
||||||
config.tray_output &&
|
config.tray_output &&
|
||||||
strcasecmp("primary", config.tray_output) == 0) {
|
strcasecmp("primary", config.tray_output) == 0) {
|
||||||
SLIST_FOREACH (walk, outputs, slist) {
|
SLIST_FOREACH(walk, outputs, slist) {
|
||||||
if (!walk->active)
|
if (!walk->active)
|
||||||
continue;
|
continue;
|
||||||
DLOG("Falling back to output %s because no primary output is configured\n", walk->name);
|
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);
|
DLOG("DestroyNotify for window = %08x, event = %08x\n", event->window, event->event);
|
||||||
|
|
||||||
i3_output *walk;
|
i3_output *walk;
|
||||||
SLIST_FOREACH (walk, outputs, slist) {
|
SLIST_FOREACH(walk, outputs, slist) {
|
||||||
if (!walk->active)
|
if (!walk->active)
|
||||||
continue;
|
continue;
|
||||||
DLOG("checking output %s\n", walk->name);
|
DLOG("checking output %s\n", walk->name);
|
||||||
trayclient *trayclient;
|
trayclient *trayclient;
|
||||||
TAILQ_FOREACH (trayclient, walk->trayclients, tailq) {
|
TAILQ_FOREACH(trayclient, walk->trayclients, tailq) {
|
||||||
if (trayclient->win != event->window)
|
if (trayclient->win != event->window)
|
||||||
continue;
|
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);
|
DLOG("MapNotify for window = %08x, event = %08x\n", event->window, event->event);
|
||||||
|
|
||||||
i3_output *walk;
|
i3_output *walk;
|
||||||
SLIST_FOREACH (walk, outputs, slist) {
|
SLIST_FOREACH(walk, outputs, slist) {
|
||||||
if (!walk->active)
|
if (!walk->active)
|
||||||
continue;
|
continue;
|
||||||
DLOG("checking output %s\n", walk->name);
|
DLOG("checking output %s\n", walk->name);
|
||||||
trayclient *trayclient;
|
trayclient *trayclient;
|
||||||
TAILQ_FOREACH (trayclient, walk->trayclients, tailq) {
|
TAILQ_FOREACH(trayclient, walk->trayclients, tailq) {
|
||||||
if (trayclient->win != event->window)
|
if (trayclient->win != event->window)
|
||||||
continue;
|
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);
|
DLOG("UnmapNotify for window = %08x, event = %08x\n", event->window, event->event);
|
||||||
|
|
||||||
i3_output *walk;
|
i3_output *walk;
|
||||||
SLIST_FOREACH (walk, outputs, slist) {
|
SLIST_FOREACH(walk, outputs, slist) {
|
||||||
if (!walk->active)
|
if (!walk->active)
|
||||||
continue;
|
continue;
|
||||||
DLOG("checking output %s\n", walk->name);
|
DLOG("checking output %s\n", walk->name);
|
||||||
trayclient *trayclient;
|
trayclient *trayclient;
|
||||||
TAILQ_FOREACH (trayclient, walk->trayclients, tailq) {
|
TAILQ_FOREACH(trayclient, walk->trayclients, tailq) {
|
||||||
if (trayclient->win != event->window)
|
if (trayclient->win != event->window)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -739,11 +822,11 @@ static void handle_property_notify(xcb_property_notify_event_t *event) {
|
|||||||
DLOG("xembed_info updated\n");
|
DLOG("xembed_info updated\n");
|
||||||
trayclient *trayclient = NULL, *walk;
|
trayclient *trayclient = NULL, *walk;
|
||||||
i3_output *o_walk;
|
i3_output *o_walk;
|
||||||
SLIST_FOREACH (o_walk, outputs, slist) {
|
SLIST_FOREACH(o_walk, outputs, slist) {
|
||||||
if (!o_walk->active)
|
if (!o_walk->active)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
TAILQ_FOREACH (walk, o_walk->trayclients, tailq) {
|
TAILQ_FOREACH(walk, o_walk->trayclients, tailq) {
|
||||||
if (walk->win != event->window)
|
if (walk->win != event->window)
|
||||||
continue;
|
continue;
|
||||||
trayclient = walk;
|
trayclient = walk;
|
||||||
@ -802,12 +885,12 @@ static void handle_configure_request(xcb_configure_request_event_t *event) {
|
|||||||
|
|
||||||
trayclient *trayclient;
|
trayclient *trayclient;
|
||||||
i3_output *output;
|
i3_output *output;
|
||||||
SLIST_FOREACH (output, outputs, slist) {
|
SLIST_FOREACH(output, outputs, slist) {
|
||||||
if (!output->active)
|
if (!output->active)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int clients = 0;
|
int clients = 0;
|
||||||
TAILQ_FOREACH_REVERSE (trayclient, output->trayclients, tc_head, tailq) {
|
TAILQ_FOREACH_REVERSE(trayclient, output->trayclients, tc_head, tailq) {
|
||||||
if (!trayclient->mapped)
|
if (!trayclient->mapped)
|
||||||
continue;
|
continue;
|
||||||
clients++;
|
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) {
|
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:
|
case XCB_EXPOSE:
|
||||||
/* Expose-events happen, when the window needs to be redrawn */
|
/* Expose-events happen, when the window needs to be redrawn */
|
||||||
redraw_bars();
|
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) {
|
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
|
* Early initialization of the connection to X11: Everything which does not
|
||||||
* depend on 'config'.
|
* depend on 'config'.
|
||||||
@ -1053,44 +1125,23 @@ char *init_xcb_early() {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void register_xkb_keyevents() {
|
void register_xkb_keyevents() {
|
||||||
if (xkb_dpy == NULL) {
|
const xcb_query_extension_reply_t *extreply;
|
||||||
int xkb_major, xkb_minor, xkb_errbase, xkb_err;
|
extreply = xcb_get_extension_data(conn, &xcb_xkb_id);
|
||||||
xkb_major = XkbMajorVersion;
|
if (!extreply->present) {
|
||||||
xkb_minor = XkbMinorVersion;
|
ELOG("xkb is not present on this server\n");
|
||||||
|
|
||||||
xkb_dpy = XkbOpenDisplay(NULL,
|
|
||||||
&xkb_event_base,
|
|
||||||
&xkb_errbase,
|
|
||||||
&xkb_major,
|
|
||||||
&xkb_minor,
|
|
||||||
&xkb_err);
|
|
||||||
|
|
||||||
if (xkb_dpy == NULL) {
|
|
||||||
ELOG("No XKB!\n");
|
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
DLOG("initializing xcb-xkb\n");
|
||||||
if (fcntl(ConnectionNumber(xkb_dpy), F_SETFD, FD_CLOEXEC) == -1) {
|
xcb_xkb_use_extension(conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION);
|
||||||
ELOG("Could not set FD_CLOEXEC on xkbdpy: %s\n", strerror(errno));
|
xcb_xkb_select_events(conn,
|
||||||
exit(EXIT_FAILURE);
|
XCB_XKB_ID_USE_CORE_KBD,
|
||||||
}
|
XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
|
||||||
|
0,
|
||||||
int i1;
|
XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
|
||||||
if (!XkbQueryExtension(xkb_dpy, &i1, &xkb_event_base, &xkb_errbase, &xkb_major, &xkb_minor)) {
|
0xff,
|
||||||
ELOG("XKB not supported by X-server!\n");
|
0xff,
|
||||||
exit(EXIT_FAILURE);
|
NULL);
|
||||||
}
|
xkb_base = extreply->first_event;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1098,13 +1149,14 @@ void register_xkb_keyevents() {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void deregister_xkb_keyevents() {
|
void deregister_xkb_keyevents() {
|
||||||
if (xkb_dpy != NULL) {
|
xcb_xkb_select_events(conn,
|
||||||
ev_io_stop(main_loop, xkb_io);
|
XCB_XKB_ID_USE_CORE_KBD,
|
||||||
XCloseDisplay(xkb_dpy);
|
0,
|
||||||
close(xkb_io->fd);
|
0,
|
||||||
FREE(xkb_io);
|
0,
|
||||||
xkb_dpy = NULL;
|
0xff,
|
||||||
}
|
0xff,
|
||||||
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1280,7 +1332,7 @@ void init_tray_colors(void) {
|
|||||||
void clean_xcb(void) {
|
void clean_xcb(void) {
|
||||||
i3_output *o_walk;
|
i3_output *o_walk;
|
||||||
free_workspaces();
|
free_workspaces();
|
||||||
SLIST_FOREACH (o_walk, outputs, slist) {
|
SLIST_FOREACH(o_walk, outputs, slist) {
|
||||||
destroy_window(o_walk);
|
destroy_window(o_walk);
|
||||||
FREE(o_walk->trayclients);
|
FREE(o_walk->trayclients);
|
||||||
FREE(o_walk->workspaces);
|
FREE(o_walk->workspaces);
|
||||||
@ -1435,7 +1487,7 @@ void reconfig_windows(bool redraw_bars) {
|
|||||||
static bool tray_configured = false;
|
static bool tray_configured = false;
|
||||||
|
|
||||||
i3_output *walk;
|
i3_output *walk;
|
||||||
SLIST_FOREACH (walk, outputs, slist) {
|
SLIST_FOREACH(walk, outputs, slist) {
|
||||||
if (!walk->active) {
|
if (!walk->active) {
|
||||||
/* If an output is not active, we destroy its bar */
|
/* If an output is not active, we destroy its bar */
|
||||||
/* FIXME: Maybe we rather want to unmap? */
|
/* FIXME: Maybe we rather want to unmap? */
|
||||||
@ -1462,6 +1514,12 @@ void reconfig_windows(bool redraw_bars) {
|
|||||||
values[2] = XCB_EVENT_MASK_EXPOSURE |
|
values[2] = XCB_EVENT_MASK_EXPOSURE |
|
||||||
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
|
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
|
||||||
XCB_EVENT_MASK_BUTTON_PRESS;
|
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,
|
xcb_void_cookie_t win_cookie = xcb_create_window_checked(xcb_connection,
|
||||||
root_screen->root_depth,
|
root_screen->root_depth,
|
||||||
walk->bar,
|
walk->bar,
|
||||||
@ -1595,7 +1653,7 @@ void reconfig_windows(bool redraw_bars) {
|
|||||||
* VGA-1 but output == [HDMI-1]).
|
* VGA-1 but output == [HDMI-1]).
|
||||||
*/
|
*/
|
||||||
i3_output *output;
|
i3_output *output;
|
||||||
SLIST_FOREACH (output, outputs, slist) {
|
SLIST_FOREACH(output, outputs, slist) {
|
||||||
if (strcasecmp(output->name, tray_output) == 0 ||
|
if (strcasecmp(output->name, tray_output) == 0 ||
|
||||||
(strcasecmp(tray_output, "primary") == 0 && output->primary)) {
|
(strcasecmp(tray_output, "primary") == 0 && output->primary)) {
|
||||||
init_tray();
|
init_tray();
|
||||||
@ -1684,7 +1742,7 @@ void draw_bars(bool unhide) {
|
|||||||
refresh_statusline();
|
refresh_statusline();
|
||||||
|
|
||||||
i3_output *outputs_walk;
|
i3_output *outputs_walk;
|
||||||
SLIST_FOREACH (outputs_walk, outputs, slist) {
|
SLIST_FOREACH(outputs_walk, outputs, slist) {
|
||||||
if (!outputs_walk->active) {
|
if (!outputs_walk->active) {
|
||||||
DLOG("Output %s inactive, skipping...\n", outputs_walk->name);
|
DLOG("Output %s inactive, skipping...\n", outputs_walk->name);
|
||||||
continue;
|
continue;
|
||||||
@ -1714,29 +1772,29 @@ void draw_bars(bool unhide) {
|
|||||||
* position */
|
* position */
|
||||||
trayclient *trayclient;
|
trayclient *trayclient;
|
||||||
int traypx = 0;
|
int traypx = 0;
|
||||||
TAILQ_FOREACH (trayclient, outputs_walk->trayclients, tailq) {
|
TAILQ_FOREACH(trayclient, outputs_walk->trayclients, tailq) {
|
||||||
if (!trayclient->mapped)
|
if (!trayclient->mapped)
|
||||||
continue;
|
continue;
|
||||||
/* We assume the tray icons are quadratic (we use the font
|
/* We assume the tray icons are quadratic (we use the font
|
||||||
* *height* as *width* of the icons) because we configured them
|
* *height* as *width* of the icons) because we configured them
|
||||||
* like this. */
|
* like this. */
|
||||||
traypx += font.height + 2;
|
traypx += font.height + logical_px(2);
|
||||||
}
|
}
|
||||||
/* Add 2px of padding if there are any tray icons */
|
/* Add 2px of padding if there are any tray icons */
|
||||||
if (traypx > 0)
|
if (traypx > 0)
|
||||||
traypx += 2;
|
traypx += logical_px(2);
|
||||||
xcb_copy_area(xcb_connection,
|
xcb_copy_area(xcb_connection,
|
||||||
statusline_pm,
|
statusline_pm,
|
||||||
outputs_walk->buffer,
|
outputs_walk->buffer,
|
||||||
outputs_walk->bargc,
|
outputs_walk->bargc,
|
||||||
MAX(0, (int16_t)(statusline_width - outputs_walk->rect.w + 4)), 0,
|
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 - 4)), 3,
|
MAX(0, (int16_t)(outputs_walk->rect.w - statusline_width - traypx - logical_px(4))), 0,
|
||||||
MIN(outputs_walk->rect.w - traypx - 4, (int)statusline_width), font.height + 2);
|
MIN(outputs_walk->rect.w - traypx - logical_px(4), (int)statusline_width), bar_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config.disable_ws) {
|
if (!config.disable_ws) {
|
||||||
i3_ws *ws_walk;
|
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",
|
DLOG("Drawing Button for WS %s at x = %d, len = %d\n",
|
||||||
i3string_as_utf8(ws_walk->name), i, ws_walk->name_width);
|
i3string_as_utf8(ws_walk->name), i, ws_walk->name_width);
|
||||||
uint32_t fg_color = colors.inactive_ws_fg;
|
uint32_t fg_color = colors.inactive_ws_fg;
|
||||||
@ -1852,7 +1910,7 @@ void draw_bars(bool unhide) {
|
|||||||
*/
|
*/
|
||||||
void redraw_bars(void) {
|
void redraw_bars(void) {
|
||||||
i3_output *outputs_walk;
|
i3_output *outputs_walk;
|
||||||
SLIST_FOREACH (outputs_walk, outputs, slist) {
|
SLIST_FOREACH(outputs_walk, outputs, slist) {
|
||||||
if (!outputs_walk->active) {
|
if (!outputs_walk->active) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
xmacro(_NET_SUPPORTED)
|
xmacro(_NET_SUPPORTED)
|
||||||
xmacro(_NET_SUPPORTING_WM_CHECK)
|
xmacro(_NET_SUPPORTING_WM_CHECK)
|
||||||
xmacro(_NET_WM_NAME)
|
xmacro(_NET_WM_NAME)
|
||||||
|
xmacro(_NET_WM_MOVERESIZE)
|
||||||
xmacro(_NET_WM_STATE_FULLSCREEN)
|
xmacro(_NET_WM_STATE_FULLSCREEN)
|
||||||
xmacro(_NET_WM_STATE_DEMANDS_ATTENTION)
|
xmacro(_NET_WM_STATE_DEMANDS_ATTENTION)
|
||||||
xmacro(_NET_WM_STATE_MODAL)
|
xmacro(_NET_WM_STATE_MODAL)
|
||||||
@ -16,7 +17,11 @@ xmacro(_NET_WM_STRUT_PARTIAL)
|
|||||||
xmacro(_NET_CLIENT_LIST)
|
xmacro(_NET_CLIENT_LIST)
|
||||||
xmacro(_NET_CLIENT_LIST_STACKING)
|
xmacro(_NET_CLIENT_LIST_STACKING)
|
||||||
xmacro(_NET_CURRENT_DESKTOP)
|
xmacro(_NET_CURRENT_DESKTOP)
|
||||||
|
xmacro(_NET_NUMBER_OF_DESKTOPS)
|
||||||
|
xmacro(_NET_DESKTOP_NAMES)
|
||||||
|
xmacro(_NET_DESKTOP_VIEWPORT)
|
||||||
xmacro(_NET_ACTIVE_WINDOW)
|
xmacro(_NET_ACTIVE_WINDOW)
|
||||||
|
xmacro(_NET_CLOSE_WINDOW)
|
||||||
xmacro(_NET_STARTUP_ID)
|
xmacro(_NET_STARTUP_ID)
|
||||||
xmacro(_NET_WORKAREA)
|
xmacro(_NET_WORKAREA)
|
||||||
xmacro(WM_PROTOCOLS)
|
xmacro(WM_PROTOCOLS)
|
||||||
@ -34,3 +39,4 @@ xmacro(I3_PID)
|
|||||||
xmacro(_NET_REQUEST_FRAME_EXTENTS)
|
xmacro(_NET_REQUEST_FRAME_EXTENTS)
|
||||||
xmacro(_NET_FRAME_EXTENTS)
|
xmacro(_NET_FRAME_EXTENTS)
|
||||||
xmacro(_MOTIF_WM_HINTS)
|
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,
|
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)
|
* 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);
|
void check_for_duplicate_bindings(struct context *context);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs the given binding and handles parse errors. Returns a CommandResult for
|
* Frees the binding. If bind is null, it simply returns.
|
||||||
* running the binding's command. Caller should render tree if
|
*/
|
||||||
* needs_tree_render is true. Free with command_result_free().
|
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);
|
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]]'.
|
* Implementation of 'move <direction> [<pixels> [px]]'.
|
||||||
|
@ -44,6 +44,14 @@ struct CommandResult {
|
|||||||
bool needs_tree_render;
|
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
|
* 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
|
* 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);
|
Con *con_new_skeleton(Con *parent, i3Window *window);
|
||||||
|
|
||||||
|
|
||||||
/* A wrapper for con_new_skeleton, to retain the old con_new behaviour
|
/* 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);
|
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
|
* Moves the given container to the currently focused container on the given
|
||||||
* workspace.
|
* workspace.
|
||||||
@ -345,3 +356,9 @@ void con_set_urgency(Con *con, bool urgent);
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
char *con_get_tree_representation(Con *con);
|
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;
|
char *socket_path;
|
||||||
|
|
||||||
/** Bar display mode (hide unless modifier is pressed or show in dock mode or always hide in invisible mode) */
|
/** 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 */
|
/* 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). */
|
/** Bar modifier (to show bar when in hide mode). */
|
||||||
enum {
|
enum {
|
||||||
@ -258,8 +261,17 @@ struct Barconfig {
|
|||||||
M_MOD5 = 7
|
M_MOD5 = 7
|
||||||
} modifier;
|
} 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). */
|
/** 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
|
/** Command that should be run to execute i3bar, give a full path if i3bar is not
|
||||||
* in your $PATH.
|
* in your $PATH.
|
||||||
@ -314,6 +326,20 @@ struct Barconfig {
|
|||||||
TAILQ_ENTRY(Barconfig) configs;
|
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.
|
* 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
|
* using 'call cfg_foo()' in parser-specs/.*.spec. Useful so that we don’t need
|
||||||
* to repeat the definition all the time. */
|
* to repeat the definition all the time. */
|
||||||
#define CFGFUN(name, ...) \
|
#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
|
/* The following functions are called by the config parser, see
|
||||||
* parser-specs/config.spec. They get the parsed parameters and store them in
|
* 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(floating_modifier, const char *modifiers);
|
||||||
CFGFUN(new_window, const char *windowtype, const char *border, const long width);
|
CFGFUN(new_window, const char *windowtype, const char *border, const long width);
|
||||||
CFGFUN(workspace, const char *workspace, const char *output);
|
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(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_font, const char *font);
|
||||||
CFGFUN(bar_mode, const char *mode);
|
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_output, const char *output);
|
||||||
CFGFUN(bar_verbose, const char *verbose);
|
CFGFUN(bar_verbose, const char *verbose);
|
||||||
CFGFUN(bar_modifier, const char *modifier);
|
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_position, const char *position);
|
||||||
CFGFUN(bar_i3bar_command, const char *i3bar_command);
|
CFGFUN(bar_i3bar_command, const char *i3bar_command);
|
||||||
CFGFUN(bar_color, const char *colorclass, const char *border, const char *background, const char *text);
|
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
|
* 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 Assignment Assignment;
|
||||||
typedef struct Window i3Window;
|
typedef struct Window i3Window;
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* Helper types
|
* Helper types
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
typedef enum { D_LEFT, D_RIGHT, D_UP, D_DOWN } direction_t;
|
typedef enum { D_LEFT,
|
||||||
typedef enum { NO_ORIENTATION = 0, HORIZ, VERT } orientation_t;
|
D_RIGHT,
|
||||||
typedef enum { BS_NORMAL = 0, BS_NONE = 1, BS_PIXEL = 2 } border_style_t;
|
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
|
/** parameter to specify whether tree_close() and x_window_kill() should kill
|
||||||
* only this specific window or the whole X11 client */
|
* 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. */
|
/** describes if the window is adjacent to the output (physical screen) edges. */
|
||||||
typedef enum { ADJ_NONE = 0,
|
typedef enum { ADJ_NONE = 0,
|
||||||
ADJ_LEFT_SCREEN_EDGE = (1 << 0),
|
ADJ_LEFT_SCREEN_EDGE = (1 << 0),
|
||||||
ADJ_RIGHT_SCREEN_EDGE = (1 << 1),
|
ADJ_RIGHT_SCREEN_EDGE = (1 << 1),
|
||||||
ADJ_UPPER_SCREEN_EDGE = (1 << 2),
|
ADJ_UPPER_SCREEN_EDGE = (1 << 2),
|
||||||
ADJ_LOWER_SCREEN_EDGE = (1 << 4)} adjacent_t;
|
ADJ_LOWER_SCREEN_EDGE = (1 << 4) } adjacent_t;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
BIND_NONE = 0,
|
BIND_NONE = 0,
|
||||||
@ -247,6 +255,11 @@ struct Binding {
|
|||||||
B_UPON_KEYRELEASE_IGNORE_MODS = 2,
|
B_UPON_KEYRELEASE_IGNORE_MODS = 2,
|
||||||
} release;
|
} 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;
|
uint32_t number_keycodes;
|
||||||
|
|
||||||
/** Keycode to bind */
|
/** Keycode to bind */
|
||||||
@ -267,7 +280,6 @@ struct Binding {
|
|||||||
* This is an array of number_keycodes size. */
|
* This is an array of number_keycodes size. */
|
||||||
xcb_keycode_t *translated_to;
|
xcb_keycode_t *translated_to;
|
||||||
|
|
||||||
|
|
||||||
/** Command, like in command mode */
|
/** Command, like in command mode */
|
||||||
char *command;
|
char *command;
|
||||||
|
|
||||||
@ -367,7 +379,9 @@ struct Window {
|
|||||||
bool doesnt_accept_focus;
|
bool doesnt_accept_focus;
|
||||||
|
|
||||||
/** Whether the window says it is a dock window */
|
/** 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 */
|
/** When this window was marked urgent. 0 means not urgent */
|
||||||
struct timeval urgent;
|
struct timeval urgent;
|
||||||
@ -407,7 +421,9 @@ struct Match {
|
|||||||
M_DOCK_BOTTOM = 3
|
M_DOCK_BOTTOM = 3
|
||||||
} dock;
|
} dock;
|
||||||
xcb_window_t id;
|
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;
|
Con *con_id;
|
||||||
|
|
||||||
/* Where the window looking for a match should be inserted:
|
/* Where the window looking for a match should be inserted:
|
||||||
@ -419,7 +435,9 @@ struct Match {
|
|||||||
* (dockareas)
|
* (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;
|
TAILQ_ENTRY(Match) matches;
|
||||||
|
|
||||||
@ -470,7 +488,9 @@ struct Assignment {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/** Fullscreen modes. Used by Con.fullscreen_mode. */
|
/** 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.
|
* 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;
|
TAILQ_ENTRY(Con) floating_windows;
|
||||||
|
|
||||||
/** callbacks */
|
/** callbacks */
|
||||||
void(*on_remove_child)(Con *);
|
void (*on_remove_child)(Con *);
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
/* Not a scratchpad window. */
|
/* Not a scratchpad window. */
|
||||||
|
@ -18,6 +18,24 @@
|
|||||||
*/
|
*/
|
||||||
void ewmh_update_current_desktop(void);
|
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.
|
* Updates _NET_ACTIVE_WINDOW with the currently focused window.
|
||||||
*
|
*
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
|
|
||||||
/** Callback for dragging */
|
/** 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 */
|
/** Macro to create a callback function for dragging */
|
||||||
#define DRAGGING_CB(name) \
|
#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),
|
typedef enum { BORDER_LEFT = (1 << 0),
|
||||||
BORDER_RIGHT = (1 << 1),
|
BORDER_RIGHT = (1 << 1),
|
||||||
BORDER_TOP = (1 << 2),
|
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
|
* Enables floating mode for the given container by detaching it from its
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include <xcb/randr.h>
|
#include <xcb/randr.h>
|
||||||
|
|
||||||
extern int randr_base;
|
extern int randr_base;
|
||||||
|
extern int xkb_base;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the given sequence to the list of events which are ignored.
|
* Adds the given sequence to the list of events which are ignored.
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
|
|
||||||
#include <xcb/xcb_keysyms.h>
|
#include <xcb/xcb_keysyms.h>
|
||||||
|
#include <xcb/xkb.h>
|
||||||
|
|
||||||
#include <X11/XKBlib.h>
|
#include <X11/XKBlib.h>
|
||||||
|
|
||||||
@ -42,7 +43,7 @@ extern xcb_key_symbols_t *keysyms;
|
|||||||
extern char **start_argv;
|
extern char **start_argv;
|
||||||
extern Display *xlibdpy, *xkbdpy;
|
extern Display *xlibdpy, *xkbdpy;
|
||||||
extern int xkb_current_group;
|
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_head, Autostart) autostarts;
|
||||||
extern TAILQ_HEAD(autostarts_always_head, Autostart) autostarts_always;
|
extern TAILQ_HEAD(autostarts_always_head, Autostart) autostarts_always;
|
||||||
extern TAILQ_HEAD(ws_assignments_head, Workspace_Assignment) ws_assignments;
|
extern TAILQ_HEAD(ws_assignments_head, Workspace_Assignment) ws_assignments;
|
||||||
|
@ -17,7 +17,7 @@ typedef struct i3_ipc_header {
|
|||||||
char magic[6];
|
char magic[6];
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
} __attribute__ ((packed)) i3_ipc_header_t;
|
} __attribute__((packed)) i3_ipc_header_t;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Messages from clients to i3
|
* 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 */
|
/** Bar config update will be triggered to update the bar config */
|
||||||
#define I3_IPC_EVENT_BARCONFIG_UPDATE (I3_IPC_EVENT_MASK | 4)
|
#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.
|
* 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 */
|
/* Macro to declare a callback */
|
||||||
#define IPC_HANDLER(name) \
|
#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, \
|
int size, uint32_t message_size, \
|
||||||
uint32_t message_type)
|
uint32_t message_type)
|
||||||
|
|
||||||
@ -89,11 +89,17 @@ void ipc_shutdown(void);
|
|||||||
void dump_node(yajl_gen gen, Con *con, bool inplace_restart);
|
void dump_node(yajl_gen gen, Con *con, bool inplace_restart);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For the workspace "focus" event we send, along the usual "change" field,
|
* Generates a json workspace event. Returns a dynamically allocated yajl
|
||||||
* also the current and previous workspace, in "current" and "old"
|
* generator. Free with yajl_gen_free().
|
||||||
* respectively.
|
|
||||||
*/
|
*/
|
||||||
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,
|
* 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.
|
* For the barconfig update events, we send the serialized barconfig.
|
||||||
*/
|
*/
|
||||||
void ipc_send_barconfig_update_event(Barconfig *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. */
|
* infrastructure, we define a fallback. */
|
||||||
#if !defined(LOG)
|
#if !defined(LOG)
|
||||||
void verboselog(char *fmt, ...)
|
void verboselog(char *fmt, ...)
|
||||||
__attribute__ ((format (printf, 1, 2)));
|
__attribute__((format(printf, 1, 2)));
|
||||||
#define LOG(fmt, ...) verboselog("[libi3] " __FILE__ " " fmt, ##__VA_ARGS__)
|
#define LOG(fmt, ...) verboselog("[libi3] " __FILE__ " " fmt, ##__VA_ARGS__)
|
||||||
#endif
|
#endif
|
||||||
#if !defined(ELOG)
|
#if !defined(ELOG)
|
||||||
void errorlog(char *fmt, ...)
|
void errorlog(char *fmt, ...)
|
||||||
__attribute__ ((format (printf, 1, 2)));
|
__attribute__((format(printf, 1, 2)));
|
||||||
#define ELOG(fmt, ...) errorlog("[libi3] ERROR: " fmt, ##__VA_ARGS__)
|
#define ELOG(fmt, ...) errorlog("[libi3] ERROR: " fmt, ##__VA_ARGS__)
|
||||||
#endif
|
#endif
|
||||||
#if !defined(DLOG)
|
#if !defined(DLOG)
|
||||||
void debuglog(char *fmt, ...)
|
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__)
|
#define DLOG(fmt, ...) debuglog("%s:%s:%d - " fmt, I3__FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -141,6 +141,12 @@ int sasprintf(char **strp, const char *fmt, ...);
|
|||||||
*/
|
*/
|
||||||
i3String *i3string_from_utf8(const char *from_utf8);
|
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.
|
* Build an i3String from an UTF-8 encoded string with fixed length.
|
||||||
* To be used when no proper NUL-terminaison is available.
|
* 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);
|
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.
|
* Build an i3String from an UCS-2 encoded string.
|
||||||
* Returns the newly-allocated i3String.
|
* Returns the newly-allocated i3String.
|
||||||
@ -168,12 +181,12 @@ void i3string_free(i3String *str);
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#define I3STRING_FREE(str) \
|
#define I3STRING_FREE(str) \
|
||||||
do { \
|
do { \
|
||||||
if (str != NULL) { \
|
if (str != NULL) { \
|
||||||
i3string_free(str); \
|
i3string_free(str); \
|
||||||
str = NULL; \
|
str = NULL; \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the UTF-8 encoded version of the i3String.
|
* 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);
|
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.
|
* 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,
|
* 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);
|
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);
|
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);
|
void free_font(void);
|
||||||
|
@ -78,14 +78,14 @@ void set_verbosity(bool _verbose);
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void debuglog(char *fmt, ...)
|
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.
|
* Logs the given message to stdout while prefixing the current time to it.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void errorlog(char *fmt, ...)
|
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,
|
* 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, ...)
|
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.
|
* Deletes the unused log files. Useful if i3 exits immediately, eg.
|
||||||
|
@ -10,8 +10,8 @@
|
|||||||
#pragma once
|
#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)
|
* 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.
|
* Singly-linked List definitions.
|
||||||
*/
|
*/
|
||||||
#define SLIST_HEAD(name, type) \
|
#define SLIST_HEAD(name, type) \
|
||||||
struct name { \
|
struct name { \
|
||||||
struct type *slh_first; /* first element */ \
|
struct type *slh_first; /* first element */ \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SLIST_HEAD_INITIALIZER(head) \
|
#define SLIST_HEAD_INITIALIZER(head) \
|
||||||
{ NULL }
|
{ NULL }
|
||||||
|
|
||||||
#define SLIST_ENTRY(type) \
|
#define SLIST_ENTRY(type) \
|
||||||
struct { \
|
struct { \
|
||||||
struct type *sle_next; /* next element */ \
|
struct type *sle_next; /* next element */ \
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Singly-linked List access methods.
|
* Singly-linked List access methods.
|
||||||
@ -112,7 +112,7 @@ struct { \
|
|||||||
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
||||||
|
|
||||||
#define SLIST_FOREACH(var, head, field) \
|
#define SLIST_FOREACH(var, head, field) \
|
||||||
for((var) = SLIST_FIRST(head); \
|
for ((var) = SLIST_FIRST(head); \
|
||||||
(var) != SLIST_END(head); \
|
(var) != SLIST_END(head); \
|
||||||
(var) = SLIST_NEXT(var, field))
|
(var) = SLIST_NEXT(var, field))
|
||||||
|
|
||||||
@ -124,29 +124,35 @@ struct { \
|
|||||||
/*
|
/*
|
||||||
* Singly-linked List functions.
|
* Singly-linked List functions.
|
||||||
*/
|
*/
|
||||||
#define SLIST_INIT(head) { \
|
#define SLIST_INIT(head) \
|
||||||
|
{ \
|
||||||
SLIST_FIRST(head) = SLIST_END(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; \
|
(elm)->field.sle_next = (slistelm)->field.sle_next; \
|
||||||
(slistelm)->field.sle_next = (elm); \
|
(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; \
|
(elm)->field.sle_next = (head)->slh_first; \
|
||||||
(head)->slh_first = (elm); \
|
(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; \
|
(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; \
|
(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)) { \
|
if ((head)->slh_first == (elm)) { \
|
||||||
SLIST_REMOVE_HEAD((head), field); \
|
SLIST_REMOVE_HEAD((head), field); \
|
||||||
} else { \
|
} else { \
|
||||||
@ -154,28 +160,27 @@ struct { \
|
|||||||
\
|
\
|
||||||
while (curelm->field.sle_next != (elm)) \
|
while (curelm->field.sle_next != (elm)) \
|
||||||
curelm = curelm->field.sle_next; \
|
curelm = curelm->field.sle_next; \
|
||||||
curelm->field.sle_next = \
|
curelm->field.sle_next = curelm->field.sle_next->field.sle_next; \
|
||||||
curelm->field.sle_next->field.sle_next; \
|
|
||||||
_Q_INVALIDATE((elm)->field.sle_next); \
|
_Q_INVALIDATE((elm)->field.sle_next); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* List definitions.
|
* List definitions.
|
||||||
*/
|
*/
|
||||||
#define LIST_HEAD(name, type) \
|
#define LIST_HEAD(name, type) \
|
||||||
struct name { \
|
struct name { \
|
||||||
struct type *lh_first; /* first element */ \
|
struct type *lh_first; /* first element */ \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define LIST_HEAD_INITIALIZER(head) \
|
#define LIST_HEAD_INITIALIZER(head) \
|
||||||
{ NULL }
|
{ NULL }
|
||||||
|
|
||||||
#define LIST_ENTRY(type) \
|
#define LIST_ENTRY(type) \
|
||||||
struct { \
|
struct { \
|
||||||
struct type *le_next; /* next element */ \
|
struct type *le_next; /* next element */ \
|
||||||
struct type **le_prev; /* address of previous next element */ \
|
struct type **le_prev; /* address of previous next element */ \
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* List access methods
|
* List access methods
|
||||||
@ -186,74 +191,77 @@ struct { \
|
|||||||
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
|
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
|
||||||
|
|
||||||
#define LIST_FOREACH(var, head, field) \
|
#define LIST_FOREACH(var, head, field) \
|
||||||
for((var) = LIST_FIRST(head); \
|
for ((var) = LIST_FIRST(head); \
|
||||||
(var)!= LIST_END(head); \
|
(var) != LIST_END(head); \
|
||||||
(var) = LIST_NEXT(var, field))
|
(var) = LIST_NEXT(var, field))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* List functions.
|
* List functions.
|
||||||
*/
|
*/
|
||||||
#define LIST_INIT(head) do { \
|
#define LIST_INIT(head) \
|
||||||
|
do { \
|
||||||
LIST_FIRST(head) = LIST_END(head); \
|
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) \
|
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
|
||||||
(listelm)->field.le_next->field.le_prev = \
|
(listelm)->field.le_next->field.le_prev = &(elm)->field.le_next; \
|
||||||
&(elm)->field.le_next; \
|
|
||||||
(listelm)->field.le_next = (elm); \
|
(listelm)->field.le_next = (elm); \
|
||||||
(elm)->field.le_prev = &(listelm)->field.le_next; \
|
(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_prev = (listelm)->field.le_prev; \
|
||||||
(elm)->field.le_next = (listelm); \
|
(elm)->field.le_next = (listelm); \
|
||||||
*(listelm)->field.le_prev = (elm); \
|
*(listelm)->field.le_prev = (elm); \
|
||||||
(listelm)->field.le_prev = &(elm)->field.le_next; \
|
(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) \
|
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); \
|
(head)->lh_first = (elm); \
|
||||||
(elm)->field.le_prev = &(head)->lh_first; \
|
(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) \
|
if ((elm)->field.le_next != NULL) \
|
||||||
(elm)->field.le_next->field.le_prev = \
|
(elm)->field.le_next->field.le_prev = (elm)->field.le_prev; \
|
||||||
(elm)->field.le_prev; \
|
|
||||||
*(elm)->field.le_prev = (elm)->field.le_next; \
|
*(elm)->field.le_prev = (elm)->field.le_next; \
|
||||||
_Q_INVALIDATE((elm)->field.le_prev); \
|
_Q_INVALIDATE((elm)->field.le_prev); \
|
||||||
_Q_INVALIDATE((elm)->field.le_next); \
|
_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) \
|
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
|
||||||
(elm2)->field.le_next->field.le_prev = \
|
(elm2)->field.le_next->field.le_prev = &(elm2)->field.le_next; \
|
||||||
&(elm2)->field.le_next; \
|
|
||||||
(elm2)->field.le_prev = (elm)->field.le_prev; \
|
(elm2)->field.le_prev = (elm)->field.le_prev; \
|
||||||
*(elm2)->field.le_prev = (elm2); \
|
*(elm2)->field.le_prev = (elm2); \
|
||||||
_Q_INVALIDATE((elm)->field.le_prev); \
|
_Q_INVALIDATE((elm)->field.le_prev); \
|
||||||
_Q_INVALIDATE((elm)->field.le_next); \
|
_Q_INVALIDATE((elm)->field.le_next); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Simple queue definitions.
|
* Simple queue definitions.
|
||||||
*/
|
*/
|
||||||
#define SIMPLEQ_HEAD(name, type) \
|
#define SIMPLEQ_HEAD(name, type) \
|
||||||
struct name { \
|
struct name { \
|
||||||
struct type *sqh_first; /* first element */ \
|
struct type *sqh_first; /* first element */ \
|
||||||
struct type **sqh_last; /* addr of last next element */ \
|
struct type **sqh_last; /* addr of last next element */ \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SIMPLEQ_HEAD_INITIALIZER(head) \
|
#define SIMPLEQ_HEAD_INITIALIZER(head) \
|
||||||
{ NULL, &(head).sqh_first }
|
{ NULL, &(head).sqh_first }
|
||||||
|
|
||||||
#define SIMPLEQ_ENTRY(type) \
|
#define SIMPLEQ_ENTRY(type) \
|
||||||
struct { \
|
struct { \
|
||||||
struct type *sqe_next; /* next element */ \
|
struct type *sqe_next; /* next element */ \
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Simple queue access methods.
|
* Simple queue access methods.
|
||||||
@ -264,58 +272,63 @@ struct { \
|
|||||||
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
|
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
|
||||||
|
|
||||||
#define SIMPLEQ_FOREACH(var, head, field) \
|
#define SIMPLEQ_FOREACH(var, head, field) \
|
||||||
for((var) = SIMPLEQ_FIRST(head); \
|
for ((var) = SIMPLEQ_FIRST(head); \
|
||||||
(var) != SIMPLEQ_END(head); \
|
(var) != SIMPLEQ_END(head); \
|
||||||
(var) = SIMPLEQ_NEXT(var, field))
|
(var) = SIMPLEQ_NEXT(var, field))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Simple queue functions.
|
* Simple queue functions.
|
||||||
*/
|
*/
|
||||||
#define SIMPLEQ_INIT(head) do { \
|
#define SIMPLEQ_INIT(head) \
|
||||||
|
do { \
|
||||||
(head)->sqh_first = NULL; \
|
(head)->sqh_first = NULL; \
|
||||||
(head)->sqh_last = &(head)->sqh_first; \
|
(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) \
|
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
|
||||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||||
(head)->sqh_first = (elm); \
|
(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; \
|
(elm)->field.sqe_next = NULL; \
|
||||||
*(head)->sqh_last = (elm); \
|
*(head)->sqh_last = (elm); \
|
||||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) \
|
||||||
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
|
do { \
|
||||||
|
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL) \
|
||||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||||
(listelm)->field.sqe_next = (elm); \
|
(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) \
|
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
|
||||||
(head)->sqh_last = &(head)->sqh_first; \
|
(head)->sqh_last = &(head)->sqh_first; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Tail queue definitions.
|
* Tail queue definitions.
|
||||||
*/
|
*/
|
||||||
#define TAILQ_HEAD(name, type) \
|
#define TAILQ_HEAD(name, type) \
|
||||||
struct name { \
|
struct name { \
|
||||||
struct type *tqh_first; /* first element */ \
|
struct type *tqh_first; /* first element */ \
|
||||||
struct type **tqh_last; /* addr of last next element */ \
|
struct type **tqh_last; /* addr of last next element */ \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TAILQ_HEAD_INITIALIZER(head) \
|
#define TAILQ_HEAD_INITIALIZER(head) \
|
||||||
{ NULL, &(head).tqh_first }
|
{ NULL, &(head).tqh_first }
|
||||||
|
|
||||||
#define TAILQ_ENTRY(type) \
|
#define TAILQ_ENTRY(type) \
|
||||||
struct { \
|
struct { \
|
||||||
struct type *tqe_next; /* next element */ \
|
struct type *tqe_next; /* next element */ \
|
||||||
struct type **tqe_prev; /* address of previous next element */ \
|
struct type **tqe_prev; /* address of previous next element */ \
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* tail queue access methods
|
* tail queue access methods
|
||||||
@ -332,82 +345,86 @@ struct { \
|
|||||||
(TAILQ_FIRST(head) == TAILQ_END(head))
|
(TAILQ_FIRST(head) == TAILQ_END(head))
|
||||||
|
|
||||||
#define TAILQ_FOREACH(var, head, field) \
|
#define TAILQ_FOREACH(var, head, field) \
|
||||||
for((var) = TAILQ_FIRST(head); \
|
for ((var) = TAILQ_FIRST(head); \
|
||||||
(var) != TAILQ_END(head); \
|
(var) != TAILQ_END(head); \
|
||||||
(var) = TAILQ_NEXT(var, field))
|
(var) = TAILQ_NEXT(var, field))
|
||||||
|
|
||||||
#define TAILQ_FOREACH_REVERSE(var, head, headname, 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_END(head); \
|
||||||
(var) = TAILQ_PREV(var, headname, field))
|
(var) = TAILQ_PREV(var, headname, field))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Tail queue functions.
|
* Tail queue functions.
|
||||||
*/
|
*/
|
||||||
#define TAILQ_INIT(head) do { \
|
#define TAILQ_INIT(head) \
|
||||||
|
do { \
|
||||||
(head)->tqh_first = NULL; \
|
(head)->tqh_first = NULL; \
|
||||||
(head)->tqh_last = &(head)->tqh_first; \
|
(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) \
|
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
|
||||||
(head)->tqh_first->field.tqe_prev = \
|
(head)->tqh_first->field.tqe_prev = &(elm)->field.tqe_next; \
|
||||||
&(elm)->field.tqe_next; \
|
|
||||||
else \
|
else \
|
||||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||||
(head)->tqh_first = (elm); \
|
(head)->tqh_first = (elm); \
|
||||||
(elm)->field.tqe_prev = &(head)->tqh_first; \
|
(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_next = NULL; \
|
||||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||||
*(head)->tqh_last = (elm); \
|
*(head)->tqh_last = (elm); \
|
||||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) \
|
||||||
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
|
do { \
|
||||||
(elm)->field.tqe_next->field.tqe_prev = \
|
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL) \
|
||||||
&(elm)->field.tqe_next; \
|
(elm)->field.tqe_next->field.tqe_prev = &(elm)->field.tqe_next; \
|
||||||
else \
|
else \
|
||||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||||
(listelm)->field.tqe_next = (elm); \
|
(listelm)->field.tqe_next = (elm); \
|
||||||
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
|
(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_prev = (listelm)->field.tqe_prev; \
|
||||||
(elm)->field.tqe_next = (listelm); \
|
(elm)->field.tqe_next = (listelm); \
|
||||||
*(listelm)->field.tqe_prev = (elm); \
|
*(listelm)->field.tqe_prev = (elm); \
|
||||||
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
|
(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) \
|
if (((elm)->field.tqe_next) != NULL) \
|
||||||
(elm)->field.tqe_next->field.tqe_prev = \
|
(elm)->field.tqe_next->field.tqe_prev = (elm)->field.tqe_prev; \
|
||||||
(elm)->field.tqe_prev; \
|
|
||||||
else \
|
else \
|
||||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||||
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
|
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
|
||||||
_Q_INVALIDATE((elm)->field.tqe_prev); \
|
_Q_INVALIDATE((elm)->field.tqe_prev); \
|
||||||
_Q_INVALIDATE((elm)->field.tqe_next); \
|
_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) \
|
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
|
||||||
(elm2)->field.tqe_next->field.tqe_prev = \
|
(elm2)->field.tqe_next->field.tqe_prev = &(elm2)->field.tqe_next; \
|
||||||
&(elm2)->field.tqe_next; \
|
|
||||||
else \
|
else \
|
||||||
(head)->tqh_last = &(elm2)->field.tqe_next; \
|
(head)->tqh_last = &(elm2)->field.tqe_next; \
|
||||||
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
|
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
|
||||||
*(elm2)->field.tqe_prev = (elm2); \
|
*(elm2)->field.tqe_prev = (elm2); \
|
||||||
_Q_INVALIDATE((elm)->field.tqe_prev); \
|
_Q_INVALIDATE((elm)->field.tqe_prev); \
|
||||||
_Q_INVALIDATE((elm)->field.tqe_next); \
|
_Q_INVALIDATE((elm)->field.tqe_next); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/* Swaps two consecutive elements. 'second' *MUST* follow 'first' */
|
/* 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); \
|
*((first)->field.tqe_prev) = (second); \
|
||||||
(second)->field.tqe_prev = (first)->field.tqe_prev; \
|
(second)->field.tqe_prev = (first)->field.tqe_prev; \
|
||||||
(first)->field.tqe_prev = &((second)->field.tqe_next); \
|
(first)->field.tqe_prev = &((second)->field.tqe_next); \
|
||||||
@ -417,25 +434,25 @@ struct { \
|
|||||||
(second)->field.tqe_next = first; \
|
(second)->field.tqe_next = first; \
|
||||||
if ((head)->tqh_last == &((second)->field.tqe_next)) \
|
if ((head)->tqh_last == &((second)->field.tqe_next)) \
|
||||||
(head)->tqh_last = &((first)->field.tqe_next); \
|
(head)->tqh_last = &((first)->field.tqe_next); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Circular queue definitions.
|
* Circular queue definitions.
|
||||||
*/
|
*/
|
||||||
#define CIRCLEQ_HEAD(name, type) \
|
#define CIRCLEQ_HEAD(name, type) \
|
||||||
struct name { \
|
struct name { \
|
||||||
struct type *cqh_first; /* first element */ \
|
struct type *cqh_first; /* first element */ \
|
||||||
struct type *cqh_last; /* last element */ \
|
struct type *cqh_last; /* last element */ \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CIRCLEQ_HEAD_INITIALIZER(head) \
|
#define CIRCLEQ_HEAD_INITIALIZER(head) \
|
||||||
{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
|
{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
|
||||||
|
|
||||||
#define CIRCLEQ_ENTRY(type) \
|
#define CIRCLEQ_ENTRY(type) \
|
||||||
struct { \
|
struct { \
|
||||||
struct type *cqe_next; /* next element */ \
|
struct type *cqe_next; /* next element */ \
|
||||||
struct type *cqe_prev; /* previous element */ \
|
struct type *cqe_prev; /* previous element */ \
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Circular queue access methods
|
* Circular queue access methods
|
||||||
@ -449,24 +466,26 @@ struct { \
|
|||||||
(CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
|
(CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
|
||||||
|
|
||||||
#define CIRCLEQ_FOREACH(var, head, field) \
|
#define CIRCLEQ_FOREACH(var, head, field) \
|
||||||
for((var) = CIRCLEQ_FIRST(head); \
|
for ((var) = CIRCLEQ_FIRST(head); \
|
||||||
(var) != CIRCLEQ_END(head); \
|
(var) != CIRCLEQ_END(head); \
|
||||||
(var) = CIRCLEQ_NEXT(var, field))
|
(var) = CIRCLEQ_NEXT(var, field))
|
||||||
|
|
||||||
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
|
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
|
||||||
for((var) = CIRCLEQ_LAST(head); \
|
for ((var) = CIRCLEQ_LAST(head); \
|
||||||
(var) != CIRCLEQ_END(head); \
|
(var) != CIRCLEQ_END(head); \
|
||||||
(var) = CIRCLEQ_PREV(var, field))
|
(var) = CIRCLEQ_PREV(var, field))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Circular queue functions.
|
* Circular queue functions.
|
||||||
*/
|
*/
|
||||||
#define CIRCLEQ_INIT(head) do { \
|
#define CIRCLEQ_INIT(head) \
|
||||||
|
do { \
|
||||||
(head)->cqh_first = CIRCLEQ_END(head); \
|
(head)->cqh_first = CIRCLEQ_END(head); \
|
||||||
(head)->cqh_last = 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_next = (listelm)->field.cqe_next; \
|
||||||
(elm)->field.cqe_prev = (listelm); \
|
(elm)->field.cqe_prev = (listelm); \
|
||||||
if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
|
if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
|
||||||
@ -474,9 +493,10 @@ struct { \
|
|||||||
else \
|
else \
|
||||||
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
|
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
|
||||||
(listelm)->field.cqe_next = (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_next = (listelm); \
|
||||||
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
|
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
|
||||||
if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
|
if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
|
||||||
@ -484,9 +504,10 @@ struct { \
|
|||||||
else \
|
else \
|
||||||
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
|
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
|
||||||
(listelm)->field.cqe_prev = (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_next = (head)->cqh_first; \
|
||||||
(elm)->field.cqe_prev = CIRCLEQ_END(head); \
|
(elm)->field.cqe_prev = CIRCLEQ_END(head); \
|
||||||
if ((head)->cqh_last == CIRCLEQ_END(head)) \
|
if ((head)->cqh_last == CIRCLEQ_END(head)) \
|
||||||
@ -494,9 +515,10 @@ struct { \
|
|||||||
else \
|
else \
|
||||||
(head)->cqh_first->field.cqe_prev = (elm); \
|
(head)->cqh_first->field.cqe_prev = (elm); \
|
||||||
(head)->cqh_first = (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_next = CIRCLEQ_END(head); \
|
||||||
(elm)->field.cqe_prev = (head)->cqh_last; \
|
(elm)->field.cqe_prev = (head)->cqh_last; \
|
||||||
if ((head)->cqh_first == CIRCLEQ_END(head)) \
|
if ((head)->cqh_first == CIRCLEQ_END(head)) \
|
||||||
@ -504,34 +526,32 @@ struct { \
|
|||||||
else \
|
else \
|
||||||
(head)->cqh_last->field.cqe_next = (elm); \
|
(head)->cqh_last->field.cqe_next = (elm); \
|
||||||
(head)->cqh_last = (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)) \
|
if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
|
||||||
(head)->cqh_last = (elm)->field.cqe_prev; \
|
(head)->cqh_last = (elm)->field.cqe_prev; \
|
||||||
else \
|
else \
|
||||||
(elm)->field.cqe_next->field.cqe_prev = \
|
(elm)->field.cqe_next->field.cqe_prev = (elm)->field.cqe_prev; \
|
||||||
(elm)->field.cqe_prev; \
|
|
||||||
if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
|
if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
|
||||||
(head)->cqh_first = (elm)->field.cqe_next; \
|
(head)->cqh_first = (elm)->field.cqe_next; \
|
||||||
else \
|
else \
|
||||||
(elm)->field.cqe_prev->field.cqe_next = \
|
(elm)->field.cqe_prev->field.cqe_next = (elm)->field.cqe_next; \
|
||||||
(elm)->field.cqe_next; \
|
|
||||||
_Q_INVALIDATE((elm)->field.cqe_prev); \
|
_Q_INVALIDATE((elm)->field.cqe_prev); \
|
||||||
_Q_INVALIDATE((elm)->field.cqe_next); \
|
_Q_INVALIDATE((elm)->field.cqe_next); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
|
#define CIRCLEQ_REPLACE(head, elm, elm2, field) \
|
||||||
if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
|
do { \
|
||||||
CIRCLEQ_END(head)) \
|
if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == CIRCLEQ_END(head)) \
|
||||||
(head)->cqh_last = (elm2); \
|
(head)->cqh_last = (elm2); \
|
||||||
else \
|
else \
|
||||||
(elm2)->field.cqe_next->field.cqe_prev = (elm2); \
|
(elm2)->field.cqe_next->field.cqe_prev = (elm2); \
|
||||||
if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
|
if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == CIRCLEQ_END(head)) \
|
||||||
CIRCLEQ_END(head)) \
|
|
||||||
(head)->cqh_first = (elm2); \
|
(head)->cqh_first = (elm2); \
|
||||||
else \
|
else \
|
||||||
(elm2)->field.cqe_prev->field.cqe_next = (elm2); \
|
(elm2)->field.cqe_prev->field.cqe_next = (elm2); \
|
||||||
_Q_INVALIDATE((elm)->field.cqe_prev); \
|
_Q_INVALIDATE((elm)->field.cqe_prev); \
|
||||||
_Q_INVALIDATE((elm)->field.cqe_next); \
|
_Q_INVALIDATE((elm)->field.cqe_next); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
@ -68,15 +68,15 @@ extern "C" {
|
|||||||
|
|
||||||
#ifndef _sd_printf_attr_
|
#ifndef _sd_printf_attr_
|
||||||
#if __GNUC__ >= 4
|
#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
|
#else
|
||||||
#define _sd_printf_attr_(a,b)
|
#define _sd_printf_attr_(a, b)
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef _sd_hidden_
|
#ifndef _sd_hidden_
|
||||||
#if (__GNUC__ >= 4) && !defined(SD_EXPORT_SYMBOLS)
|
#if (__GNUC__ >= 4) && !defined(SD_EXPORT_SYMBOLS)
|
||||||
#define _sd_hidden_ __attribute__ ((visibility("hidden")))
|
#define _sd_hidden_ __attribute__((visibility("hidden")))
|
||||||
#else
|
#else
|
||||||
#define _sd_hidden_
|
#define _sd_hidden_
|
||||||
#endif
|
#endif
|
||||||
@ -242,7 +242,7 @@ int sd_notify(int unset_environment, const char *state) _sd_hidden_;
|
|||||||
|
|
||||||
See sd_notifyf(3) for more information.
|
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
|
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
|
* (immediately), the application is reparented to init (process-id 1), which
|
||||||
* correctly handles childs, so we don’t have to do it :-).
|
* correctly handles childs, so we don’t have to do it :-).
|
||||||
*
|
*
|
||||||
* The shell is determined by looking for the SHELL environment variable. If
|
* The shell used to start applications is the system's bourne shell (i.e.,
|
||||||
* it does not exist, /bin/sh is used.
|
* /bin/sh).
|
||||||
*
|
*
|
||||||
* The no_startup_id flag determines whether a startup notification context
|
* The no_startup_id flag determines whether a startup notification context
|
||||||
* (and ID) should be created, which is the default and encouraged behavior.
|
* (and ID) should be created, which is the default and encouraged behavior.
|
||||||
|
@ -15,22 +15,24 @@
|
|||||||
#include "data.h"
|
#include "data.h"
|
||||||
|
|
||||||
#define die(...) errx(EXIT_FAILURE, __VA_ARGS__);
|
#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 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) ? \
|
#define CIRCLEQ_NEXT_OR_NULL(head, elm, field) (CIRCLEQ_NEXT(elm, field) != CIRCLEQ_END(head) ? CIRCLEQ_NEXT(elm, field) : NULL)
|
||||||
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_PREV_OR_NULL(head, elm, field) (CIRCLEQ_PREV(elm, field) != CIRCLEQ_END(head) ? \
|
|
||||||
CIRCLEQ_PREV(elm, field) : NULL)
|
|
||||||
#define FOR_TABLE(workspace) \
|
#define FOR_TABLE(workspace) \
|
||||||
for (int cols = 0; cols < (workspace)->cols; cols++) \
|
for (int cols = 0; cols < (workspace)->cols; cols++) \
|
||||||
for (int rows = 0; rows < (workspace)->rows; rows++)
|
for (int rows = 0; rows < (workspace)->rows; rows++)
|
||||||
|
|
||||||
#define NODES_FOREACH(head) \
|
#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)
|
TAILQ_FOREACH(child, &((head)->nodes_head), nodes)
|
||||||
|
|
||||||
#define NODES_FOREACH_REVERSE(head) \
|
#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)
|
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 */
|
/* greps the ->nodes of the given head and returns the first node that matches the given condition */
|
||||||
@ -43,15 +45,15 @@
|
|||||||
break; \
|
break; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define FREE(pointer) do { \
|
#define FREE(pointer) \
|
||||||
|
do { \
|
||||||
if (pointer != NULL) { \
|
if (pointer != NULL) { \
|
||||||
free(pointer); \
|
free(pointer); \
|
||||||
pointer = NULL; \
|
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 min(int a, int b);
|
||||||
int max(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.
|
* Returns the next workspace.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
Con* workspace_next(void);
|
Con *workspace_next(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the previous workspace.
|
* Returns the previous workspace.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
Con* workspace_prev(void);
|
Con *workspace_prev(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the next workspace on the same output
|
* 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
|
* 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.
|
* Focuses the previously focused workspace.
|
||||||
@ -100,7 +100,6 @@ void workspace_back_and_forth(void);
|
|||||||
*/
|
*/
|
||||||
Con *workspace_back_and_forth_get(void);
|
Con *workspace_back_and_forth_get(void);
|
||||||
|
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
/**
|
/**
|
||||||
* Assigns the given workspace to the given screen by correctly updating its
|
* Assigns the given workspace to the given screen by correctly updating its
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
XCB_EVENT_MASK_PROPERTY_CHANGE | \
|
XCB_EVENT_MASK_PROPERTY_CHANGE | \
|
||||||
XCB_EVENT_MASK_ENTER_WINDOW)
|
XCB_EVENT_MASK_ENTER_WINDOW)
|
||||||
|
|
||||||
#define xmacro(atom) xcb_atom_t A_ ## atom;
|
#define xmacro(atom) xcb_atom_t A_##atom;
|
||||||
#include "atoms.xmacro"
|
#include "atoms.xmacro"
|
||||||
#undef 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);
|
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);
|
bool xcb_reply_contains_atom(xcb_get_property_reply_t *prop, xcb_atom_t atom);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,8 +14,8 @@
|
|||||||
#include <yajl/yajl_version.h>
|
#include <yajl/yajl_version.h>
|
||||||
|
|
||||||
/* Shorter names for all those yajl_gen_* functions */
|
/* Shorter names for all those yajl_gen_* functions */
|
||||||
#define y(x, ...) yajl_gen_ ## x (gen, ##__VA_ARGS__)
|
#define y(x, ...) yajl_gen_##x(gen, ##__VA_ARGS__)
|
||||||
#define ystr(str) yajl_gen_string(gen, (unsigned char*)str, strlen(str))
|
#define ystr(str) yajl_gen_string(gen, (unsigned char *)str, strlen(str))
|
||||||
|
|
||||||
#define ygenalloc() yajl_gen_alloc(NULL)
|
#define ygenalloc() yajl_gen_alloc(NULL)
|
||||||
#define yalloc(callbacks, client) yajl_alloc(callbacks, NULL, client)
|
#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 "libi3.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
@ -12,5 +19,13 @@ extern xcb_screen_t *root_screen;
|
|||||||
int logical_px(const int logical) {
|
int logical_px(const int logical) {
|
||||||
const int dpi = (double)root_screen->height_in_pixels * 25.4 /
|
const int dpi = (double)root_screen->height_in_pixels * 25.4 /
|
||||||
(double)root_screen->height_in_millimeters;
|
(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);
|
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,
|
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 */
|
/* Create the Pango layout */
|
||||||
/* root_visual_type is cached in load_pango_font */
|
/* root_visual_type is cached in load_pango_font */
|
||||||
cairo_surface_t *surface = cairo_xcb_surface_create(conn, drawable,
|
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_wrap(layout, PANGO_WRAP_CHAR);
|
||||||
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
|
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);
|
pango_layout_set_text(layout, text, text_len);
|
||||||
|
|
||||||
/* Do the drawing */
|
/* 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.
|
* 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 */
|
/* Create a dummy Pango layout */
|
||||||
/* root_visual_type is cached in load_pango_font */
|
/* 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);
|
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 */
|
/* Get the font width */
|
||||||
gint width;
|
gint width;
|
||||||
pango_layout_set_font_description(layout, savedFont->specific.pango_desc);
|
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_layout_set_text(layout, text, text_len);
|
||||||
|
|
||||||
pango_cairo_update_layout(cr, layout);
|
pango_cairo_update_layout(cr, layout);
|
||||||
pango_layout_get_pixel_size(layout, &width, NULL);
|
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,
|
* 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) {
|
i3Font load_font(const char *pattern, const bool fallback) {
|
||||||
|
/* if any font was previously loaded, free it now */
|
||||||
|
free_font();
|
||||||
|
|
||||||
i3Font font;
|
i3Font font;
|
||||||
font.type = FONT_TYPE_NONE;
|
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
|
#if PANGO_SUPPORT
|
||||||
/* Try to load a pango font if specified */
|
/* Try to load a pango font if specified */
|
||||||
if (strlen(pattern) > strlen("pango:") && !strncmp(pattern, "pango:", strlen("pango:"))) {
|
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) {
|
void free_font(void) {
|
||||||
|
/* if there is no saved font, simply return */
|
||||||
|
if (savedFont == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
free(savedFont->pattern);
|
free(savedFont->pattern);
|
||||||
switch (savedFont->type) {
|
switch (savedFont->type) {
|
||||||
case FONT_TYPE_NONE:
|
case FONT_TYPE_NONE:
|
||||||
@ -277,6 +301,8 @@ void free_font(void) {
|
|||||||
assert(false);
|
assert(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
savedFont = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -366,7 +392,7 @@ void draw_text(i3String *text, xcb_drawable_t drawable,
|
|||||||
case FONT_TYPE_PANGO:
|
case FONT_TYPE_PANGO:
|
||||||
/* Render the text using Pango */
|
/* Render the text using Pango */
|
||||||
draw_text_pango(i3string_as_utf8(text), i3string_get_num_bytes(text),
|
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;
|
return;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
@ -405,7 +431,7 @@ void draw_text_ascii(const char *text, xcb_drawable_t drawable,
|
|||||||
case FONT_TYPE_PANGO:
|
case FONT_TYPE_PANGO:
|
||||||
/* Render the text using Pango */
|
/* Render the text using Pango */
|
||||||
draw_text_pango(text, strlen(text),
|
draw_text_pango(text, strlen(text),
|
||||||
drawable, x, y, max_width);
|
drawable, x, y, max_width, false);
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
@ -501,7 +527,8 @@ int predict_text_width(i3String *text) {
|
|||||||
#if PANGO_SUPPORT
|
#if PANGO_SUPPORT
|
||||||
case FONT_TYPE_PANGO:
|
case FONT_TYPE_PANGO:
|
||||||
/* Calculate extents using 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
|
#endif
|
||||||
default:
|
default:
|
||||||
assert(false);
|
assert(false);
|
||||||
|
@ -20,6 +20,7 @@ struct _i3String {
|
|||||||
xcb_char2b_t *ucs2;
|
xcb_char2b_t *ucs2;
|
||||||
size_t num_glyphs;
|
size_t num_glyphs;
|
||||||
size_t num_bytes;
|
size_t num_bytes;
|
||||||
|
bool is_markup;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -39,6 +40,19 @@ i3String *i3string_from_utf8(const char *from_utf8) {
|
|||||||
return str;
|
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.
|
* Build an i3String from an UTF-8 encoded string with fixed length.
|
||||||
* To be used when no proper NUL-terminaison is available.
|
* 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;
|
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.
|
* Build an i3String from an UCS-2 encoded string.
|
||||||
* Returns the newly-allocated i3String.
|
* Returns the newly-allocated i3String.
|
||||||
@ -133,6 +161,13 @@ size_t i3string_get_num_bytes(i3String *str) {
|
|||||||
return str->num_bytes;
|
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.
|
* Returns the number of glyphs in an i3String.
|
||||||
*
|
*
|
||||||
|
@ -7,7 +7,7 @@ template::[header-declarations]
|
|||||||
<refentrytitle>{mantitle}</refentrytitle>
|
<refentrytitle>{mantitle}</refentrytitle>
|
||||||
<manvolnum>{manvolnum}</manvolnum>
|
<manvolnum>{manvolnum}</manvolnum>
|
||||||
<refmiscinfo class="source">i3</refmiscinfo>
|
<refmiscinfo class="source">i3</refmiscinfo>
|
||||||
<refmiscinfo class="version">4.8</refmiscinfo>
|
<refmiscinfo class="version">4.9</refmiscinfo>
|
||||||
<refmiscinfo class="manual">i3 Manual</refmiscinfo>
|
<refmiscinfo class="manual">i3 Manual</refmiscinfo>
|
||||||
</refmeta>
|
</refmeta>
|
||||||
<refnamediv>
|
<refnamediv>
|
||||||
|
@ -22,6 +22,7 @@ is appropriate for the distribution.
|
|||||||
It tries to start one of the following (in that order):
|
It tries to start one of the following (in that order):
|
||||||
|
|
||||||
* $TERMINAL (this is a non-standard variable)
|
* $TERMINAL (this is a non-standard variable)
|
||||||
|
* x-terminal-emulator (only present on Debian and derivatives)
|
||||||
* urxvt
|
* urxvt
|
||||||
* rxvt
|
* rxvt
|
||||||
* terminator
|
* terminator
|
||||||
|
@ -230,7 +230,7 @@ bindsym Mod1+h split h
|
|||||||
bindsym Mod1+v split v
|
bindsym Mod1+v split v
|
||||||
|
|
||||||
# enter fullscreen mode for the focused container
|
# enter fullscreen mode for the focused container
|
||||||
bindsym Mod1+f fullscreen
|
bindsym Mod1+f fullscreen toggle
|
||||||
|
|
||||||
# change container layout (stacked, tabbed, default)
|
# change container layout (stacked, tabbed, default)
|
||||||
bindsym Mod1+s layout stacking
|
bindsym Mod1+s layout stacking
|
||||||
|
@ -156,12 +156,28 @@ state KILL:
|
|||||||
end
|
end
|
||||||
-> call cmd_kill($kill_mode)
|
-> call cmd_kill($kill_mode)
|
||||||
|
|
||||||
|
# fullscreen enable|toggle [global]
|
||||||
|
# fullscreen disable
|
||||||
# fullscreen [global]
|
# fullscreen [global]
|
||||||
state FULLSCREEN:
|
state FULLSCREEN:
|
||||||
fullscreen_mode = 'global'
|
action = 'disable'
|
||||||
-> call cmd_fullscreen($fullscreen_mode)
|
-> call cmd_fullscreen($action, "output")
|
||||||
|
action = 'enable', 'toggle'
|
||||||
|
-> FULLSCREEN_MODE
|
||||||
|
action = ''
|
||||||
|
-> FULLSCREEN_COMPAT
|
||||||
|
|
||||||
|
state FULLSCREEN_MODE:
|
||||||
|
mode = 'global'
|
||||||
|
-> call cmd_fullscreen($action, $mode)
|
||||||
end
|
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
|
# split v|h|vertical|horizontal
|
||||||
state SPLIT:
|
state SPLIT:
|
||||||
|
@ -278,6 +278,8 @@ state FONT:
|
|||||||
state BINDING:
|
state BINDING:
|
||||||
release = '--release'
|
release = '--release'
|
||||||
->
|
->
|
||||||
|
whole_window = '--whole-window'
|
||||||
|
->
|
||||||
modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Ctrl', 'Mode_switch', '$mod'
|
modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Ctrl', 'Mode_switch', '$mod'
|
||||||
->
|
->
|
||||||
'+'
|
'+'
|
||||||
@ -288,8 +290,10 @@ state BINDING:
|
|||||||
state BINDCOMMAND:
|
state BINDCOMMAND:
|
||||||
release = '--release'
|
release = '--release'
|
||||||
->
|
->
|
||||||
|
whole_window = '--whole-window'
|
||||||
|
->
|
||||||
command = string
|
command = string
|
||||||
-> call cfg_binding($bindtype, $modifiers, $key, $release, $command)
|
-> call cfg_binding($bindtype, $modifiers, $key, $release, $whole_window, $command)
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# Mode configuration
|
# Mode configuration
|
||||||
@ -333,8 +337,10 @@ state MODE_BINDING:
|
|||||||
state MODE_BINDCOMMAND:
|
state MODE_BINDCOMMAND:
|
||||||
release = '--release'
|
release = '--release'
|
||||||
->
|
->
|
||||||
|
whole_window = '--whole-window'
|
||||||
|
->
|
||||||
command = string
|
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)
|
# Bar configuration (i3bar)
|
||||||
@ -358,6 +364,8 @@ state BAR:
|
|||||||
'hidden_state' -> BAR_HIDDEN_STATE
|
'hidden_state' -> BAR_HIDDEN_STATE
|
||||||
'id' -> BAR_ID
|
'id' -> BAR_ID
|
||||||
'modifier' -> BAR_MODIFIER
|
'modifier' -> BAR_MODIFIER
|
||||||
|
'wheel_up_cmd' -> BAR_WHEEL_UP_CMD
|
||||||
|
'wheel_down_cmd' -> BAR_WHEEL_DOWN_CMD
|
||||||
'position' -> BAR_POSITION
|
'position' -> BAR_POSITION
|
||||||
'output' -> BAR_OUTPUT
|
'output' -> BAR_OUTPUT
|
||||||
'tray_output' -> BAR_TRAY_OUTPUT
|
'tray_output' -> BAR_TRAY_OUTPUT
|
||||||
@ -403,6 +411,14 @@ state BAR_MODIFIER:
|
|||||||
modifier = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Control', 'Ctrl', 'Shift'
|
modifier = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Control', 'Ctrl', 'Shift'
|
||||||
-> call cfg_bar_modifier($modifier); BAR
|
-> 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:
|
state BAR_POSITION:
|
||||||
position = 'top', 'bottom'
|
position = 'top', 'bottom'
|
||||||
-> call cfg_bar_position($position); BAR
|
-> call cfg_bar_position($position); BAR
|
||||||
@ -412,7 +428,7 @@ state BAR_OUTPUT:
|
|||||||
-> call cfg_bar_output($output); BAR
|
-> call cfg_bar_output($output); BAR
|
||||||
|
|
||||||
state BAR_TRAY_OUTPUT:
|
state BAR_TRAY_OUTPUT:
|
||||||
output = string
|
output = word
|
||||||
-> call cfg_bar_tray_output($output); BAR
|
-> call cfg_bar_tray_output($output); BAR
|
||||||
|
|
||||||
state BAR_FONT:
|
state BAR_FONT:
|
||||||
|
@ -23,7 +23,7 @@ void run_assignments(i3Window *window) {
|
|||||||
|
|
||||||
/* Check if any assignments match */
|
/* Check if any assignments match */
|
||||||
Assignment *current;
|
Assignment *current;
|
||||||
TAILQ_FOREACH (current, &assignments, assignments) {
|
TAILQ_FOREACH(current, &assignments, assignments) {
|
||||||
if (!match_matches_window(&(current->match), window))
|
if (!match_matches_window(&(current->match), window))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -72,7 +72,7 @@ void run_assignments(i3Window *window) {
|
|||||||
Assignment *assignment_for(i3Window *window, int type) {
|
Assignment *assignment_for(i3Window *window, int type) {
|
||||||
Assignment *assignment;
|
Assignment *assignment;
|
||||||
|
|
||||||
TAILQ_FOREACH (assignment, &assignments, assignments) {
|
TAILQ_FOREACH(assignment, &assignments, assignments) {
|
||||||
if ((type != A_ANY && (assignment->type & type) == 0) ||
|
if ((type != A_ANY && (assignment->type & type) == 0) ||
|
||||||
!match_matches_window(&(assignment->match), window))
|
!match_matches_window(&(assignment->match), window))
|
||||||
continue;
|
continue;
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
*/
|
*/
|
||||||
#include "all.h"
|
#include "all.h"
|
||||||
|
|
||||||
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
|
||||||
pid_t command_error_nagbar_pid = -1;
|
pid_t command_error_nagbar_pid = -1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -25,7 +27,7 @@ static struct Mode *mode_from_name(const char *name) {
|
|||||||
struct Mode *mode;
|
struct Mode *mode;
|
||||||
|
|
||||||
/* Try to find the mode in the list of modes and return it */
|
/* 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)
|
if (strcmp(mode->name, name) == 0)
|
||||||
return mode;
|
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,
|
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));
|
Binding *new_binding = scalloc(sizeof(Binding));
|
||||||
DLOG("bindtype %s, modifiers %s, input code %s, release %s\n", bindtype, modifiers, input_code, release);
|
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->release = (release != NULL ? B_UPON_KEYRELEASE : B_UPON_KEYPRESS);
|
||||||
|
new_binding->whole_window = (whole_window != NULL);
|
||||||
if (strcmp(bindtype, "bindsym") == 0) {
|
if (strcmp(bindtype, "bindsym") == 0) {
|
||||||
new_binding->input_type = (strncasecmp(input_code, "button", (sizeof("button") - 1)) == 0
|
new_binding->input_type = (strncasecmp(input_code, "button", (sizeof("button") - 1)) == 0
|
||||||
? B_MOUSE
|
? 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) {
|
void grab_all_keys(xcb_connection_t *conn, bool bind_mode_switch) {
|
||||||
Binding *bind;
|
Binding *bind;
|
||||||
TAILQ_FOREACH (bind, bindings, bindings) {
|
TAILQ_FOREACH(bind, bindings, bindings) {
|
||||||
if (bind->input_type != B_KEYBOARD ||
|
if (bind->input_type != B_KEYBOARD ||
|
||||||
(bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) == 0) ||
|
(bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) == 0) ||
|
||||||
(!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) {
|
if (!is_release) {
|
||||||
/* On a press event, we first reset all B_UPON_KEYRELEASE_IGNORE_MODS
|
/* On a press event, we first reset all B_UPON_KEYRELEASE_IGNORE_MODS
|
||||||
* bindings back to B_UPON_KEYRELEASE */
|
* bindings back to B_UPON_KEYRELEASE */
|
||||||
TAILQ_FOREACH (bind, bindings, bindings) {
|
TAILQ_FOREACH(bind, bindings, bindings) {
|
||||||
if (bind->input_type != input_type)
|
if (bind->input_type != input_type)
|
||||||
continue;
|
continue;
|
||||||
if (bind->release == B_UPON_KEYRELEASE_IGNORE_MODS)
|
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
|
/* First compare the modifiers (unless this is a
|
||||||
* B_UPON_KEYRELEASE_IGNORE_MODS binding and this is a KeyRelease
|
* B_UPON_KEYRELEASE_IGNORE_MODS binding and this is a KeyRelease
|
||||||
* event) */
|
* event) */
|
||||||
@ -248,7 +251,7 @@ void translate_keysyms(void) {
|
|||||||
min_keycode = xcb_get_setup(conn)->min_keycode;
|
min_keycode = xcb_get_setup(conn)->min_keycode;
|
||||||
max_keycode = xcb_get_setup(conn)->max_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) {
|
if (bind->input_type == B_MOUSE) {
|
||||||
int button = atoi(bind->symbol + (sizeof("button") - 1));
|
int button = atoi(bind->symbol + (sizeof("button") - 1));
|
||||||
bind->keycode = button;
|
bind->keycode = button;
|
||||||
@ -263,8 +266,8 @@ void translate_keysyms(void) {
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* We need to translate the symbol to a keycode */
|
/* We need to translate the symbol to a keycode */
|
||||||
keysym = XStringToKeysym(bind->symbol);
|
keysym = xkb_keysym_from_name(bind->symbol, XKB_KEYSYM_NO_FLAGS);
|
||||||
if (keysym == NoSymbol) {
|
if (keysym == XKB_KEY_NoSymbol) {
|
||||||
ELOG("Could not translate string to key symbol: \"%s\"\n",
|
ELOG("Could not translate string to key symbol: \"%s\"\n",
|
||||||
bind->symbol);
|
bind->symbol);
|
||||||
continue;
|
continue;
|
||||||
@ -304,7 +307,7 @@ void switch_mode(const char *new_mode) {
|
|||||||
|
|
||||||
DLOG("Switching to mode %s\n", 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)
|
if (strcasecmp(mode->name, new_mode) != 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -334,8 +337,8 @@ void switch_mode(const char *new_mode) {
|
|||||||
*/
|
*/
|
||||||
void check_for_duplicate_bindings(struct context *context) {
|
void check_for_duplicate_bindings(struct context *context) {
|
||||||
Binding *bind, *current;
|
Binding *bind, *current;
|
||||||
TAILQ_FOREACH (current, bindings, bindings) {
|
TAILQ_FOREACH(current, bindings, bindings) {
|
||||||
TAILQ_FOREACH (bind, bindings, bindings) {
|
TAILQ_FOREACH(bind, bindings, bindings) {
|
||||||
/* Abort when we reach the current keybinding, only check the
|
/* Abort when we reach the current keybinding, only check the
|
||||||
* bindings before */
|
* bindings before */
|
||||||
if (bind == current)
|
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
|
* Creates a dynamically allocated copy of bind.
|
||||||
* running the binding's command. Caller should render tree if
|
*/
|
||||||
* needs_tree_render is true. Free with command_result_free().
|
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) {
|
CommandResult *run_binding(Binding *bind, Con *con) {
|
||||||
/* We need to copy the command since “reload” may be part of the command,
|
char *command;
|
||||||
* and then the memory that bind->command points to may not contain the
|
|
||||||
|
/* 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. */
|
* same data anymore. */
|
||||||
char *command_copy = sstrdup(bind->command);
|
if (con == NULL)
|
||||||
CommandResult *result = parse_command(command_copy, NULL);
|
command = sstrdup(bind->command);
|
||||||
free(command_copy);
|
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)
|
if (result->needs_tree_render)
|
||||||
tree_render();
|
tree_render();
|
||||||
@ -414,7 +456,8 @@ CommandResult *run_binding(Binding *bind) {
|
|||||||
free(pageraction);
|
free(pageraction);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: emit event for running a binding */
|
ipc_send_binding_event("run", bind_cp);
|
||||||
|
binding_free(bind_cp);
|
||||||
|
|
||||||
return result;
|
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)
|
if (con->parent->type == CT_DOCKAREA)
|
||||||
goto done;
|
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
|
/* Any click in a workspace should focus that workspace. If the
|
||||||
* workspace is on another output we need to do a workspace_show in
|
* workspace is on another output we need to do a workspace_show in
|
||||||
* order for i3bar (and others) to notice the change in workspace. */
|
* 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)
|
if (ws != focused_workspace)
|
||||||
workspace_show(ws);
|
workspace_show(ws);
|
||||||
focused_id = XCB_NONE;
|
|
||||||
|
|
||||||
/* get the floating con */
|
/* get the floating con */
|
||||||
Con *floatingcon = con_inside_floating(con);
|
Con *floatingcon = con_inside_floating(con);
|
||||||
@ -300,6 +327,7 @@ done:
|
|||||||
xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
|
xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
|
||||||
xcb_flush(conn);
|
xcb_flush(conn);
|
||||||
tree_render();
|
tree_render();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,9 +341,10 @@ done:
|
|||||||
*/
|
*/
|
||||||
int handle_button_press(xcb_button_press_event_t *event) {
|
int handle_button_press(xcb_button_press_event_t *event) {
|
||||||
Con *con;
|
Con *con;
|
||||||
DLOG("Button %d pressed on window 0x%08x (child 0x%08x) at (%d, %d) (root %d, %d)\n",
|
DLOG("Button %d %s 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->state, (event->response_type == XCB_BUTTON_PRESS ? "press" : "release"),
|
||||||
event->root_x, event->root_y);
|
event->event, event->child, event->event_x, event->event_y, event->root_x,
|
||||||
|
event->root_y);
|
||||||
|
|
||||||
last_timestamp = event->time;
|
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 (!(con = con_by_frame_id(event->event))) {
|
||||||
/* If the root window is clicked, find the relevant output from the
|
/* If the root window is clicked, find the relevant output from the
|
||||||
* click coordinates and focus the output's active workspace. */
|
* 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;
|
Con *output, *ws;
|
||||||
TAILQ_FOREACH (output, &(croot->nodes_head), nodes) {
|
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
||||||
if (con_is_internal(output) ||
|
if (con_is_internal(output) ||
|
||||||
!rect_contains(output->rect, event->event_x, event->event_y))
|
!rect_contains(output->rect, event->event_x, event->event_y))
|
||||||
continue;
|
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 */
|
/* Check if the click was on the decoration of a child */
|
||||||
Con *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))
|
if (!rect_contains(child->deco_rect, event->event_x, event->event_y))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
264
src/commands.c
264
src/commands.c
@ -273,7 +273,7 @@ void cmd_criteria_init(I3_CMD) {
|
|||||||
}
|
}
|
||||||
TAILQ_INIT(&owindows);
|
TAILQ_INIT(&owindows);
|
||||||
/* copy all_cons */
|
/* copy all_cons */
|
||||||
TAILQ_FOREACH (con, &all_cons, all_cons) {
|
TAILQ_FOREACH(con, &all_cons, all_cons) {
|
||||||
ow = smalloc(sizeof(owindow));
|
ow = smalloc(sizeof(owindow));
|
||||||
ow->con = con;
|
ow->con = con;
|
||||||
TAILQ_INSERT_TAIL(&owindows, ow, owindows);
|
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);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TAILQ_FOREACH (current, &owindows, owindows) {
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||||
con_move_to_workspace(current->con, ws, true, false);
|
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;
|
HANDLE_EMPTY_MATCH;
|
||||||
|
|
||||||
TAILQ_FOREACH (current, &owindows, owindows) {
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||||
con_move_to_workspace(current->con, ws, true, false);
|
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);
|
LOG("should move window to workspace %s\n", name);
|
||||||
/* get the workspace */
|
/* 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);
|
ws = maybe_auto_back_and_forth_workspace(ws);
|
||||||
|
|
||||||
HANDLE_EMPTY_MATCH;
|
HANDLE_EMPTY_MATCH;
|
||||||
|
|
||||||
TAILQ_FOREACH (current, &owindows, owindows) {
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||||
con_move_to_workspace(current->con, ws, true, false);
|
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 */
|
/* get the workspace */
|
||||||
Con *output, *workspace = NULL;
|
Con *output, *workspace = NULL;
|
||||||
|
|
||||||
char *endptr = NULL;
|
long parsed_num = ws_name_to_number(which);
|
||||||
long parsed_num = strtol(which, &endptr, 10);
|
|
||||||
if (parsed_num == LONG_MIN ||
|
if (parsed_num == -1) {
|
||||||
parsed_num == LONG_MAX ||
|
|
||||||
parsed_num < 0 ||
|
|
||||||
endptr == which) {
|
|
||||||
LOG("Could not parse initial part of \"%s\" as a number.\n", which);
|
LOG("Could not parse initial part of \"%s\" as a number.\n", which);
|
||||||
// TODO: better error message
|
// TODO: better error message
|
||||||
yerror("Could not parse number");
|
yerror("Could not parse number");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TAILQ_FOREACH (output, &(croot->nodes_head), nodes)
|
TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
|
||||||
GREP_FIRST(workspace, output_get_content(output),
|
GREP_FIRST(workspace, output_get_content(output),
|
||||||
child->num == parsed_num);
|
child->num == parsed_num);
|
||||||
|
|
||||||
@ -574,7 +591,7 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
|
|||||||
|
|
||||||
HANDLE_EMPTY_MATCH;
|
HANDLE_EMPTY_MATCH;
|
||||||
|
|
||||||
TAILQ_FOREACH (current, &owindows, owindows) {
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||||
con_move_to_workspace(current->con, workspace, true, false);
|
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. */
|
/* Ensure all the other children have a percentage set. */
|
||||||
Con *child;
|
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);
|
LOG("child->percent = %f (child %p)\n", child->percent, child);
|
||||||
if (child->percent == 0.0)
|
if (child->percent == 0.0)
|
||||||
child->percent = percentage;
|
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);
|
LOG("subtract_percent = %f\n", subtract_percent);
|
||||||
/* Ensure that the new percentages are positive and greater than
|
/* Ensure that the new percentages are positive and greater than
|
||||||
* 0.05 to have a reasonable minimum size. */
|
* 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)
|
if (child == current)
|
||||||
continue;
|
continue;
|
||||||
if (!definitelyGreaterThan(child->percent - subtract_percent, 0.05, DBL_EPSILON)) {
|
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);
|
current->percent += ((double)ppt / 100.0);
|
||||||
LOG("current->percent after = %f\n", current->percent);
|
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)
|
if (child == current)
|
||||||
continue;
|
continue;
|
||||||
child->percent -= subtract_percent;
|
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;
|
HANDLE_EMPTY_MATCH;
|
||||||
|
|
||||||
owindow *current;
|
owindow *current;
|
||||||
TAILQ_FOREACH (current, &owindows, owindows) {
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
/* Don't handle dock windows (issue #1201) */
|
/* Don't handle dock windows (issue #1201) */
|
||||||
if (current->con->window && current->con->window->dock) {
|
if (current->con->window && current->con->window->dock) {
|
||||||
DLOG("This is a dock window. Not resizing (con = %p)\n)", current->con);
|
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;
|
HANDLE_EMPTY_MATCH;
|
||||||
|
|
||||||
TAILQ_FOREACH (current, &owindows, owindows) {
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||||
int border_style = current->con->border_style;
|
int border_style = current->con->border_style;
|
||||||
char *end;
|
char *end;
|
||||||
@ -927,7 +944,7 @@ void cmd_append_layout(I3_CMD, char *path) {
|
|||||||
restore_open_placeholder_windows(parent);
|
restore_open_placeholder_windows(parent);
|
||||||
|
|
||||||
if (content == JSON_CONTENT_WORKSPACE)
|
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;
|
cmd_output->needs_tree_render = true;
|
||||||
}
|
}
|
||||||
@ -941,6 +958,12 @@ void cmd_workspace(I3_CMD, char *which) {
|
|||||||
|
|
||||||
DLOG("which=%s\n", 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)
|
if (strcmp(which, "next") == 0)
|
||||||
ws = workspace_next();
|
ws = workspace_next();
|
||||||
else if (strcmp(which, "prev") == 0)
|
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) {
|
void cmd_workspace_number(I3_CMD, char *which) {
|
||||||
Con *output, *workspace = NULL;
|
Con *output, *workspace = NULL;
|
||||||
|
|
||||||
char *endptr = NULL;
|
if (con_get_fullscreen_con(croot, CF_GLOBAL)) {
|
||||||
long parsed_num = strtol(which, &endptr, 10);
|
LOG("Cannot switch workspace while in global fullscreen\n");
|
||||||
if (parsed_num == LONG_MIN ||
|
ysuccess(false);
|
||||||
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");
|
|
||||||
|
|
||||||
return;
|
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),
|
GREP_FIRST(workspace, output_get_content(output),
|
||||||
child->num == parsed_num);
|
child->num == parsed_num);
|
||||||
|
|
||||||
@ -1007,6 +1032,12 @@ void cmd_workspace_number(I3_CMD, char *which) {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cmd_workspace_back_and_forth(I3_CMD) {
|
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();
|
workspace_back_and_forth();
|
||||||
|
|
||||||
cmd_output->needs_tree_render = true;
|
cmd_output->needs_tree_render = true;
|
||||||
@ -1025,10 +1056,39 @@ void cmd_workspace_name(I3_CMD, char *name) {
|
|||||||
return;
|
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);
|
DLOG("should switch to workspace %s\n", name);
|
||||||
if (maybe_back_and_forth(cmd_output, name))
|
if (maybe_back_and_forth(cmd_output, name))
|
||||||
return;
|
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;
|
cmd_output->needs_tree_render = true;
|
||||||
// XXX: default reply for now, make this a better reply
|
// 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");
|
DLOG("Clearing all windows which have that mark first\n");
|
||||||
|
|
||||||
Con *con;
|
Con *con;
|
||||||
TAILQ_FOREACH (con, &all_cons, all_cons) {
|
TAILQ_FOREACH(con, &all_cons, all_cons) {
|
||||||
if (con->mark && strcmp(con->mark, mark) == 0)
|
if (con->mark && strcmp(con->mark, mark) == 0)
|
||||||
FREE(con->mark);
|
FREE(con->mark);
|
||||||
}
|
}
|
||||||
@ -1053,7 +1113,7 @@ void cmd_mark(I3_CMD, char *mark) {
|
|||||||
|
|
||||||
HANDLE_EMPTY_MATCH;
|
HANDLE_EMPTY_MATCH;
|
||||||
|
|
||||||
TAILQ_FOREACH (current, &owindows, owindows) {
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||||
current->con->mark = sstrdup(mark);
|
current->con->mark = sstrdup(mark);
|
||||||
}
|
}
|
||||||
@ -1070,13 +1130,13 @@ void cmd_mark(I3_CMD, char *mark) {
|
|||||||
void cmd_unmark(I3_CMD, char *mark) {
|
void cmd_unmark(I3_CMD, char *mark) {
|
||||||
if (mark == NULL) {
|
if (mark == NULL) {
|
||||||
Con *con;
|
Con *con;
|
||||||
TAILQ_FOREACH (con, &all_cons, all_cons) {
|
TAILQ_FOREACH(con, &all_cons, all_cons) {
|
||||||
FREE(con->mark);
|
FREE(con->mark);
|
||||||
}
|
}
|
||||||
DLOG("removed all window marks");
|
DLOG("removed all window marks");
|
||||||
} else {
|
} else {
|
||||||
Con *con;
|
Con *con;
|
||||||
TAILQ_FOREACH (con, &all_cons, all_cons) {
|
TAILQ_FOREACH(con, &all_cons, all_cons) {
|
||||||
if (con->mark && strcmp(con->mark, mark) == 0)
|
if (con->mark && strcmp(con->mark, mark) == 0)
|
||||||
FREE(con->mark);
|
FREE(con->mark);
|
||||||
}
|
}
|
||||||
@ -1116,7 +1176,7 @@ void cmd_move_con_to_output(I3_CMD, char *name) {
|
|||||||
Output *output;
|
Output *output;
|
||||||
|
|
||||||
// TODO: fix the handling of criteria
|
// TODO: fix the handling of criteria
|
||||||
TAILQ_FOREACH (current, &owindows, owindows)
|
TAILQ_FOREACH(current, &owindows, owindows)
|
||||||
current_output = get_output_of_con(current->con);
|
current_output = get_output_of_con(current->con);
|
||||||
|
|
||||||
assert(current_output != NULL);
|
assert(current_output != NULL);
|
||||||
@ -1147,7 +1207,7 @@ void cmd_move_con_to_output(I3_CMD, char *name) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TAILQ_FOREACH (current, &owindows, owindows) {
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||||
con_move_to_workspace(current->con, ws, true, false);
|
con_move_to_workspace(current->con, ws, true, false);
|
||||||
}
|
}
|
||||||
@ -1168,7 +1228,7 @@ void cmd_floating(I3_CMD, char *floating_mode) {
|
|||||||
|
|
||||||
HANDLE_EMPTY_MATCH;
|
HANDLE_EMPTY_MATCH;
|
||||||
|
|
||||||
TAILQ_FOREACH (current, &owindows, owindows) {
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||||
if (strcmp(floating_mode, "toggle") == 0) {
|
if (strcmp(floating_mode, "toggle") == 0) {
|
||||||
DLOG("should toggle mode\n");
|
DLOG("should toggle mode\n");
|
||||||
@ -1198,7 +1258,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
|
|||||||
HANDLE_EMPTY_MATCH;
|
HANDLE_EMPTY_MATCH;
|
||||||
|
|
||||||
owindow *current;
|
owindow *current;
|
||||||
TAILQ_FOREACH (current, &owindows, owindows) {
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
Output *current_output = get_output_of_con(current->con);
|
Output *current_output = get_output_of_con(current->con);
|
||||||
if (!current_output) {
|
if (!current_output) {
|
||||||
ELOG("Cannot get current output. This is a bug in i3.\n");
|
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 */
|
/* check if we can find a workspace assigned to this output */
|
||||||
bool used_assignment = false;
|
bool used_assignment = false;
|
||||||
struct Workspace_Assignment *assignment;
|
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)
|
if (strcmp(assignment->output, current_output->name) != 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* check if this workspace is already attached to the tree */
|
/* check if this workspace is already attached to the tree */
|
||||||
Con *workspace = NULL, *out;
|
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),
|
GREP_FIRST(workspace, output_get_content(out),
|
||||||
!strcasecmp(child->name, assignment->name));
|
!strcasecmp(child->name, assignment->name));
|
||||||
if (workspace != NULL)
|
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);
|
create_workspace_on_output(current_output, ws->parent);
|
||||||
|
|
||||||
/* notify the IPC listeners */
|
/* notify the IPC listeners */
|
||||||
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
|
ipc_send_workspace_event("init", ws, NULL);
|
||||||
}
|
}
|
||||||
DLOG("Detaching\n");
|
DLOG("Detaching\n");
|
||||||
|
|
||||||
@ -1271,10 +1331,10 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
|
|||||||
|
|
||||||
/* fix the coordinates of the floating containers */
|
/* fix the coordinates of the floating containers */
|
||||||
Con *floating_con;
|
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));
|
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) {
|
if (workspace_was_visible) {
|
||||||
/* Focus the moved workspace on the destination output. */
|
/* Focus the moved workspace on the destination output. */
|
||||||
workspace_show(ws);
|
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.
|
* focus order/number of other workspaces on the output.
|
||||||
* Instead, we loop through the available workspaces and only work with
|
* Instead, we loop through the available workspaces and only work with
|
||||||
* previously_visible_ws if we still find it. */
|
* 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)
|
if (ws != previously_visible_ws)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -1313,7 +1373,7 @@ void cmd_split(I3_CMD, char *direction) {
|
|||||||
if (match_is_empty(current_match))
|
if (match_is_empty(current_match))
|
||||||
tree_split(focused, (direction[0] == 'v' ? VERT : HORIZ));
|
tree_split(focused, (direction[0] == 'v' ? VERT : HORIZ));
|
||||||
else {
|
else {
|
||||||
TAILQ_FOREACH (current, &owindows, owindows) {
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||||
tree_split(current->con, (direction[0] == 'v' ? VERT : HORIZ));
|
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))
|
if (match_is_empty(current_match))
|
||||||
tree_close_con(kill_mode);
|
tree_close_con(kill_mode);
|
||||||
else {
|
else {
|
||||||
TAILQ_FOREACH (current, &owindows, owindows) {
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||||
tree_close(current->con, kill_mode, false, false);
|
tree_close(current->con, kill_mode, false, false);
|
||||||
}
|
}
|
||||||
@ -1418,7 +1478,7 @@ void cmd_focus_window_mode(I3_CMD, char *window_mode) {
|
|||||||
else
|
else
|
||||||
window_mode = "floating";
|
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) ||
|
if ((strcmp(window_mode, "floating") == 0 && current->type != CT_FLOATING_CON) ||
|
||||||
(strcmp(window_mode, "tiling") == 0 && current->type == CT_FLOATING_CON))
|
(strcmp(window_mode, "tiling") == 0 && current->type == CT_FLOATING_CON))
|
||||||
continue;
|
continue;
|
||||||
@ -1480,7 +1540,7 @@ void cmd_focus(I3_CMD) {
|
|||||||
Con *__i3_scratch = workspace_get("__i3_scratch", NULL);
|
Con *__i3_scratch = workspace_get("__i3_scratch", NULL);
|
||||||
int count = 0;
|
int count = 0;
|
||||||
owindow *current;
|
owindow *current;
|
||||||
TAILQ_FOREACH (current, &owindows, owindows) {
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
Con *ws = con_get_workspace(current->con);
|
Con *ws = con_get_workspace(current->con);
|
||||||
/* If no workspace could be found, this was a dock window.
|
/* If no workspace could be found, this was a dock window.
|
||||||
* Just skip it, you cannot focus dock windows. */
|
* 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) {
|
void cmd_fullscreen(I3_CMD, char *action, char *fullscreen_mode) {
|
||||||
if (fullscreen_mode == NULL)
|
fullscreen_mode_t mode = strcmp(fullscreen_mode, "global") == 0 ? CF_GLOBAL : CF_OUTPUT;
|
||||||
fullscreen_mode = "output";
|
DLOG("%s fullscreen, mode = %s\n", action, fullscreen_mode);
|
||||||
DLOG("toggling fullscreen, mode = %s\n", fullscreen_mode);
|
|
||||||
owindow *current;
|
owindow *current;
|
||||||
|
|
||||||
HANDLE_EMPTY_MATCH;
|
HANDLE_EMPTY_MATCH;
|
||||||
|
|
||||||
TAILQ_FOREACH (current, &owindows, owindows) {
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
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;
|
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
|
// 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);
|
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);
|
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);
|
DLOG("floating move with %d pixels\n", px);
|
||||||
Rect newrect = focused->parent->rect;
|
Rect newrect = current->con->parent->rect;
|
||||||
if (strcmp(direction, "left") == 0) {
|
if (strcmp(direction, "left") == 0) {
|
||||||
newrect.x -= px;
|
newrect.x -= px;
|
||||||
} else if (strcmp(direction, "right") == 0) {
|
} 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) {
|
} else if (strcmp(direction, "down") == 0) {
|
||||||
newrect.y += px;
|
newrect.y += px;
|
||||||
}
|
}
|
||||||
floating_reposition(focused->parent, newrect);
|
floating_reposition(current->con->parent, newrect);
|
||||||
} else {
|
} 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;
|
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
|
// XXX: default reply for now, make this a better reply
|
||||||
ysuccess(true);
|
ysuccess(true);
|
||||||
@ -1622,7 +1698,7 @@ void cmd_layout(I3_CMD, char *layout_str) {
|
|||||||
if (match_is_empty(current_match))
|
if (match_is_empty(current_match))
|
||||||
con_set_layout(focused, layout);
|
con_set_layout(focused, layout);
|
||||||
else {
|
else {
|
||||||
TAILQ_FOREACH (current, &owindows, owindows) {
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||||
con_set_layout(current->con, layout);
|
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))
|
if (match_is_empty(current_match))
|
||||||
con_toggle_layout(focused, toggle_mode);
|
con_toggle_layout(focused, toggle_mode);
|
||||||
else {
|
else {
|
||||||
TAILQ_FOREACH (current, &owindows, owindows) {
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||||
con_toggle_layout(current->con, toggle_mode);
|
con_toggle_layout(current->con, toggle_mode);
|
||||||
}
|
}
|
||||||
@ -1685,7 +1761,7 @@ void cmd_reload(I3_CMD) {
|
|||||||
load_configuration(conn, NULL, true);
|
load_configuration(conn, NULL, true);
|
||||||
x_set_i3_atoms();
|
x_set_i3_atoms();
|
||||||
/* Send an IPC event just in case the ws names have changed */
|
/* 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 */
|
/* Send an update event for the barconfig just in case it has changed */
|
||||||
update_barconfig();
|
update_barconfig();
|
||||||
|
|
||||||
@ -1745,7 +1821,7 @@ void cmd_focus_output(I3_CMD, char *name) {
|
|||||||
Output *current_output = NULL;
|
Output *current_output = NULL;
|
||||||
Output *output;
|
Output *output;
|
||||||
|
|
||||||
TAILQ_FOREACH (current, &owindows, owindows)
|
TAILQ_FOREACH(current, &owindows, owindows)
|
||||||
current_output = get_output_of_con(current->con);
|
current_output = get_output_of_con(current->con);
|
||||||
assert(current_output != NULL);
|
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) {
|
void cmd_move_window_to_position(I3_CMD, char *method, char *cx, char *cy) {
|
||||||
int x = atoi(cx);
|
int x = atoi(cx);
|
||||||
int y = atoi(cy);
|
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");
|
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) {
|
if (strcmp(method, "absolute") == 0) {
|
||||||
focused->parent->rect.x = x;
|
current->con->parent->rect.x = x;
|
||||||
focused->parent->rect.y = y;
|
current->con->parent->rect.y = y;
|
||||||
|
|
||||||
DLOG("moving to absolute position %d %d\n", x, 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;
|
cmd_output->needs_tree_render = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(method, "position") == 0) {
|
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);
|
DLOG("moving to position %d %d\n", x, y);
|
||||||
newrect.x = x;
|
newrect.x = x;
|
||||||
newrect.y = y;
|
newrect.y = y;
|
||||||
|
|
||||||
floating_reposition(focused->parent, newrect);
|
floating_reposition(current->con->parent, newrect);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: default reply for now, make this a better reply
|
// XXX: default reply for now, make this a better reply
|
||||||
|
if (!has_error)
|
||||||
ysuccess(true);
|
ysuccess(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1856,7 +1944,7 @@ void cmd_move_scratchpad(I3_CMD) {
|
|||||||
|
|
||||||
HANDLE_EMPTY_MATCH;
|
HANDLE_EMPTY_MATCH;
|
||||||
|
|
||||||
TAILQ_FOREACH (current, &owindows, owindows) {
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||||
scratchpad_move(current->con);
|
scratchpad_move(current->con);
|
||||||
}
|
}
|
||||||
@ -1877,7 +1965,7 @@ void cmd_scratchpad_show(I3_CMD) {
|
|||||||
if (match_is_empty(current_match)) {
|
if (match_is_empty(current_match)) {
|
||||||
scratchpad_show(NULL);
|
scratchpad_show(NULL);
|
||||||
} else {
|
} else {
|
||||||
TAILQ_FOREACH (current, &owindows, owindows) {
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||||
scratchpad_show(current->con);
|
scratchpad_show(current->con);
|
||||||
}
|
}
|
||||||
@ -1906,7 +1994,7 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
|
|||||||
|
|
||||||
Con *output, *workspace = NULL;
|
Con *output, *workspace = NULL;
|
||||||
if (old_name) {
|
if (old_name) {
|
||||||
TAILQ_FOREACH (output, &(croot->nodes_head), nodes)
|
TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
|
||||||
GREP_FIRST(workspace, output_get_content(output),
|
GREP_FIRST(workspace, output_get_content(output),
|
||||||
!strcasecmp(child->name, old_name));
|
!strcasecmp(child->name, old_name));
|
||||||
} else {
|
} else {
|
||||||
@ -1922,7 +2010,7 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Con *check_dest = NULL;
|
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),
|
GREP_FIRST(check_dest, output_get_content(output),
|
||||||
!strcasecmp(child->name, new_name));
|
!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. */
|
/* Change the name and try to parse it as a number. */
|
||||||
FREE(workspace->name);
|
FREE(workspace->name);
|
||||||
workspace->name = sstrdup(new_name);
|
workspace->name = sstrdup(new_name);
|
||||||
char *endptr = NULL;
|
|
||||||
long parsed_num = strtol(new_name, &endptr, 10);
|
workspace->num = ws_name_to_number(new_name);
|
||||||
if (parsed_num == LONG_MIN ||
|
|
||||||
parsed_num == LONG_MAX ||
|
|
||||||
parsed_num < 0 ||
|
|
||||||
endptr == new_name)
|
|
||||||
workspace->num = -1;
|
|
||||||
else
|
|
||||||
workspace->num = parsed_num;
|
|
||||||
LOG("num = %d\n", workspace->num);
|
LOG("num = %d\n", workspace->num);
|
||||||
|
|
||||||
/* By re-attaching, the sort order will be correct afterwards. */
|
/* 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;
|
cmd_output->needs_tree_render = true;
|
||||||
ysuccess(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;
|
bool changed_sth = false;
|
||||||
Barconfig *current = NULL;
|
Barconfig *current = NULL;
|
||||||
TAILQ_FOREACH (current, &barconfigs, configs) {
|
TAILQ_FOREACH(current, &barconfigs, configs) {
|
||||||
if (bar_id && strcmp(current->id, bar_id) != 0)
|
if (bar_id && strcmp(current->id, bar_id) != 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -2027,7 +2111,7 @@ bool cmd_bar_hidden_state(char *bar_hidden_state, char *bar_id) {
|
|||||||
|
|
||||||
bool changed_sth = false;
|
bool changed_sth = false;
|
||||||
Barconfig *current = NULL;
|
Barconfig *current = NULL;
|
||||||
TAILQ_FOREACH (current, &barconfigs, configs) {
|
TAILQ_FOREACH(current, &barconfigs, configs) {
|
||||||
if (bar_id && strcmp(current->id, bar_id) != 0)
|
if (bar_id && strcmp(current->id, bar_id) != 0)
|
||||||
continue;
|
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
|
* 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
|
* 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 ||
|
if (strcmp(token->name, "string") == 0 ||
|
||||||
strcmp(token->name, "word") == 0) {
|
strcmp(token->name, "word") == 0) {
|
||||||
const char *beginning = walk;
|
char *str = parse_string(&walk, (token->name[0] != 's'));
|
||||||
/* Handle quoted strings (or words). */
|
if (str != NULL) {
|
||||||
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];
|
|
||||||
}
|
|
||||||
if (token->identifier)
|
if (token->identifier)
|
||||||
push_string(token->identifier, str);
|
push_string(token->identifier, str);
|
||||||
/* If we are at the end of a quoted string, skip the ending
|
/* 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"
|
#include "all.h"
|
||||||
|
#include "yajl_utils.h"
|
||||||
char *colors[] = {
|
|
||||||
"#ff0000",
|
|
||||||
"#00FF00",
|
|
||||||
"#0000FF",
|
|
||||||
"#ff00ff",
|
|
||||||
"#00ffff",
|
|
||||||
"#ffff00",
|
|
||||||
"#aa0000",
|
|
||||||
"#00aa00",
|
|
||||||
"#0000aa",
|
|
||||||
"#aa00aa"};
|
|
||||||
|
|
||||||
static void con_on_remove_child(Con *con);
|
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
|
* 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;
|
Con *parent = con;
|
||||||
|
|
||||||
while (parent && parent->type != CT_WORKSPACE && parent->type != CT_DOCKAREA) {
|
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;
|
new->depth = window->depth;
|
||||||
else
|
else
|
||||||
new->depth = XCB_COPY_FROM_PARENT;
|
new->depth = XCB_COPY_FROM_PARENT;
|
||||||
static int cnt = 0;
|
DLOG("opening window\n");
|
||||||
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;
|
|
||||||
|
|
||||||
TAILQ_INIT(&(new->floating_head));
|
TAILQ_INIT(&(new->floating_head));
|
||||||
TAILQ_INIT(&(new->nodes_head));
|
TAILQ_INIT(&(new->nodes_head));
|
||||||
@ -142,7 +122,7 @@ void con_attach(Con *con, Con *parent, bool ignore_focus) {
|
|||||||
} else {
|
} else {
|
||||||
if (!ignore_focus) {
|
if (!ignore_focus) {
|
||||||
/* Get the first tiling container in focus stack */
|
/* 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)
|
if (loop->type == CT_FLOATING_CON)
|
||||||
continue;
|
continue;
|
||||||
current = loop;
|
current = loop;
|
||||||
@ -231,6 +211,7 @@ void con_focus(Con *con) {
|
|||||||
con->urgent = false;
|
con->urgent = false;
|
||||||
con_update_parents_urgency(con);
|
con_update_parents_urgency(con);
|
||||||
workspace_update_urgent_flag(con_get_workspace(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);
|
TAILQ_REMOVE(&bfs_head, entry, entries);
|
||||||
free(entry);
|
free(entry);
|
||||||
|
|
||||||
TAILQ_FOREACH (child, &(current->nodes_head), nodes) {
|
TAILQ_FOREACH(child, &(current->nodes_head), nodes) {
|
||||||
entry = smalloc(sizeof(struct bfs_entry));
|
entry = smalloc(sizeof(struct bfs_entry));
|
||||||
entry->con = child;
|
entry->con = child;
|
||||||
TAILQ_INSERT_TAIL(&bfs_head, entry, entries);
|
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 = smalloc(sizeof(struct bfs_entry));
|
||||||
entry->con = child;
|
entry->con = child;
|
||||||
TAILQ_INSERT_TAIL(&bfs_head, entry, entries);
|
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_by_window_id(xcb_window_t window) {
|
||||||
Con *con;
|
Con *con;
|
||||||
TAILQ_FOREACH (con, &all_cons, all_cons)
|
TAILQ_FOREACH(con, &all_cons, all_cons)
|
||||||
if (con->window != NULL && con->window->id == window)
|
if (con->window != NULL && con->window->id == window)
|
||||||
return con;
|
return con;
|
||||||
return NULL;
|
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_by_frame_id(xcb_window_t frame) {
|
||||||
Con *con;
|
Con *con;
|
||||||
TAILQ_FOREACH (con, &all_cons, all_cons)
|
TAILQ_FOREACH(con, &all_cons, all_cons)
|
||||||
if (con->frame == frame)
|
if (con->frame == frame)
|
||||||
return con;
|
return con;
|
||||||
return NULL;
|
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("searching con for window %p starting at con %p\n", window, con);
|
||||||
//DLOG("class == %s\n", window->class_class);
|
//DLOG("class == %s\n", window->class_class);
|
||||||
|
|
||||||
TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
|
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
||||||
TAILQ_FOREACH (match, &(child->swallow_head), matches) {
|
TAILQ_FOREACH(match, &(child->swallow_head), matches) {
|
||||||
if (!match_matches_window(match, window))
|
if (!match_matches_window(match, window))
|
||||||
continue;
|
continue;
|
||||||
if (store_match != NULL)
|
if (store_match != NULL)
|
||||||
@ -503,8 +484,8 @@ Con *con_for_window(Con *con, i3Window *window, Match **store_match) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TAILQ_FOREACH (child, &(con->floating_head), floating_windows) {
|
TAILQ_FOREACH(child, &(con->floating_head), floating_windows) {
|
||||||
TAILQ_FOREACH (match, &(child->swallow_head), matches) {
|
TAILQ_FOREACH(match, &(child->swallow_head), matches) {
|
||||||
if (!match_matches_window(match, window))
|
if (!match_matches_window(match, window))
|
||||||
continue;
|
continue;
|
||||||
if (store_match != NULL)
|
if (store_match != NULL)
|
||||||
@ -527,7 +508,7 @@ int con_num_children(Con *con) {
|
|||||||
Con *child;
|
Con *child;
|
||||||
int children = 0;
|
int children = 0;
|
||||||
|
|
||||||
TAILQ_FOREACH (child, &(con->nodes_head), nodes)
|
TAILQ_FOREACH(child, &(con->nodes_head), nodes)
|
||||||
children++;
|
children++;
|
||||||
|
|
||||||
return children;
|
return children;
|
||||||
@ -547,7 +528,7 @@ void con_fix_percent(Con *con) {
|
|||||||
// with a percentage set we have
|
// with a percentage set we have
|
||||||
double total = 0.0;
|
double total = 0.0;
|
||||||
int children_with_percent = 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) {
|
if (child->percent > 0.0) {
|
||||||
total += child->percent;
|
total += child->percent;
|
||||||
++children_with_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
|
// if there were children without a percentage set, set to a value that
|
||||||
// will make those children proportional to all others
|
// will make those children proportional to all others
|
||||||
if (children_with_percent != children) {
|
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 (child->percent <= 0.0) {
|
||||||
if (children_with_percent == 0)
|
if (children_with_percent == 0)
|
||||||
total += (child->percent = 1.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
|
// if we got a zero, just distribute the space equally, otherwise
|
||||||
// distribute according to the proportions we got
|
// distribute according to the proportions we got
|
||||||
if (total == 0.0) {
|
if (total == 0.0) {
|
||||||
TAILQ_FOREACH (child, &(con->nodes_head), nodes)
|
TAILQ_FOREACH(child, &(con->nodes_head), nodes)
|
||||||
child->percent = 1.0 / children;
|
child->percent = 1.0 / children;
|
||||||
} else if (total != 1.0) {
|
} else if (total != 1.0) {
|
||||||
TAILQ_FOREACH (child, &(con->nodes_head), nodes)
|
TAILQ_FOREACH(child, &(con->nodes_head), nodes)
|
||||||
child->percent /= total;
|
child->percent /= total;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -585,37 +566,27 @@ void con_fix_percent(Con *con) {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void con_toggle_fullscreen(Con *con, int fullscreen_mode) {
|
void con_toggle_fullscreen(Con *con, int fullscreen_mode) {
|
||||||
Con *workspace, *fullscreen;
|
|
||||||
|
|
||||||
if (con->type == CT_WORKSPACE) {
|
if (con->type == CT_WORKSPACE) {
|
||||||
DLOG("You cannot make a workspace fullscreen.\n");
|
DLOG("You cannot make a workspace fullscreen.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DLOG("toggling fullscreen for %p / %s\n", con, con->name);
|
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;
|
con->fullscreen_mode = fullscreen_mode;
|
||||||
} else {
|
|
||||||
/* 1: disable fullscreen */
|
|
||||||
con->fullscreen_mode = CF_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
DLOG("mode now: %d\n", con->fullscreen_mode);
|
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);
|
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
|
* Moves the given container to the currently focused container on the given
|
||||||
* workspace.
|
* workspace.
|
||||||
@ -807,7 +851,7 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
|
|||||||
|
|
||||||
if (!con_is_leaf(con)) {
|
if (!con_is_leaf(con)) {
|
||||||
Con *child;
|
Con *child;
|
||||||
TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
|
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
||||||
if (!child->window)
|
if (!child->window)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -832,6 +876,8 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
CALL(parent, on_remove_child);
|
CALL(parent, on_remove_child);
|
||||||
|
|
||||||
|
ipc_send_window_event("move", con);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1003,7 +1049,7 @@ Con *con_descend_tiling_focused(Con *con) {
|
|||||||
return next;
|
return next;
|
||||||
do {
|
do {
|
||||||
before = next;
|
before = next;
|
||||||
TAILQ_FOREACH (child, &(next->focus_head), focused) {
|
TAILQ_FOREACH(child, &(next->focus_head), focused) {
|
||||||
if (child->type == CT_FLOATING_CON)
|
if (child->type == CT_FLOATING_CON)
|
||||||
continue;
|
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,
|
/* Wrong orientation. We use the last focused con. Within that con,
|
||||||
* we recurse to chose the left/right con or at least the last
|
* we recurse to chose the left/right con or at least the last
|
||||||
* focused one. */
|
* focused one. */
|
||||||
TAILQ_FOREACH (current, &(con->focus_head), focused) {
|
TAILQ_FOREACH(current, &(con->focus_head), focused) {
|
||||||
if (current->type != CT_FLOATING_CON) {
|
if (current->type != CT_FLOATING_CON) {
|
||||||
most = current;
|
most = current;
|
||||||
break;
|
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,
|
/* Wrong orientation. We use the last focused con. Within that con,
|
||||||
* we recurse to chose the top/bottom con or at least the last
|
* we recurse to chose the top/bottom con or at least the last
|
||||||
* focused one. */
|
* focused one. */
|
||||||
TAILQ_FOREACH (current, &(con->focus_head), focused) {
|
TAILQ_FOREACH(current, &(con->focus_head), focused) {
|
||||||
if (current->type != CT_FLOATING_CON) {
|
if (current->type != CT_FLOATING_CON) {
|
||||||
most = current;
|
most = current;
|
||||||
break;
|
break;
|
||||||
@ -1390,8 +1436,15 @@ static void con_on_remove_child(Con *con) {
|
|||||||
if (con->type == CT_WORKSPACE) {
|
if (con->type == CT_WORKSPACE) {
|
||||||
if (TAILQ_EMPTY(&(con->focus_head)) && !workspace_is_visible(con)) {
|
if (TAILQ_EMPTY(&(con->focus_head)) && !workspace_is_visible(con)) {
|
||||||
LOG("Closing old workspace (%p / %s), it is empty\n", con, con->name);
|
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);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
@ -1432,7 +1485,7 @@ Rect con_minimum_size(Con *con) {
|
|||||||
if (con->layout == L_STACKED || con->layout == L_TABBED) {
|
if (con->layout == L_STACKED || con->layout == L_TABBED) {
|
||||||
uint32_t max_width = 0, max_height = 0, deco_height = 0;
|
uint32_t max_width = 0, max_height = 0, deco_height = 0;
|
||||||
Con *child;
|
Con *child;
|
||||||
TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
|
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
||||||
Rect min = con_minimum_size(child);
|
Rect min = con_minimum_size(child);
|
||||||
deco_height += child->deco_rect.height;
|
deco_height += child->deco_rect.height;
|
||||||
max_width = max(max_width, min.width);
|
max_width = max(max_width, min.width);
|
||||||
@ -1449,7 +1502,7 @@ Rect con_minimum_size(Con *con) {
|
|||||||
if (con_is_split(con)) {
|
if (con_is_split(con)) {
|
||||||
uint32_t width = 0, height = 0;
|
uint32_t width = 0, height = 0;
|
||||||
Con *child;
|
Con *child;
|
||||||
TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
|
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
||||||
Rect min = con_minimum_size(child);
|
Rect min = con_minimum_size(child);
|
||||||
if (con->layout == L_SPLITH) {
|
if (con->layout == L_SPLITH) {
|
||||||
width += min.width;
|
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
|
/* We are not interested in floating windows since they can only be
|
||||||
* attached to a workspace → nodes_head instead of focus_head */
|
* 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))
|
if (con_has_urgent_child(child))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1603,14 +1656,16 @@ void con_set_urgency(Con *con, bool urgent) {
|
|||||||
|
|
||||||
con_update_parents_urgency(con);
|
con_update_parents_urgency(con);
|
||||||
|
|
||||||
if (con->urgent == urgent)
|
|
||||||
LOG("Urgency flag changed to %d\n", con->urgent);
|
|
||||||
|
|
||||||
Con *ws;
|
Con *ws;
|
||||||
/* Set the urgency flag on the workspace, if a workspace could be found
|
/* Set the urgency flag on the workspace, if a workspace could be found
|
||||||
* (for dock clients, that is not the case). */
|
* (for dock clients, that is not the case). */
|
||||||
if ((ws = con_get_workspace(con)) != NULL)
|
if ((ws = con_get_workspace(con)) != NULL)
|
||||||
workspace_update_urgent_flag(ws);
|
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 */
|
/* 2) append representation of children */
|
||||||
Con *child;
|
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 *child_txt = con_get_tree_representation(child);
|
||||||
|
|
||||||
char *tmp_buf;
|
char *tmp_buf;
|
||||||
|
25
src/config.c
25
src/config.c
@ -11,9 +11,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#include "all.h"
|
#include "all.h"
|
||||||
|
#include <xkbcommon/xkbcommon.h>
|
||||||
/* We need Xlib for XStringToKeysym */
|
|
||||||
#include <X11/Xlib.h>
|
|
||||||
|
|
||||||
char *current_configpath = NULL;
|
char *current_configpath = NULL;
|
||||||
Config config;
|
Config config;
|
||||||
@ -36,7 +34,7 @@ void ungrab_all_keys(xcb_connection_t *conn) {
|
|||||||
*/
|
*/
|
||||||
void update_barconfig() {
|
void update_barconfig() {
|
||||||
Barconfig *current;
|
Barconfig *current;
|
||||||
TAILQ_FOREACH (current, &barconfigs, configs) {
|
TAILQ_FOREACH(current, &barconfigs, configs) {
|
||||||
ipc_send_barconfig_update_event(current);
|
ipc_send_barconfig_update_event(current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,12 +112,19 @@ static char *get_config_path(const char *override_configpath) {
|
|||||||
* parse_file().
|
* 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);
|
char *path = get_config_path(override_configpath);
|
||||||
LOG("Parsing configfile %s\n", path);
|
LOG("Parsing configfile %s\n", path);
|
||||||
FREE(current_configpath);
|
FREE(current_configpath);
|
||||||
current_configpath = path;
|
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)) {
|
while (!TAILQ_EMPTY(bindings)) {
|
||||||
bind = TAILQ_FIRST(bindings);
|
bind = TAILQ_FIRST(bindings);
|
||||||
TAILQ_REMOVE(bindings, bind, bindings);
|
TAILQ_REMOVE(bindings, bind, bindings);
|
||||||
FREE(bind->translated_to);
|
binding_free(bind);
|
||||||
FREE(bind->command);
|
|
||||||
FREE(bind);
|
|
||||||
}
|
}
|
||||||
FREE(bindings);
|
FREE(bindings);
|
||||||
SLIST_REMOVE(&modes, mode, Mode, modes);
|
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 */
|
/* Invalidate pixmap caches in case font or colors changed */
|
||||||
Con *con;
|
Con *con;
|
||||||
TAILQ_FOREACH (con, &all_cons, all_cons)
|
TAILQ_FOREACH(con, &all_cons, all_cons)
|
||||||
FREE(con->deco_render_params);
|
FREE(con->deco_render_params);
|
||||||
|
|
||||||
/* Get rid of the current font */
|
/* 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)
|
if (config.workspace_urgency_timer == 0)
|
||||||
config.workspace_urgency_timer = 0.5;
|
config.workspace_urgency_timer = 0.5;
|
||||||
|
|
||||||
parse_configuration(override_configpath);
|
parse_configuration(override_configpath, true);
|
||||||
|
|
||||||
if (reload) {
|
if (reload) {
|
||||||
translate_keysyms();
|
translate_keysyms();
|
||||||
|
@ -171,8 +171,8 @@ CFGFUN(font, const char *font) {
|
|||||||
font_pattern = sstrdup(font);
|
font_pattern = sstrdup(font);
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
configure_binding(bindtype, modifiers, key, release, command, DEFAULT_BINDING_MODE);
|
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;
|
static char *current_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) {
|
||||||
configure_binding(bindtype, modifiers, key, release, command, current_mode);
|
configure_binding(bindtype, modifiers, key, release, whole_window, command, current_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
CFGFUN(enter_mode, const char *modename) {
|
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) {
|
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 = border_style;
|
||||||
config.default_border_width = border_width;
|
config.default_border_width = logical_px(border_width);
|
||||||
} else {
|
} 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 = 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 */
|
* outputs */
|
||||||
struct Workspace_Assignment *assignment;
|
struct Workspace_Assignment *assignment;
|
||||||
bool duplicate = false;
|
bool duplicate = false;
|
||||||
TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) {
|
TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
|
||||||
if (strcasecmp(assignment->name, workspace) == 0) {
|
if (strcasecmp(assignment->name, workspace) == 0) {
|
||||||
ELOG("You have a duplicate workspace assignment for workspace \"%s\"\n",
|
ELOG("You have a duplicate workspace assignment for workspace \"%s\"\n",
|
||||||
workspace);
|
workspace);
|
||||||
@ -460,6 +462,16 @@ CFGFUN(bar_modifier, const char *modifier) {
|
|||||||
current_bar.modifier = M_SHIFT;
|
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) {
|
CFGFUN(bar_position, const char *position) {
|
||||||
current_bar.position = (strcmp(position, "top") == 0 ? P_TOP : P_BOTTOM);
|
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.
|
* 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);
|
SLIST_HEAD(variables_head, Variable) variables = SLIST_HEAD_INITIALIZER(&variables);
|
||||||
int fd, ret, read_bytes = 0;
|
int fd, ret, read_bytes = 0;
|
||||||
struct stat stbuf;
|
struct stat stbuf;
|
||||||
@ -917,7 +917,7 @@ void parse_file(const char *f) {
|
|||||||
* variables (otherwise we will count them twice, which is bad when
|
* variables (otherwise we will count them twice, which is bad when
|
||||||
* 'extra' is negative) */
|
* 'extra' is negative) */
|
||||||
char *bufcopy = sstrdup(buf);
|
char *bufcopy = sstrdup(buf);
|
||||||
SLIST_FOREACH (current, &variables, variables) {
|
SLIST_FOREACH(current, &variables, variables) {
|
||||||
int extra = (strlen(current->value) - strlen(current->key));
|
int extra = (strlen(current->value) - strlen(current->key));
|
||||||
char *next;
|
char *next;
|
||||||
for (next = bufcopy;
|
for (next = bufcopy;
|
||||||
@ -937,11 +937,11 @@ void parse_file(const char *f) {
|
|||||||
destwalk = new;
|
destwalk = new;
|
||||||
while (walk < (buf + stbuf.st_size)) {
|
while (walk < (buf + stbuf.st_size)) {
|
||||||
/* Find the next variable */
|
/* Find the next variable */
|
||||||
SLIST_FOREACH (current, &variables, variables)
|
SLIST_FOREACH(current, &variables, variables)
|
||||||
current->next_match = strcasestr(walk, current->key);
|
current->next_match = strcasestr(walk, current->key);
|
||||||
nearest = NULL;
|
nearest = NULL;
|
||||||
int distance = stbuf.st_size;
|
int distance = stbuf.st_size;
|
||||||
SLIST_FOREACH (current, &variables, variables) {
|
SLIST_FOREACH(current, &variables, variables) {
|
||||||
if (current->next_match == NULL)
|
if (current->next_match == NULL)
|
||||||
continue;
|
continue;
|
||||||
if ((current->next_match - walk) < distance) {
|
if ((current->next_match - walk) < distance) {
|
||||||
@ -1000,7 +1000,7 @@ void parse_file(const char *f) {
|
|||||||
|
|
||||||
check_for_duplicate_bindings(context);
|
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");
|
ELOG("FYI: You are using i3 version " I3_VERSION "\n");
|
||||||
if (version == 3)
|
if (version == 3)
|
||||||
ELOG("Please convert your configfile first, then fix any remaining errors (see above).\n");
|
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);
|
free(pageraction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool has_errors = context->has_errors;
|
||||||
|
|
||||||
FREE(context->line_copy);
|
FREE(context->line_copy);
|
||||||
free(context);
|
free(context);
|
||||||
free(new);
|
free(new);
|
||||||
@ -1042,6 +1044,8 @@ void parse_file(const char *f) {
|
|||||||
SLIST_REMOVE_HEAD(&variables, variables);
|
SLIST_REMOVE_HEAD(&variables, variables);
|
||||||
FREE(current);
|
FREE(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return !has_errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
103
src/ewmh.c
103
src/ewmh.c
@ -24,9 +24,9 @@ void ewmh_update_current_desktop(void) {
|
|||||||
uint32_t idx = 0;
|
uint32_t idx = 0;
|
||||||
/* We count to get the index of this workspace because named workspaces
|
/* We count to get the index of this workspace because named workspaces
|
||||||
* don’t have the ->num property */
|
* don’t have the ->num property */
|
||||||
TAILQ_FOREACH (output, &(croot->nodes_head), nodes) {
|
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
||||||
Con *ws;
|
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, "__"))
|
if (STARTS_WITH(ws->name, "__"))
|
||||||
continue;
|
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.
|
* 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. */
|
/* 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_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) {
|
static Output *get_screen_at(unsigned int x, unsigned int y) {
|
||||||
Output *output;
|
Output *output;
|
||||||
TAILQ_FOREACH (output, &outputs, outputs)
|
TAILQ_FOREACH(output, &outputs, outputs)
|
||||||
if (output->rect.x == x && output->rect.y == y)
|
if (output->rect.x == x && output->rect.y == y)
|
||||||
return output;
|
return output;
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ static Rect total_outputs_dimensions(void) {
|
|||||||
Output *output;
|
Output *output;
|
||||||
/* Use Rect to encapsulate dimensions, ignoring x/y */
|
/* Use Rect to encapsulate dimensions, ignoring x/y */
|
||||||
Rect outputs_dimensions = {0, 0, 0, 0};
|
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.height += output->rect.height;
|
||||||
outputs_dimensions.width += output->rect.width;
|
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) {
|
if (memcmp(&(nc->rect), &zero, sizeof(Rect)) == 0) {
|
||||||
DLOG("Geometry not set, combining children\n");
|
DLOG("Geometry not set, combining children\n");
|
||||||
Con *child;
|
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);
|
DLOG("child geometry: %d x %d\n", child->geometry.width, child->geometry.height);
|
||||||
nc->rect.width += child->geometry.width;
|
nc->rect.width += child->geometry.width;
|
||||||
nc->rect.height = max(nc->rect.height, child->geometry.height);
|
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
|
/* Check if we need to re-assign it to a different workspace because of its
|
||||||
* coordinates and exit if that was done successfully. */
|
* 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;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Sanitize coordinates: Check if they are on any output */
|
/* 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;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ELOG("No output found at destination coordinates, centering floating window on current ws\n");
|
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.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);
|
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) {
|
void floating_disable(Con *con, bool automatic) {
|
||||||
@ -351,6 +357,8 @@ void floating_disable(Con *con, bool automatic) {
|
|||||||
|
|
||||||
if (set_focus)
|
if (set_focus)
|
||||||
con_focus(con);
|
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>
|
#include <libsn/sn-monitor.h>
|
||||||
|
|
||||||
int randr_base = -1;
|
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,
|
/* 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
|
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);
|
event = SLIST_NEXT(event, ignore_events);
|
||||||
}
|
}
|
||||||
|
|
||||||
SLIST_FOREACH (event, &ignore_events, ignore_events) {
|
SLIST_FOREACH(event, &ignore_events, ignore_events) {
|
||||||
if (event->sequence != sequence)
|
if (event->sequence != sequence)
|
||||||
continue;
|
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);
|
layout_t layout = (enter_child ? con->parent->layout : con->layout);
|
||||||
if (layout == L_DEFAULT) {
|
if (layout == L_DEFAULT) {
|
||||||
Con *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)) {
|
if (rect_contains(child->deco_rect, event->event_x, event->event_y)) {
|
||||||
LOG("using child %p / %s instead!\n", child, child->name);
|
LOG("using child %p / %s instead!\n", child, child->name);
|
||||||
con = child;
|
con = child;
|
||||||
@ -231,7 +233,7 @@ static void handle_motion_notify(xcb_motion_notify_event_t *event) {
|
|||||||
|
|
||||||
/* see over which rect the user is */
|
/* see over which rect the user is */
|
||||||
Con *current;
|
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))
|
if (!rect_contains(current->deco_rect, event->event_x, event->event_y))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -282,7 +284,6 @@ static void handle_map_request(xcb_map_request_event_t *event) {
|
|||||||
add_ignore_event(event->sequence, -1);
|
add_ignore_event(event->sequence, -1);
|
||||||
|
|
||||||
manage_window(event->window, cookie, false);
|
manage_window(event->window, cookie, false);
|
||||||
x_push_changes(croot);
|
|
||||||
return;
|
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_close(con, DONT_KILL_WINDOW, false, false);
|
||||||
tree_render();
|
tree_render();
|
||||||
x_push_changes(croot);
|
|
||||||
|
|
||||||
ignore_end:
|
ignore_end:
|
||||||
/* If the client (as opposed to i3) destroyed or unmapped a window, an
|
/* 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;
|
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)
|
* Handle client messages (EWMH)
|
||||||
*
|
*
|
||||||
@ -791,6 +804,98 @@ static void handle_client_message(xcb_client_message_event_t *event) {
|
|||||||
XCB_ATOM_CARDINAL, 32, 4,
|
XCB_ATOM_CARDINAL, 32, 4,
|
||||||
&r);
|
&r);
|
||||||
xcb_flush(conn);
|
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 {
|
} else {
|
||||||
DLOG("unhandled clientmessage\n");
|
DLOG("unhandled clientmessage\n");
|
||||||
return;
|
return;
|
||||||
@ -1045,6 +1150,30 @@ static void handle_focus_in(xcb_focus_in_event_t *event) {
|
|||||||
return;
|
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
|
/* Returns false if the event could not be processed (e.g. the window could not
|
||||||
* be found), true otherwise */
|
* 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);
|
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_normal_hints},
|
||||||
{0, UINT_MAX, handle_clientleader_change},
|
{0, UINT_MAX, handle_clientleader_change},
|
||||||
{0, UINT_MAX, handle_transient_for},
|
{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))
|
#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[4].atom = A_WM_CLIENT_LEADER;
|
||||||
property_handlers[5].atom = XCB_ATOM_WM_TRANSIENT_FOR;
|
property_handlers[5].atom = XCB_ATOM_WM_TRANSIENT_FOR;
|
||||||
property_handlers[6].atom = A_WM_WINDOW_ROLE;
|
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) {
|
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) {
|
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 &&
|
if (randr_base > -1 &&
|
||||||
type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
|
type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
|
||||||
handle_screen_change(event);
|
handle_screen_change(event);
|
||||||
return;
|
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) {
|
switch (type) {
|
||||||
case XCB_KEY_PRESS:
|
case XCB_KEY_PRESS:
|
||||||
case XCB_KEY_RELEASE:
|
case XCB_KEY_RELEASE:
|
||||||
@ -1128,6 +1297,7 @@ void handle_event(int type, xcb_generic_event_t *event) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case XCB_BUTTON_PRESS:
|
case XCB_BUTTON_PRESS:
|
||||||
|
case XCB_BUTTON_RELEASE:
|
||||||
handle_button_press((xcb_button_press_event_t *)event);
|
handle_button_press((xcb_button_press_event_t *)event);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -5,8 +5,8 @@ CLEAN_TARGETS += clean-i3
|
|||||||
i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c))
|
i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c))
|
||||||
i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h)
|
i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h)
|
||||||
i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.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_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 = $(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_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
|
# When using clang, we use pre-compiled headers to speed up the build. With
|
||||||
# gcc, this actually makes the build slower.
|
# 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) {
|
void ipc_send_event(const char *event, uint32_t message_type, const char *payload) {
|
||||||
ipc_client *current;
|
ipc_client *current;
|
||||||
TAILQ_FOREACH (current, &all_clients, clients) {
|
TAILQ_FOREACH(current, &all_clients, clients) {
|
||||||
/* see if this client is interested in this event */
|
/* see if this client is interested in this event */
|
||||||
bool interested = false;
|
bool interested = false;
|
||||||
for (int i = 0; i < current->num_events; i++) {
|
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);
|
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) {
|
void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
|
||||||
y(map_open);
|
y(map_open);
|
||||||
ystr("id");
|
ystr("id");
|
||||||
@ -293,14 +347,17 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
|
|||||||
y(integer, con->current_border_width);
|
y(integer, con->current_border_width);
|
||||||
|
|
||||||
dump_rect(gen, "rect", con->rect);
|
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, "window_rect", con->window_rect);
|
||||||
dump_rect(gen, "geometry", con->geometry);
|
dump_rect(gen, "geometry", con->geometry);
|
||||||
|
|
||||||
ystr("name");
|
ystr("name");
|
||||||
if (con->window && con->window->name)
|
if (con->window && con->window->name)
|
||||||
ystr(i3string_as_utf8(con->window->name));
|
ystr(i3string_as_utf8(con->window->name));
|
||||||
else
|
else if (con->name != NULL)
|
||||||
ystr(con->name);
|
ystr(con->name);
|
||||||
|
else
|
||||||
|
y(null);
|
||||||
|
|
||||||
if (con->type == CT_WORKSPACE) {
|
if (con->type == CT_WORKSPACE) {
|
||||||
ystr("num");
|
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(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);
|
y(map_close);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,7 +406,7 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
|
|||||||
y(array_open);
|
y(array_open);
|
||||||
Con *node;
|
Con *node;
|
||||||
if (con->type != CT_DOCKAREA || !inplace_restart) {
|
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);
|
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");
|
ystr("floating_nodes");
|
||||||
y(array_open);
|
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);
|
dump_node(gen, node, inplace_restart);
|
||||||
}
|
}
|
||||||
y(array_close);
|
y(array_close);
|
||||||
|
|
||||||
ystr("focus");
|
ystr("focus");
|
||||||
y(array_open);
|
y(array_open);
|
||||||
TAILQ_FOREACH (node, &(con->focus_head), focused) {
|
TAILQ_FOREACH(node, &(con->focus_head), focused) {
|
||||||
y(integer, (long int)node);
|
y(integer, (long int)node);
|
||||||
}
|
}
|
||||||
y(array_close);
|
y(array_close);
|
||||||
@ -386,7 +448,7 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
|
|||||||
ystr("swallows");
|
ystr("swallows");
|
||||||
y(array_open);
|
y(array_open);
|
||||||
Match *match;
|
Match *match;
|
||||||
TAILQ_FOREACH (match, &(con->swallow_head), matches) {
|
TAILQ_FOREACH(match, &(con->swallow_head), matches) {
|
||||||
y(map_open);
|
y(map_open);
|
||||||
if (match->dock != -1) {
|
if (match->dock != -1) {
|
||||||
ystr("dock");
|
ystr("dock");
|
||||||
@ -512,6 +574,16 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
|
|||||||
break;
|
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");
|
ystr("position");
|
||||||
if (config->position == P_BOTTOM)
|
if (config->position == P_BOTTOM)
|
||||||
ystr("bottom");
|
ystr("bottom");
|
||||||
@ -591,18 +663,15 @@ IPC_HANDLER(get_workspaces) {
|
|||||||
Con *focused_ws = con_get_workspace(focused);
|
Con *focused_ws = con_get_workspace(focused);
|
||||||
|
|
||||||
Con *output;
|
Con *output;
|
||||||
TAILQ_FOREACH (output, &(croot->nodes_head), nodes) {
|
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
||||||
if (con_is_internal(output))
|
if (con_is_internal(output))
|
||||||
continue;
|
continue;
|
||||||
Con *ws;
|
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);
|
assert(ws->type == CT_WORKSPACE);
|
||||||
y(map_open);
|
y(map_open);
|
||||||
|
|
||||||
ystr("num");
|
ystr("num");
|
||||||
if (ws->num == -1)
|
|
||||||
y(null);
|
|
||||||
else
|
|
||||||
y(integer, ws->num);
|
y(integer, ws->num);
|
||||||
|
|
||||||
ystr("name");
|
ystr("name");
|
||||||
@ -656,7 +725,7 @@ IPC_HANDLER(get_outputs) {
|
|||||||
y(array_open);
|
y(array_open);
|
||||||
|
|
||||||
Output *output;
|
Output *output;
|
||||||
TAILQ_FOREACH (output, &outputs, outputs) {
|
TAILQ_FOREACH(output, &outputs, outputs) {
|
||||||
y(map_open);
|
y(map_open);
|
||||||
|
|
||||||
ystr("name");
|
ystr("name");
|
||||||
@ -710,7 +779,7 @@ IPC_HANDLER(get_marks) {
|
|||||||
y(array_open);
|
y(array_open);
|
||||||
|
|
||||||
Con *con;
|
Con *con;
|
||||||
TAILQ_FOREACH (con, &all_cons, all_cons)
|
TAILQ_FOREACH(con, &all_cons, all_cons)
|
||||||
if (con->mark != NULL)
|
if (con->mark != NULL)
|
||||||
ystr(con->mark);
|
ystr(con->mark);
|
||||||
|
|
||||||
@ -766,7 +835,7 @@ IPC_HANDLER(get_bar_config) {
|
|||||||
if (message_size == 0) {
|
if (message_size == 0) {
|
||||||
y(array_open);
|
y(array_open);
|
||||||
Barconfig *current;
|
Barconfig *current;
|
||||||
TAILQ_FOREACH (current, &barconfigs, configs) {
|
TAILQ_FOREACH(current, &barconfigs, configs) {
|
||||||
ystr(current->id);
|
ystr(current->id);
|
||||||
}
|
}
|
||||||
y(array_close);
|
y(array_close);
|
||||||
@ -786,7 +855,7 @@ IPC_HANDLER(get_bar_config) {
|
|||||||
strncpy(bar_id, (const char *)message, message_size);
|
strncpy(bar_id, (const char *)message, message_size);
|
||||||
LOG("IPC: looking for config for bar ID \"%s\"\n", bar_id);
|
LOG("IPC: looking for config for bar ID \"%s\"\n", bar_id);
|
||||||
Barconfig *current, *config = NULL;
|
Barconfig *current, *config = NULL;
|
||||||
TAILQ_FOREACH (current, &barconfigs, configs) {
|
TAILQ_FOREACH(current, &barconfigs, configs) {
|
||||||
if (strcmp(current->id, bar_id) != 0)
|
if (strcmp(current->id, bar_id) != 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -852,7 +921,7 @@ IPC_HANDLER(subscribe) {
|
|||||||
ipc_client *current, *client = NULL;
|
ipc_client *current, *client = NULL;
|
||||||
|
|
||||||
/* Search the ipc_client structure for this connection */
|
/* Search the ipc_client structure for this connection */
|
||||||
TAILQ_FOREACH (current, &all_clients, clients) {
|
TAILQ_FOREACH(current, &all_clients, clients) {
|
||||||
if (current->fd != fd)
|
if (current->fd != fd)
|
||||||
continue;
|
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 */
|
/* Delete the client from the list of clients */
|
||||||
ipc_client *current;
|
ipc_client *current;
|
||||||
TAILQ_FOREACH (current, &all_clients, clients) {
|
TAILQ_FOREACH(current, &all_clients, clients) {
|
||||||
if (current->fd != w->fd)
|
if (current->fd != w->fd)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -1051,20 +1120,22 @@ int ipc_create_socket(const char *filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For the workspace "focus" event we send, along the usual "change" field,
|
* Generates a json workspace event. Returns a dynamically allocated yajl
|
||||||
* also the current and previous workspace, in "current" and "old"
|
* generator. Free with yajl_gen_free().
|
||||||
* respectively.
|
|
||||||
*/
|
*/
|
||||||
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");
|
setlocale(LC_NUMERIC, "C");
|
||||||
yajl_gen gen = ygenalloc();
|
yajl_gen gen = ygenalloc();
|
||||||
|
|
||||||
y(map_open);
|
y(map_open);
|
||||||
|
|
||||||
ystr("change");
|
ystr("change");
|
||||||
ystr("focus");
|
ystr(change);
|
||||||
|
|
||||||
ystr("current");
|
ystr("current");
|
||||||
|
if (current == NULL)
|
||||||
|
y(null);
|
||||||
|
else
|
||||||
dump_node(gen, current, false);
|
dump_node(gen, current, false);
|
||||||
|
|
||||||
ystr("old");
|
ystr("old");
|
||||||
@ -1075,13 +1146,26 @@ void ipc_send_workspace_focus_event(Con *current, Con *old) {
|
|||||||
|
|
||||||
y(map_close);
|
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;
|
const unsigned char *payload;
|
||||||
ylength length;
|
ylength length;
|
||||||
y(get_buf, &payload, &length);
|
y(get_buf, &payload, &length);
|
||||||
|
|
||||||
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
|
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
|
||||||
|
|
||||||
y(free);
|
y(free);
|
||||||
setlocale(LC_NUMERIC, "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1132,3 +1216,33 @@ void ipc_send_barconfig_update_event(Barconfig *barconfig) {
|
|||||||
y(free);
|
y(free);
|
||||||
setlocale(LC_NUMERIC, "");
|
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)
|
if (bind == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CommandResult *result = run_binding(bind);
|
CommandResult *result = run_binding(bind, NULL);
|
||||||
|
|
||||||
if (result->needs_tree_render)
|
if (result->needs_tree_render)
|
||||||
tree_render();
|
tree_render();
|
||||||
|
@ -24,6 +24,7 @@ static Con *json_node;
|
|||||||
static Con *to_focus;
|
static Con *to_focus;
|
||||||
static bool parsing_swallows;
|
static bool parsing_swallows;
|
||||||
static bool parsing_rect;
|
static bool parsing_rect;
|
||||||
|
static bool parsing_deco_rect;
|
||||||
static bool parsing_window_rect;
|
static bool parsing_window_rect;
|
||||||
static bool parsing_geometry;
|
static bool parsing_geometry;
|
||||||
static bool parsing_focus;
|
static bool parsing_focus;
|
||||||
@ -47,7 +48,7 @@ static int json_start_map(void *ctx) {
|
|||||||
match_init(current_swallow);
|
match_init(current_swallow);
|
||||||
TAILQ_INSERT_TAIL(&(json_node->swallow_head), current_swallow, matches);
|
TAILQ_INSERT_TAIL(&(json_node->swallow_head), current_swallow, matches);
|
||||||
} else {
|
} 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) {
|
if (last_key && strcasecmp(last_key, "floating_nodes") == 0) {
|
||||||
DLOG("New floating_node\n");
|
DLOG("New floating_node\n");
|
||||||
Con *ws = con_get_workspace(json_node);
|
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) {
|
static int json_end_map(void *ctx) {
|
||||||
LOG("end of map\n");
|
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. */
|
/* Set a few default values to simplify manually crafted layout files. */
|
||||||
if (json_node->layout == L_DEFAULT) {
|
if (json_node->layout == L_DEFAULT) {
|
||||||
DLOG("Setting layout = L_SPLITH\n");
|
DLOG("Setting layout = L_SPLITH\n");
|
||||||
@ -98,7 +99,7 @@ static int json_end_map(void *ctx) {
|
|||||||
* workspace called “1”. */
|
* workspace called “1”. */
|
||||||
Con *output;
|
Con *output;
|
||||||
Con *workspace = NULL;
|
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));
|
GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, json_node->name));
|
||||||
char *base = sstrdup(json_node->name);
|
char *base = sstrdup(json_node->name);
|
||||||
int cnt = 1;
|
int cnt = 1;
|
||||||
@ -106,18 +107,13 @@ static int json_end_map(void *ctx) {
|
|||||||
FREE(json_node->name);
|
FREE(json_node->name);
|
||||||
asprintf(&(json_node->name), "%s_%d", base, cnt++);
|
asprintf(&(json_node->name), "%s_%d", base, cnt++);
|
||||||
workspace = NULL;
|
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));
|
GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, json_node->name));
|
||||||
}
|
}
|
||||||
free(base);
|
free(base);
|
||||||
|
|
||||||
/* Set num accordingly so that i3bar will properly sort it. */
|
/* Set num accordingly so that i3bar will properly sort it. */
|
||||||
json_node->num = ws_name_to_number(json_node->name);
|
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");
|
LOG("attaching\n");
|
||||||
@ -126,11 +122,10 @@ static int json_end_map(void *ctx) {
|
|||||||
x_con_init(json_node, json_node->depth);
|
x_con_init(json_node, json_node->depth);
|
||||||
json_node = json_node->parent;
|
json_node = json_node->parent;
|
||||||
}
|
}
|
||||||
if (parsing_rect)
|
|
||||||
parsing_rect = false;
|
parsing_rect = false;
|
||||||
if (parsing_window_rect)
|
parsing_deco_rect = false;
|
||||||
parsing_window_rect = false;
|
parsing_window_rect = false;
|
||||||
if (parsing_geometry)
|
|
||||||
parsing_geometry = false;
|
parsing_geometry = false;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -146,10 +141,10 @@ static int json_end_array(void *ctx) {
|
|||||||
if (parsing_focus) {
|
if (parsing_focus) {
|
||||||
/* Clear the list of focus mappings */
|
/* Clear the list of focus mappings */
|
||||||
struct focus_mapping *mapping;
|
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);
|
LOG("focus (reverse) %d\n", mapping->old_id);
|
||||||
Con *con;
|
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)
|
if (con->old_id != mapping->old_id)
|
||||||
continue;
|
continue;
|
||||||
LOG("got it! %p\n", con);
|
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)
|
if (strcasecmp(last_key, "rect") == 0)
|
||||||
parsing_rect = true;
|
parsing_rect = true;
|
||||||
|
|
||||||
|
if (strcasecmp(last_key, "deco_rect") == 0)
|
||||||
|
parsing_deco_rect = true;
|
||||||
|
|
||||||
if (strcasecmp(last_key, "window_rect") == 0)
|
if (strcasecmp(last_key, "window_rect") == 0)
|
||||||
parsing_window_rect = true;
|
parsing_window_rect = true;
|
||||||
|
|
||||||
@ -553,6 +551,7 @@ void tree_append_json(Con *con, const char *filename, char **errormsg) {
|
|||||||
to_focus = NULL;
|
to_focus = NULL;
|
||||||
parsing_swallows = false;
|
parsing_swallows = false;
|
||||||
parsing_rect = false;
|
parsing_rect = false;
|
||||||
|
parsing_deco_rect = false;
|
||||||
parsing_window_rect = false;
|
parsing_window_rect = false;
|
||||||
parsing_geometry = false;
|
parsing_geometry = false;
|
||||||
parsing_focus = false;
|
parsing_focus = false;
|
||||||
|
162
src/main.c
162
src/main.c
@ -36,10 +36,6 @@ int listen_fds;
|
|||||||
* temporarily for drag_pointer(). */
|
* temporarily for drag_pointer(). */
|
||||||
static struct ev_check *xcb_check;
|
static struct ev_check *xcb_check;
|
||||||
|
|
||||||
static int xkb_event_base;
|
|
||||||
|
|
||||||
int xkb_current_group;
|
|
||||||
|
|
||||||
extern Con *focused;
|
extern Con *focused;
|
||||||
|
|
||||||
char **start_argv;
|
char **start_argv;
|
||||||
@ -70,9 +66,6 @@ struct ev_loop *main_loop;
|
|||||||
|
|
||||||
xcb_key_symbols_t *keysyms;
|
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. */
|
/* Default shmlog size if not set by user. */
|
||||||
const int default_shmlog_size = 25 * 1024 * 1024;
|
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 */
|
/* We hope that those are supported and set them to true */
|
||||||
bool xcursor_supported = 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.
|
* 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.
|
* 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;
|
bool force_xinerama = false;
|
||||||
char *fake_outputs = NULL;
|
char *fake_outputs = NULL;
|
||||||
bool disable_signalhandler = false;
|
bool disable_signalhandler = false;
|
||||||
|
bool only_check_config = false;
|
||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
{"no-autostart", no_argument, 0, 'a'},
|
{"no-autostart", no_argument, 0, 'a'},
|
||||||
{"config", required_argument, 0, 'c'},
|
{"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
|
/* 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
|
* the arguments as an IPC message to i3. This allows for nice semantic
|
||||||
* commands such as 'i3 border none'. */
|
* 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.
|
/* 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
|
* This should make it easier to find mistakes when the user passes
|
||||||
* arguments by mistake. */
|
* arguments by mistake. */
|
||||||
@ -567,10 +492,6 @@ int main(int argc, char *argv[]) {
|
|||||||
xcb_query_pointer_cookie_t pointercookie = xcb_query_pointer(conn, root);
|
xcb_query_pointer_cookie_t pointercookie = xcb_query_pointer(conn, root);
|
||||||
|
|
||||||
load_configuration(conn, override_configpath, false);
|
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) {
|
if (config.ipc_socket_path == NULL) {
|
||||||
/* Fall back to a file name in /tmp/ based on the PID */
|
/* 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"
|
#include "atoms.xmacro"
|
||||||
#undef 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();
|
xcursor_load_cursors();
|
||||||
/*init_xkb();*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set a cursor for the root window (otherwise the root window will show no
|
/* Set a cursor for the root window (otherwise the root window will show no
|
||||||
cursor until the first client is launched). */
|
cursor until the first client is launched). */
|
||||||
@ -620,27 +527,22 @@ int main(int argc, char *argv[]) {
|
|||||||
else
|
else
|
||||||
xcb_set_root_cursor(XCURSOR_CURSOR_POINTER);
|
xcb_set_root_cursor(XCURSOR_CURSOR_POINTER);
|
||||||
|
|
||||||
if (xkb_supported) {
|
const xcb_query_extension_reply_t *extreply;
|
||||||
int errBase,
|
extreply = xcb_get_extension_data(conn, &xcb_xkb_id);
|
||||||
major = XkbMajorVersion,
|
if (!extreply->present) {
|
||||||
minor = XkbMinorVersion;
|
DLOG("xkb is not present on this server\n");
|
||||||
|
} else {
|
||||||
if (fcntl(ConnectionNumber(xkbdpy), F_SETFD, FD_CLOEXEC) == -1) {
|
DLOG("initializing xcb-xkb\n");
|
||||||
fprintf(stderr, "Could not set FD_CLOEXEC on xkbdpy\n");
|
xcb_xkb_use_extension(conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION);
|
||||||
return 1;
|
xcb_xkb_select_events(conn,
|
||||||
}
|
XCB_XKB_ID_USE_CORE_KBD,
|
||||||
|
XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY,
|
||||||
int i1;
|
0,
|
||||||
if (!XkbQueryExtension(xkbdpy, &i1, &xkb_event_base, &errBase, &major, &minor)) {
|
XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY,
|
||||||
fprintf(stderr, "XKB not supported by X-server\n");
|
0xff,
|
||||||
xkb_supported = false;
|
0xff,
|
||||||
}
|
NULL);
|
||||||
/* end of ugliness */
|
xkb_base = extreply->first_event;
|
||||||
|
|
||||||
if (xkb_supported && !XkbSelectEvents(xkbdpy, XkbUseCoreKbd, XkbMapNotifyMask | XkbStateNotifyMask, XkbMapNotifyMask | XkbStateNotifyMask)) {
|
|
||||||
fprintf(stderr, "Could not set XKB event mask\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
restore_connect();
|
restore_connect();
|
||||||
@ -769,25 +671,19 @@ int main(int argc, char *argv[]) {
|
|||||||
x_set_i3_atoms();
|
x_set_i3_atoms();
|
||||||
ewmh_update_workarea();
|
ewmh_update_workarea();
|
||||||
|
|
||||||
/* Set the _NET_CURRENT_DESKTOP property. */
|
/* Set the ewmh desktop properties. */
|
||||||
ewmh_update_current_desktop();
|
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 *xcb_watcher = scalloc(sizeof(struct ev_io));
|
||||||
struct ev_io *xkb = scalloc(sizeof(struct ev_io));
|
|
||||||
xcb_check = scalloc(sizeof(struct ev_check));
|
xcb_check = scalloc(sizeof(struct ev_check));
|
||||||
struct ev_prepare *xcb_prepare = scalloc(sizeof(struct ev_prepare));
|
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_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ);
|
||||||
ev_io_start(main_loop, xcb_watcher);
|
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_init(xcb_check, xcb_check_cb);
|
||||||
ev_check_start(main_loop, xcb_check);
|
ev_check_start(main_loop, xcb_check);
|
||||||
|
|
||||||
@ -888,7 +784,7 @@ int main(int argc, char *argv[]) {
|
|||||||
/* Autostarting exec-lines */
|
/* Autostarting exec-lines */
|
||||||
if (autostart) {
|
if (autostart) {
|
||||||
struct Autostart *exec;
|
struct Autostart *exec;
|
||||||
TAILQ_FOREACH (exec, &autostarts, autostarts) {
|
TAILQ_FOREACH(exec, &autostarts, autostarts) {
|
||||||
LOG("auto-starting %s\n", exec->command);
|
LOG("auto-starting %s\n", exec->command);
|
||||||
start_application(exec->command, exec->no_startup_id);
|
start_application(exec->command, exec->no_startup_id);
|
||||||
}
|
}
|
||||||
@ -896,14 +792,14 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
/* Autostarting exec_always-lines */
|
/* Autostarting exec_always-lines */
|
||||||
struct Autostart *exec_always;
|
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);
|
LOG("auto-starting (always!) %s\n", exec_always->command);
|
||||||
start_application(exec_always->command, exec_always->no_startup_id);
|
start_application(exec_always->command, exec_always->no_startup_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Start i3bar processes for all configured bars */
|
/* Start i3bar processes for all configured bars */
|
||||||
Barconfig *barconfig;
|
Barconfig *barconfig;
|
||||||
TAILQ_FOREACH (barconfig, &barconfigs, configs) {
|
TAILQ_FOREACH(barconfig, &barconfigs, configs) {
|
||||||
char *command = NULL;
|
char *command = NULL;
|
||||||
sasprintf(&command, "%s --bar_id=%s --socket=\"%s\"",
|
sasprintf(&command, "%s --bar_id=%s --socket=\"%s\"",
|
||||||
barconfig->i3bar_command ? barconfig->i3bar_command : "i3bar",
|
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");
|
DLOG("Restoring geometry\n");
|
||||||
|
|
||||||
Con *con;
|
Con *con;
|
||||||
TAILQ_FOREACH (con, &all_cons, all_cons)
|
TAILQ_FOREACH(con, &all_cons, all_cons)
|
||||||
if (con->window) {
|
if (con->window) {
|
||||||
DLOG("Re-adding X11 border of %d px\n", con->border_width);
|
DLOG("Re-adding X11 border of %d px\n", con->border_width);
|
||||||
con->window_rect.width += (2 * 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)
|
if (nc->geometry.width == 0)
|
||||||
nc->geometry = (Rect) {geom->x, geom->y, geom->width, geom->height};
|
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) {
|
if (motif_border_style != BS_NORMAL) {
|
||||||
DLOG("MOTIF_WM_HINTS specifies decorations (border_style = %d)\n", motif_border_style);
|
DLOG("MOTIF_WM_HINTS specifies decorations (border_style = %d)\n", motif_border_style);
|
||||||
if (want_floating) {
|
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 (want_floating) {
|
||||||
/* if the border style is BS_PIXEL, explicitly set the border width of
|
DLOG("geometry = %d x %d\n", nc->geometry.width, nc->geometry.height);
|
||||||
* the new container */
|
/* automatically set the border to the default value if a motif border
|
||||||
nc->current_border_width = (want_floating ? config.default_floating_border_width : config.default_border_width);
|
* 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
|
/* to avoid getting an UnmapNotify event due to reparenting, we temporarily
|
||||||
* declare no interest in any state change event of this window */
|
* declare no interest in any state change event of this window */
|
||||||
values[0] = XCB_NONE;
|
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
|
/* Defer setting focus after the 'new' event has been sent to ensure the
|
||||||
* proper window event sequence. */
|
* proper window event sequence. */
|
||||||
if (set_focus && nc->mapped) {
|
if (set_focus && !nc->window->doesnt_accept_focus && nc->mapped) {
|
||||||
DLOG("Now setting focus.\n");
|
DLOG("Now setting focus.\n");
|
||||||
con_focus(nc);
|
con_focus(nc);
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,7 @@ bool match_matches_window(Match *match, i3Window *window) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
/* if we find a window that is newer than this one, bail */
|
/* 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) &&
|
if ((con->window != NULL) &&
|
||||||
_i3_timercmp(con->window->urgent, window->urgent, > )) {
|
_i3_timercmp(con->window->urgent, window->urgent, > )) {
|
||||||
return false;
|
return false;
|
||||||
@ -151,7 +151,7 @@ bool match_matches_window(Match *match, i3Window *window) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
/* if we find a window that is older than this one (and not 0), bail */
|
/* 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) &&
|
if ((con->window != NULL) &&
|
||||||
(con->window->urgent.tv_sec != 0) &&
|
(con->window->urgent.tv_sec != 0) &&
|
||||||
_i3_timercmp(con->window->urgent, window->urgent, < )) {
|
_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);
|
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).
|
* D_UP, D_DOWN).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void tree_move(int direction) {
|
void tree_move(Con *con, int direction) {
|
||||||
position_t position;
|
position_t position;
|
||||||
Con *target;
|
Con *target;
|
||||||
|
|
||||||
DLOG("Moving in direction %d\n", direction);
|
DLOG("Moving in direction %d\n", direction);
|
||||||
|
|
||||||
/* 1: get the first parent with the same orientation */
|
/* 1: get the first parent with the same orientation */
|
||||||
Con *con = focused;
|
|
||||||
|
|
||||||
if (con->type == CT_WORKSPACE) {
|
if (con->type == CT_WORKSPACE) {
|
||||||
DLOG("Not moving workspace\n");
|
DLOG("Not moving workspace\n");
|
||||||
@ -206,6 +205,7 @@ void tree_move(int direction) {
|
|||||||
TAILQ_INSERT_HEAD(&(swap->parent->focus_head), con, focused);
|
TAILQ_INSERT_HEAD(&(swap->parent->focus_head), con, focused);
|
||||||
|
|
||||||
DLOG("Swapped.\n");
|
DLOG("Swapped.\n");
|
||||||
|
ipc_send_window_event("move", con);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,6 +213,7 @@ void tree_move(int direction) {
|
|||||||
/* If we couldn't find a place to move it on this workspace,
|
/* If we couldn't find a place to move it on this workspace,
|
||||||
* try to move it to a workspace on a different output */
|
* try to move it to a workspace on a different output */
|
||||||
move_to_output_directed(con, direction);
|
move_to_output_directed(con, direction);
|
||||||
|
ipc_send_window_event("move", con);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,4 +265,5 @@ end:
|
|||||||
FREE(con->deco_render_params);
|
FREE(con->deco_render_params);
|
||||||
|
|
||||||
tree_flatten(croot);
|
tree_flatten(croot);
|
||||||
|
ipc_send_window_event("move", con);
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
Con *output_get_content(Con *output) {
|
Con *output_get_content(Con *output) {
|
||||||
Con *child;
|
Con *child;
|
||||||
|
|
||||||
TAILQ_FOREACH (child, &(output->nodes_head), nodes)
|
TAILQ_FOREACH(child, &(output->nodes_head), nodes)
|
||||||
if (child->type == CT_CON)
|
if (child->type == CT_CON)
|
||||||
return child;
|
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) {
|
static Output *get_output_by_id(xcb_randr_output_t id) {
|
||||||
Output *output;
|
Output *output;
|
||||||
TAILQ_FOREACH (output, &outputs, outputs)
|
TAILQ_FOREACH(output, &outputs, outputs)
|
||||||
if (output->id == id)
|
if (output->id == id)
|
||||||
return output;
|
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 *get_output_by_name(const char *name) {
|
||||||
Output *output;
|
Output *output;
|
||||||
TAILQ_FOREACH (output, &outputs, outputs)
|
TAILQ_FOREACH(output, &outputs, outputs)
|
||||||
if (output->active &&
|
if (output->active &&
|
||||||
strcasecmp(output->name, name) == 0)
|
strcasecmp(output->name, name) == 0)
|
||||||
return output;
|
return output;
|
||||||
@ -65,7 +65,7 @@ Output *get_output_by_name(const char *name) {
|
|||||||
Output *get_first_output(void) {
|
Output *get_first_output(void) {
|
||||||
Output *output;
|
Output *output;
|
||||||
|
|
||||||
TAILQ_FOREACH (output, &outputs, outputs)
|
TAILQ_FOREACH(output, &outputs, outputs)
|
||||||
if (output->active)
|
if (output->active)
|
||||||
return output;
|
return output;
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ Output *get_first_output(void) {
|
|||||||
*/
|
*/
|
||||||
Output *get_output_containing(unsigned int x, unsigned int y) {
|
Output *get_output_containing(unsigned int x, unsigned int y) {
|
||||||
Output *output;
|
Output *output;
|
||||||
TAILQ_FOREACH (output, &outputs, outputs) {
|
TAILQ_FOREACH(output, &outputs, outputs) {
|
||||||
if (!output->active)
|
if (!output->active)
|
||||||
continue;
|
continue;
|
||||||
DLOG("comparing x=%d y=%d with x=%d and y=%d width %d height %d\n",
|
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;
|
Output *output;
|
||||||
int lx = rect.x, uy = rect.y;
|
int lx = rect.x, uy = rect.y;
|
||||||
int rx = rect.x + rect.width, by = rect.y + rect.height;
|
int rx = rect.x + rect.width, by = rect.y + rect.height;
|
||||||
TAILQ_FOREACH (output, &outputs, outputs) {
|
TAILQ_FOREACH(output, &outputs, outputs) {
|
||||||
if (!output->active)
|
if (!output->active)
|
||||||
continue;
|
continue;
|
||||||
DLOG("comparing x=%d y=%d with x=%d and y=%d width %d height %d\n",
|
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;
|
*other;
|
||||||
Output *output,
|
Output *output,
|
||||||
*best = NULL;
|
*best = NULL;
|
||||||
TAILQ_FOREACH (output, &outputs, outputs) {
|
TAILQ_FOREACH(output, &outputs, outputs) {
|
||||||
if (!output->active)
|
if (!output->active)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -256,7 +256,7 @@ void output_init_con(Output *output) {
|
|||||||
|
|
||||||
/* Search for a Con with that name directly below the root node. There
|
/* Search for a Con with that name directly below the root node. There
|
||||||
* might be one from a restored layout. */
|
* 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)
|
if (strcmp(current->name, output->name) != 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -355,13 +355,13 @@ void output_init_con(Output *output) {
|
|||||||
void init_ws_for_output(Output *output, Con *content) {
|
void init_ws_for_output(Output *output, Con *content) {
|
||||||
/* go through all assignments and move the existing workspaces to this output */
|
/* go through all assignments and move the existing workspaces to this output */
|
||||||
struct Workspace_Assignment *assignment;
|
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)
|
if (strcmp(assignment->output, output->name) != 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* check if this workspace actually exists */
|
/* check if this workspace actually exists */
|
||||||
Con *workspace = NULL, *out;
|
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),
|
GREP_FIRST(workspace, output_get_content(out),
|
||||||
!strcasecmp(child->name, assignment->name));
|
!strcasecmp(child->name, assignment->name));
|
||||||
if (workspace == NULL)
|
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 *ws_out_content = output_get_content(workspace_out);
|
||||||
|
|
||||||
Con *floating_con;
|
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,
|
/* NB: We use output->con here because content is not yet rendered,
|
||||||
* so it has a rect of {0, 0, 0, 0}. */
|
* so it has a rect of {0, 0, 0, 0}. */
|
||||||
floating_fix_coordinates(floating_con, &(ws_out_content->rect), &(output->con->rect));
|
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 */
|
/* 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)
|
if (strcmp(assignment->output, output->name) != 0)
|
||||||
continue;
|
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.
|
/* Fix the position of all floating windows on this output.
|
||||||
* The 'rect' of each workspace will be updated in src/render.c. */
|
* The 'rect' of each workspace will be updated in src/render.c. */
|
||||||
TAILQ_FOREACH (workspace, &(content->nodes_head), nodes) {
|
TAILQ_FOREACH(workspace, &(content->nodes_head), nodes) {
|
||||||
TAILQ_FOREACH (child, &(workspace->floating_head), floating_windows) {
|
TAILQ_FOREACH(child, &(workspace->floating_head), floating_windows) {
|
||||||
floating_fix_coordinates(child, &(workspace->rect), &(output->con->rect));
|
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
|
* the workspaces and their childs depending on output resolution. This is
|
||||||
* only done for workspaces with maximum one child. */
|
* only done for workspaces with maximum one child. */
|
||||||
if (config.default_orientation == NO_ORIENTATION) {
|
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
|
/* Workspaces with more than one child are left untouched because
|
||||||
* we do not want to change an existing layout. */
|
* we do not want to change an existing layout. */
|
||||||
if (con_num_children(workspace) > 1)
|
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
|
/* Check for clones, disable the clones and reduce the mode to the
|
||||||
* lowest common mode */
|
* lowest common mode */
|
||||||
TAILQ_FOREACH (output, &outputs, outputs) {
|
TAILQ_FOREACH(output, &outputs, outputs) {
|
||||||
if (!output->active || output->to_be_disabled)
|
if (!output->active || output->to_be_disabled)
|
||||||
continue;
|
continue;
|
||||||
DLOG("output %p / %s, position (%d, %d), checking for clones\n",
|
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:
|
* 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 active, VGA1 gets activated as a clone of LVDS1 (has no con).
|
||||||
* LVDS1 gets disabled. */
|
* LVDS1 gets disabled. */
|
||||||
TAILQ_FOREACH (output, &outputs, outputs) {
|
TAILQ_FOREACH(output, &outputs, outputs) {
|
||||||
if (output->active && output->con == NULL) {
|
if (output->active && output->con == NULL) {
|
||||||
DLOG("Need to initialize a Con for output %s\n", output->name);
|
DLOG("Need to initialize a Con for output %s\n", output->name);
|
||||||
output_init_con(output);
|
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
|
/* Handle outputs which have a new mode or are disabled now (either
|
||||||
* because the user disabled them or because they are clones) */
|
* 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) {
|
if (output->to_be_disabled) {
|
||||||
output->active = false;
|
output->active = false;
|
||||||
DLOG("Output %s disabled, re-assigning workspaces/docks\n", output->name);
|
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);
|
con_attach(current, first_content, false);
|
||||||
DLOG("Fixing the coordinates of floating containers\n");
|
DLOG("Fixing the coordinates of floating containers\n");
|
||||||
Con *floating_con;
|
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));
|
floating_fix_coordinates(floating_con, &(output->con->rect), &(first->con->rect));
|
||||||
DLOG("Done, next\n");
|
DLOG("Done, next\n");
|
||||||
}
|
}
|
||||||
@ -745,7 +745,7 @@ void randr_query_outputs(void) {
|
|||||||
|
|
||||||
/* 3: move the dock clients to the first output */
|
/* 3: move the dock clients to the first output */
|
||||||
Con *child;
|
Con *child;
|
||||||
TAILQ_FOREACH (child, &(output->con->nodes_head), nodes) {
|
TAILQ_FOREACH(child, &(output->con->nodes_head), nodes) {
|
||||||
if (child->type != CT_DOCKAREA)
|
if (child->type != CT_DOCKAREA)
|
||||||
continue;
|
continue;
|
||||||
DLOG("Handling dock con %p\n", child);
|
DLOG("Handling dock con %p\n", child);
|
||||||
@ -788,7 +788,7 @@ void randr_query_outputs(void) {
|
|||||||
get_first_output();
|
get_first_output();
|
||||||
|
|
||||||
/* Just go through each active output and assign one workspace */
|
/* Just go through each active output and assign one workspace */
|
||||||
TAILQ_FOREACH (output, &outputs, outputs) {
|
TAILQ_FOREACH(output, &outputs, outputs) {
|
||||||
if (!output->active)
|
if (!output->active)
|
||||||
continue;
|
continue;
|
||||||
Con *content = output_get_content(output->con);
|
Con *content = output_get_content(output->con);
|
||||||
@ -799,7 +799,7 @@ void randr_query_outputs(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Focus the primary screen, if possible */
|
/* Focus the primary screen, if possible */
|
||||||
TAILQ_FOREACH (output, &outputs, outputs) {
|
TAILQ_FOREACH(output, &outputs, outputs) {
|
||||||
if (!output->primary || !output->con)
|
if (!output->primary || !output->con)
|
||||||
continue;
|
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
|
/* Find the content container and ensure that there is exactly one. Also
|
||||||
* check for any non-CT_DOCKAREA clients. */
|
* check for any non-CT_DOCKAREA clients. */
|
||||||
Con *content = NULL;
|
Con *content = NULL;
|
||||||
TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
|
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
||||||
if (child->type == CT_CON) {
|
if (child->type == CT_CON) {
|
||||||
if (content != NULL) {
|
if (content != NULL) {
|
||||||
DLOG("More than one CT_CON on output container\n");
|
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
|
/* 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 */
|
* 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)
|
if (child->type != CT_DOCKAREA)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
child->rect.height = 0;
|
child->rect.height = 0;
|
||||||
TAILQ_FOREACH (dockchild, &(child->nodes_head), nodes)
|
TAILQ_FOREACH(dockchild, &(child->nodes_head), nodes)
|
||||||
child->rect.height += dockchild->geometry.height;
|
child->rect.height += dockchild->geometry.height;
|
||||||
|
|
||||||
height -= child->rect.height;
|
height -= child->rect.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Second pass: Set the widths/heights */
|
/* 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) {
|
if (child->type == CT_CON) {
|
||||||
child->rect.x = x;
|
child->rect.x = x;
|
||||||
child->rect.y = y;
|
child->rect.y = y;
|
||||||
@ -232,7 +232,7 @@ void render_con(Con *con, bool render_fullscreen) {
|
|||||||
Con *child;
|
Con *child;
|
||||||
int i = 0, assigned = 0;
|
int i = 0, assigned = 0;
|
||||||
int total = con_orientation(con) == HORIZ ? rect.width : rect.height;
|
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;
|
double percentage = child->percent > 0.0 ? child->percent : 1.0 / children;
|
||||||
assigned += sizes[i++] = percentage * total;
|
assigned += sizes[i++] = percentage * total;
|
||||||
}
|
}
|
||||||
@ -256,7 +256,7 @@ void render_con(Con *con, bool render_fullscreen) {
|
|||||||
} else if (con->type == CT_ROOT) {
|
} else if (con->type == CT_ROOT) {
|
||||||
Con *output;
|
Con *output;
|
||||||
if (!fullscreen) {
|
if (!fullscreen) {
|
||||||
TAILQ_FOREACH (output, &(con->nodes_head), nodes) {
|
TAILQ_FOREACH(output, &(con->nodes_head), nodes) {
|
||||||
render_con(output, false);
|
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
|
* all times. This is important when the user places floating
|
||||||
* windows/containers so that they overlap on another output. */
|
* windows/containers so that they overlap on another output. */
|
||||||
DLOG("Rendering floating windows:\n");
|
DLOG("Rendering floating windows:\n");
|
||||||
TAILQ_FOREACH (output, &(con->nodes_head), nodes) {
|
TAILQ_FOREACH(output, &(con->nodes_head), nodes) {
|
||||||
if (con_is_internal(output))
|
if (con_is_internal(output))
|
||||||
continue;
|
continue;
|
||||||
/* Get the active workspace of that output */
|
/* 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 *workspace = TAILQ_FIRST(&(content->focus_head));
|
||||||
Con *fullscreen = con_get_fullscreen_con(workspace, CF_OUTPUT);
|
Con *fullscreen = con_get_fullscreen_con(workspace, CF_OUTPUT);
|
||||||
Con *child;
|
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
|
/* Don’t render floating windows when there is a fullscreen window
|
||||||
* on that workspace. Necessary to make floating fullscreen work
|
* on that workspace. Necessary to make floating fullscreen work
|
||||||
* correctly (ticket #564). */
|
* correctly (ticket #564). */
|
||||||
@ -298,6 +298,8 @@ void render_con(Con *con, bool render_fullscreen) {
|
|||||||
while (transient_con != NULL &&
|
while (transient_con != NULL &&
|
||||||
transient_con->window != NULL &&
|
transient_con->window != NULL &&
|
||||||
transient_con->window->transient_for != XCB_NONE) {
|
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) {
|
if (transient_con->window->transient_for == fullscreen->window->id) {
|
||||||
is_transient_for = true;
|
is_transient_for = true;
|
||||||
break;
|
break;
|
||||||
@ -331,7 +333,7 @@ void render_con(Con *con, bool render_fullscreen) {
|
|||||||
} else {
|
} else {
|
||||||
/* FIXME: refactor this into separate functions: */
|
/* FIXME: refactor this into separate functions: */
|
||||||
Con *child;
|
Con *child;
|
||||||
TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
|
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
||||||
assert(children > 0);
|
assert(children > 0);
|
||||||
|
|
||||||
/* default layout */
|
/* 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 */
|
/* in a stacking or tabbed container, we ensure the focused client is raised */
|
||||||
if (con->layout == L_STACKED || con->layout == L_TABBED) {
|
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);
|
x_raise_con(child);
|
||||||
if ((child = TAILQ_FIRST(&(con->focus_head)))) {
|
if ((child = TAILQ_FIRST(&(con->focus_head)))) {
|
||||||
/* By rendering the stacked container again, we handle the case
|
/* By rendering the stacked container again, we handle the case
|
||||||
|
@ -137,7 +137,7 @@ static void update_placeholder_contents(placeholder_state *state) {
|
|||||||
|
|
||||||
Match *swallows;
|
Match *swallows;
|
||||||
int n = 0;
|
int n = 0;
|
||||||
TAILQ_FOREACH (swallows, &(state->con->swallow_head), matches) {
|
TAILQ_FOREACH(swallows, &(state->con->swallow_head), matches) {
|
||||||
char *serialized = NULL;
|
char *serialized = NULL;
|
||||||
|
|
||||||
#define APPEND_REGEX(re_name) \
|
#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
|
/* Set the same name as was stored in the layout file. While perhaps
|
||||||
* slightly confusing in the first instant, this brings additional
|
* slightly confusing in the first instant, this brings additional
|
||||||
* clarity to which placeholder is waiting for which actual window. */
|
* clarity to which placeholder is waiting for which actual window. */
|
||||||
|
if (con->name != NULL)
|
||||||
xcb_change_property(restore_conn, XCB_PROP_MODE_REPLACE, placeholder,
|
xcb_change_property(restore_conn, XCB_PROP_MODE_REPLACE, placeholder,
|
||||||
A__NET_WM_NAME, A_UTF8_STRING, 8, strlen(con->name), con->name);
|
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",
|
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;
|
Con *child;
|
||||||
TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
|
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
||||||
open_placeholder_window(child);
|
open_placeholder_window(child);
|
||||||
}
|
}
|
||||||
TAILQ_FOREACH (child, &(con->floating_head), floating_windows) {
|
TAILQ_FOREACH(child, &(con->floating_head), floating_windows) {
|
||||||
open_placeholder_window(child);
|
open_placeholder_window(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -239,10 +240,10 @@ static void open_placeholder_window(Con *con) {
|
|||||||
*/
|
*/
|
||||||
void restore_open_placeholder_windows(Con *parent) {
|
void restore_open_placeholder_windows(Con *parent) {
|
||||||
Con *child;
|
Con *child;
|
||||||
TAILQ_FOREACH (child, &(parent->nodes_head), nodes) {
|
TAILQ_FOREACH(child, &(parent->nodes_head), nodes) {
|
||||||
open_placeholder_window(child);
|
open_placeholder_window(child);
|
||||||
}
|
}
|
||||||
TAILQ_FOREACH (child, &(parent->floating_head), floating_windows) {
|
TAILQ_FOREACH(child, &(parent->floating_head), floating_windows) {
|
||||||
open_placeholder_window(child);
|
open_placeholder_window(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,7 +259,7 @@ void restore_open_placeholder_windows(Con *parent) {
|
|||||||
*/
|
*/
|
||||||
bool restore_kill_placeholder(xcb_window_t placeholder) {
|
bool restore_kill_placeholder(xcb_window_t placeholder) {
|
||||||
placeholder_state *state;
|
placeholder_state *state;
|
||||||
TAILQ_FOREACH (state, &state_head, state) {
|
TAILQ_FOREACH(state, &state_head, state) {
|
||||||
if (state->window != placeholder)
|
if (state->window != placeholder)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -277,7 +278,7 @@ bool restore_kill_placeholder(xcb_window_t placeholder) {
|
|||||||
|
|
||||||
static void expose_event(xcb_expose_event_t *event) {
|
static void expose_event(xcb_expose_event_t *event) {
|
||||||
placeholder_state *state;
|
placeholder_state *state;
|
||||||
TAILQ_FOREACH (state, &state_head, state) {
|
TAILQ_FOREACH(state, &state_head, state) {
|
||||||
if (state->window != event->window)
|
if (state->window != event->window)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -305,7 +306,7 @@ static void expose_event(xcb_expose_event_t *event) {
|
|||||||
*/
|
*/
|
||||||
static void configure_notify(xcb_configure_notify_event_t *event) {
|
static void configure_notify(xcb_configure_notify_event_t *event) {
|
||||||
placeholder_state *state;
|
placeholder_state *state;
|
||||||
TAILQ_FOREACH (state, &state_head, state) {
|
TAILQ_FOREACH(state, &state_head, state) {
|
||||||
if (state->window != event->window)
|
if (state->window != event->window)
|
||||||
continue;
|
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