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
|
||||||
|
|
|
@ -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
|
18
DEPENDS
18
DEPENDS
|
@ -4,12 +4,14 @@
|
||||||
"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/ │
|
||||||
|
│ 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/ │
|
│ 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/ │
|
||||||
|
@ -22,7 +24,7 @@
|
||||||
│ 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
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
|
@ -1,2 +0,0 @@
|
||||||
use-x-terminal-emulator.patch
|
|
||||||
manpage-x-terminal-emulator.patch
|
|
|
@ -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,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,13 +3,13 @@
|
||||||
#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"
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) */
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,8 @@
|
||||||
#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; \
|
||||||
|
@ -26,7 +27,8 @@
|
||||||
} 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); \
|
||||||
|
@ -36,7 +38,8 @@
|
||||||
} 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); \
|
||||||
|
@ -49,7 +52,8 @@
|
||||||
#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__); \
|
||||||
} \
|
} \
|
||||||
|
@ -60,6 +64,7 @@
|
||||||
#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);
|
||||||
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
328
i3bar/src/xcb.c
328
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
|
||||||
*
|
*
|
||||||
|
@ -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,7 +174,7 @@ 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. */
|
||||||
|
@ -176,22 +182,41 @@ void refresh_statusline(void) {
|
||||||
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) } });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -331,22 +356,31 @@ void handle_button(xcb_button_press_event_t *event) {
|
||||||
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;
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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,
|
||||||
|
@ -1720,18 +1778,18 @@ void draw_bars(bool unhide) {
|
||||||
/* 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) {
|
||||||
|
|
|
@ -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.
|
||||||
*
|
*
|
||||||
|
|
|
@ -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,17 +47,25 @@ 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,
|
||||||
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
*
|
*
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
132
include/queue.h
132
include/queue.h
|
@ -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,8 +160,7 @@ 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)
|
||||||
|
@ -193,45 +198,48 @@ struct { \
|
||||||
/*
|
/*
|
||||||
* 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); \
|
||||||
|
@ -271,30 +279,35 @@ struct { \
|
||||||
/*
|
/*
|
||||||
* 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) \
|
||||||
|
do { \
|
||||||
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL) \
|
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)
|
||||||
|
@ -344,49 +357,52 @@ struct { \
|
||||||
/*
|
/*
|
||||||
* 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) \
|
||||||
|
do { \
|
||||||
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL) \
|
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL) \
|
||||||
(elm)->field.tqe_next->field.tqe_prev = \
|
(elm)->field.tqe_next->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; \
|
||||||
(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; \
|
||||||
|
@ -394,10 +410,10 @@ struct { \
|
||||||
_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; \
|
||||||
|
@ -407,7 +423,8 @@ struct { \
|
||||||
} 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); \
|
||||||
|
@ -461,12 +478,14 @@ struct { \
|
||||||
/*
|
/*
|
||||||
* 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)) \
|
||||||
|
@ -476,7 +495,8 @@ struct { \
|
||||||
(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)) \
|
||||||
|
@ -486,7 +506,8 @@ struct { \
|
||||||
(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)) \
|
||||||
|
@ -496,7 +517,8 @@ struct { \
|
||||||
(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)) \
|
||||||
|
@ -506,29 +528,27 @@ struct { \
|
||||||
(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); \
|
||||||
|
|
|
@ -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,12 +15,14 @@
|
||||||
#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++)
|
||||||
|
@ -43,13 +45,13 @@
|
||||||
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__)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
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:
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
39
src/click.c
39
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,7 +357,7 @@ 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) ||
|
||||||
|
|
180
src/commands.c
180
src/commands.c
|
@ -513,7 +513,27 @@ 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);
|
||||||
|
|
||||||
|
@ -550,12 +570,9 @@ 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");
|
||||||
|
@ -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,16 +992,18 @@ 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 ||
|
return;
|
||||||
parsed_num < 0 ||
|
}
|
||||||
endptr == which) {
|
|
||||||
|
long parsed_num = ws_name_to_number(which);
|
||||||
|
|
||||||
|
if (parsed_num == -1) {
|
||||||
LOG("Could not parse initial part of \"%s\" as a number.\n", which);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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");
|
||||||
|
|
||||||
|
@ -1274,7 +1334,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
|
||||||
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);
|
||||||
|
@ -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);
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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
|
||||||
|
|
153
src/con.c
153
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));
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 (con->fullscreen_mode == CF_NONE)
|
||||||
if (fullscreen_mode == CF_GLOBAL)
|
con_enable_fullscreen(con, fullscreen_mode);
|
||||||
fullscreen = con_get_fullscreen_con(croot, CF_GLOBAL);
|
else
|
||||||
else {
|
con_disable_fullscreen(con);
|
||||||
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 */
|
/*
|
||||||
|
* 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.
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
21
src/config.c
21
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;
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
|
99
src/ewmh.c
99
src/ewmh.c
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
176
src/handlers.c
176
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
|
||||||
|
@ -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.
|
||||||
|
|
134
src/ipc.c
134
src/ipc.c
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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");
|
||||||
|
@ -600,9 +672,6 @@ IPC_HANDLER(get_workspaces) {
|
||||||
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");
|
||||||
|
@ -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");
|
||||||
|
@ -113,11 +114,6 @@ static int json_end_map(void *ctx) {
|
||||||
|
|
||||||
/* 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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
156
src/main.c
156
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);
|
||||||
|
|
||||||
|
|
21
src/manage.c
21
src/manage.c
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -123,8 +123,8 @@ void startup_sequence_delete(struct Startup_Sequence *sequence) {
|
||||||
* the application is reparented to init (process-id 1), which correctly handles
|
* the application is reparented to init (process-id 1), which correctly handles
|
||||||
* childs, so we don’t have to do it :-).
|
* childs, so we don’t have to do it :-).
|
||||||
*
|
*
|
||||||
* The shell is determined by looking for the SHELL environment variable. If it
|
* The shell used to start applications is the system's bourne shell (i.e.,
|
||||||
* 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.
|
||||||
|
|
28
src/tree.c
28
src/tree.c
|
@ -255,6 +255,7 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool
|
||||||
* X11 Errors are returned when the window was already destroyed */
|
* X11 Errors are returned when the window was already destroyed */
|
||||||
add_ignore_event(cookie.sequence, 0);
|
add_ignore_event(cookie.sequence, 0);
|
||||||
}
|
}
|
||||||
|
ipc_send_window_event("close", con);
|
||||||
FREE(con->window->class_class);
|
FREE(con->window->class_class);
|
||||||
FREE(con->window->class_instance);
|
FREE(con->window->class_instance);
|
||||||
i3string_free(con->window->name);
|
i3string_free(con->window->name);
|
||||||
|
@ -406,8 +407,7 @@ void tree_split(Con *con, orientation_t orientation) {
|
||||||
Con *parent = con->parent;
|
Con *parent = con->parent;
|
||||||
|
|
||||||
/* Force re-rendering to make the indicator border visible. */
|
/* Force re-rendering to make the indicator border visible. */
|
||||||
FREE(con->deco_render_params);
|
con_force_split_parents_redraw(con);
|
||||||
FREE(parent->deco_render_params);
|
|
||||||
|
|
||||||
/* if we are in a container whose parent contains only one
|
/* if we are in a container whose parent contains only one
|
||||||
* child (its split functionality is unused so far), we just change the
|
* child (its split functionality is unused so far), we just change the
|
||||||
|
@ -581,6 +581,13 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
Con *focus = con_descend_direction(workspace, direction);
|
Con *focus = con_descend_direction(workspace, direction);
|
||||||
|
|
||||||
|
/* special case: if there was no tiling con to focus and the workspace
|
||||||
|
* has a floating con in the focus stack, focus the top of the focus
|
||||||
|
* stack (which may be floating) */
|
||||||
|
if (focus == workspace)
|
||||||
|
focus = con_descend_focused(workspace);
|
||||||
|
|
||||||
if (focus) {
|
if (focus) {
|
||||||
con_focus(focus);
|
con_focus(focus);
|
||||||
x_set_warp_to(&(focus->rect));
|
x_set_warp_to(&(focus->rect));
|
||||||
|
@ -591,8 +598,10 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
|
||||||
Con *parent = con->parent;
|
Con *parent = con->parent;
|
||||||
|
|
||||||
if (con->type == CT_FLOATING_CON) {
|
if (con->type == CT_FLOATING_CON) {
|
||||||
|
if (orientation != HORIZ)
|
||||||
|
return false;
|
||||||
|
|
||||||
/* left/right focuses the previous/next floating container */
|
/* left/right focuses the previous/next floating container */
|
||||||
if (orientation == HORIZ) {
|
|
||||||
Con *next;
|
Con *next;
|
||||||
if (way == 'n')
|
if (way == 'n')
|
||||||
next = TAILQ_NEXT(con, floating_windows);
|
next = TAILQ_NEXT(con, floating_windows);
|
||||||
|
@ -611,13 +620,16 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
|
||||||
if (!next)
|
if (!next)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
/* Raise the floating window on top of other windows preserving
|
||||||
|
* relative stack order */
|
||||||
|
while (TAILQ_LAST(&(parent->floating_head), floating_head) != next) {
|
||||||
|
Con *last = TAILQ_LAST(&(parent->floating_head), floating_head);
|
||||||
|
TAILQ_REMOVE(&(parent->floating_head), last, floating_windows);
|
||||||
|
TAILQ_INSERT_HEAD(&(parent->floating_head), last, floating_windows);
|
||||||
|
}
|
||||||
|
|
||||||
con_focus(con_descend_focused(next));
|
con_focus(con_descend_focused(next));
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
/* up/down cycles through the Z-index */
|
|
||||||
/* TODO: implement cycling through the z-index */
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the orientation does not match or there is no other con to focus, we
|
/* If the orientation does not match or there is no other con to focus, we
|
||||||
|
|
|
@ -125,7 +125,8 @@ void window_update_name_legacy(i3Window *win, xcb_get_property_reply_t *prop, bo
|
||||||
*/
|
*/
|
||||||
void window_update_leader(i3Window *win, xcb_get_property_reply_t *prop) {
|
void window_update_leader(i3Window *win, xcb_get_property_reply_t *prop) {
|
||||||
if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
|
if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
|
||||||
DLOG("CLIENT_LEADER not set.\n");
|
DLOG("CLIENT_LEADER not set on window 0x%08x.\n", win->id);
|
||||||
|
win->leader = XCB_NONE;
|
||||||
FREE(prop);
|
FREE(prop);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -149,7 +150,8 @@ void window_update_leader(i3Window *win, xcb_get_property_reply_t *prop) {
|
||||||
*/
|
*/
|
||||||
void window_update_transient_for(i3Window *win, xcb_get_property_reply_t *prop) {
|
void window_update_transient_for(i3Window *win, xcb_get_property_reply_t *prop) {
|
||||||
if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
|
if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
|
||||||
DLOG("TRANSIENT_FOR not set.\n");
|
DLOG("TRANSIENT_FOR not set on window 0x%08x.\n", win->id);
|
||||||
|
win->transient_for = XCB_NONE;
|
||||||
FREE(prop);
|
FREE(prop);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -160,7 +162,7 @@ void window_update_transient_for(i3Window *win, xcb_get_property_reply_t *prop)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DLOG("Transient for changed to %08x\n", transient_for);
|
DLOG("Transient for changed to 0x%08x (window 0x%08x)\n", transient_for, win->id);
|
||||||
|
|
||||||
win->transient_for = transient_for;
|
win->transient_for = transient_for;
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#include "all.h"
|
#include "all.h"
|
||||||
|
#include "yajl_utils.h"
|
||||||
|
|
||||||
/* Stores a copy of the name of the last used workspace for the workspace
|
/* Stores a copy of the name of the last used workspace for the workspace
|
||||||
* back-and-forth switching. */
|
* back-and-forth switching. */
|
||||||
|
@ -91,7 +92,10 @@ Con *workspace_get(const char *num, bool *created) {
|
||||||
|
|
||||||
con_attach(workspace, content, false);
|
con_attach(workspace, content, false);
|
||||||
|
|
||||||
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
|
ipc_send_workspace_event("init", workspace, NULL);
|
||||||
|
ewmh_update_number_of_desktops();
|
||||||
|
ewmh_update_desktop_names();
|
||||||
|
ewmh_update_desktop_viewport();
|
||||||
if (created != NULL)
|
if (created != NULL)
|
||||||
*created = true;
|
*created = true;
|
||||||
} else if (created != NULL) {
|
} else if (created != NULL) {
|
||||||
|
@ -123,7 +127,7 @@ Con *create_workspace_on_output(Output *output, Con *content) {
|
||||||
strncasecmp(bind->command, "workspace", strlen("workspace")) != 0)
|
strncasecmp(bind->command, "workspace", strlen("workspace")) != 0)
|
||||||
continue;
|
continue;
|
||||||
DLOG("relevant command = %s\n", bind->command);
|
DLOG("relevant command = %s\n", bind->command);
|
||||||
char *target = bind->command + strlen("workspace ");
|
const char *target = bind->command + strlen("workspace ");
|
||||||
while ((*target == ' ' || *target == '\t') && target != '\0')
|
while ((*target == ' ' || *target == '\t') && target != '\0')
|
||||||
target++;
|
target++;
|
||||||
/* We check if this is the workspace
|
/* We check if this is the workspace
|
||||||
|
@ -139,16 +143,16 @@ Con *create_workspace_on_output(Output *output, Con *content) {
|
||||||
strncasecmp(target, "back_and_forth", strlen("back_and_forth")) == 0 ||
|
strncasecmp(target, "back_and_forth", strlen("back_and_forth")) == 0 ||
|
||||||
strncasecmp(target, "current", strlen("current")) == 0)
|
strncasecmp(target, "current", strlen("current")) == 0)
|
||||||
continue;
|
continue;
|
||||||
if (*target == '"')
|
char *target_name = parse_string(&target, false);
|
||||||
target++;
|
if (target_name == NULL)
|
||||||
if (strncasecmp(target, "__", strlen("__")) == 0) {
|
continue;
|
||||||
|
if (strncasecmp(target_name, "__", strlen("__")) == 0) {
|
||||||
LOG("Cannot create workspace \"%s\". Names starting with __ are i3-internal.\n", target);
|
LOG("Cannot create workspace \"%s\". Names starting with __ are i3-internal.\n", target);
|
||||||
|
free(target_name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
FREE(ws->name);
|
FREE(ws->name);
|
||||||
ws->name = strdup(target);
|
ws->name = target_name;
|
||||||
if (ws->name[strlen(ws->name) - 1] == '"')
|
|
||||||
ws->name[strlen(ws->name) - 1] = '\0';
|
|
||||||
DLOG("trying name *%s*\n", ws->name);
|
DLOG("trying name *%s*\n", ws->name);
|
||||||
|
|
||||||
/* Ensure that this workspace is not assigned to a different output —
|
/* Ensure that this workspace is not assigned to a different output —
|
||||||
|
@ -176,15 +180,7 @@ Con *create_workspace_on_output(Output *output, Con *content) {
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
/* Set ->num to the number of the workspace, if the name actually
|
/* Set ->num to the number of the workspace, if the name actually
|
||||||
* is a number or starts with a number */
|
* is a number or starts with a number */
|
||||||
char *endptr = NULL;
|
ws->num = ws_name_to_number(ws->name);
|
||||||
long parsed_num = strtol(ws->name, &endptr, 10);
|
|
||||||
if (parsed_num == LONG_MIN ||
|
|
||||||
parsed_num == LONG_MAX ||
|
|
||||||
parsed_num < 0 ||
|
|
||||||
endptr == ws->name)
|
|
||||||
ws->num = -1;
|
|
||||||
else
|
|
||||||
ws->num = parsed_num;
|
|
||||||
LOG("Used number %d for workspace with name %s\n", ws->num, ws->name);
|
LOG("Used number %d for workspace with name %s\n", ws->num, ws->name);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -325,11 +321,14 @@ static void workspace_reassign_sticky(Con *con) {
|
||||||
static void workspace_defer_update_urgent_hint_cb(EV_P_ ev_timer *w, int revents) {
|
static void workspace_defer_update_urgent_hint_cb(EV_P_ ev_timer *w, int revents) {
|
||||||
Con *con = w->data;
|
Con *con = w->data;
|
||||||
|
|
||||||
|
if (con->urgent) {
|
||||||
DLOG("Resetting urgency flag of con %p by timer\n", con);
|
DLOG("Resetting urgency flag of con %p by timer\n", 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);
|
||||||
tree_render();
|
tree_render();
|
||||||
|
}
|
||||||
|
|
||||||
ev_timer_stop(main_loop, con->urgency_timer);
|
ev_timer_stop(main_loop, con->urgency_timer);
|
||||||
FREE(con->urgency_timer);
|
FREE(con->urgency_timer);
|
||||||
|
@ -385,6 +384,7 @@ static void _workspace_show(Con *workspace) {
|
||||||
* focus and thereby immediately destroy it */
|
* focus and thereby immediately destroy it */
|
||||||
if (next->urgent && (int)(config.workspace_urgency_timer * 1000) > 0) {
|
if (next->urgent && (int)(config.workspace_urgency_timer * 1000) > 0) {
|
||||||
/* focus for now… */
|
/* focus for now… */
|
||||||
|
next->urgent = false;
|
||||||
con_focus(next);
|
con_focus(next);
|
||||||
|
|
||||||
/* … but immediately reset urgency flags; they will be set to false by
|
/* … but immediately reset urgency flags; they will be set to false by
|
||||||
|
@ -410,7 +410,7 @@ static void _workspace_show(Con *workspace) {
|
||||||
} else
|
} else
|
||||||
con_focus(next);
|
con_focus(next);
|
||||||
|
|
||||||
ipc_send_workspace_focus_event(workspace, current);
|
ipc_send_workspace_event("focus", workspace, current);
|
||||||
|
|
||||||
DLOG("old = %p / %s\n", old, (old ? old->name : "(null)"));
|
DLOG("old = %p / %s\n", old, (old ? old->name : "(null)"));
|
||||||
/* Close old workspace if necessary. This must be done *after* doing
|
/* Close old workspace if necessary. This must be done *after* doing
|
||||||
|
@ -422,8 +422,19 @@ static void _workspace_show(Con *workspace) {
|
||||||
/* check if this workspace is currently visible */
|
/* check if this workspace is currently visible */
|
||||||
if (!workspace_is_visible(old)) {
|
if (!workspace_is_visible(old)) {
|
||||||
LOG("Closing old workspace (%p / %s), it is empty\n", old, old->name);
|
LOG("Closing old workspace (%p / %s), it is empty\n", old, old->name);
|
||||||
|
yajl_gen gen = ipc_marshal_workspace_event("empty", old, NULL);
|
||||||
tree_close(old, DONT_KILL_WINDOW, false, false);
|
tree_close(old, 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);
|
||||||
|
|
||||||
|
ewmh_update_number_of_desktops();
|
||||||
|
ewmh_update_desktop_names();
|
||||||
|
ewmh_update_desktop_viewport();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -764,7 +775,7 @@ void workspace_update_urgent_flag(Con *ws) {
|
||||||
DLOG("Workspace urgency flag changed from %d to %d\n", old_flag, ws->urgent);
|
DLOG("Workspace urgency flag changed from %d to %d\n", old_flag, ws->urgent);
|
||||||
|
|
||||||
if (old_flag != ws->urgent)
|
if (old_flag != ws->urgent)
|
||||||
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"urgent\"}");
|
ipc_send_workspace_event("urgent", ws, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
30
src/x.c
30
src/x.c
|
@ -15,9 +15,9 @@
|
||||||
/* Stores the X11 window ID of the currently focused window */
|
/* Stores the X11 window ID of the currently focused window */
|
||||||
xcb_window_t focused_id = XCB_NONE;
|
xcb_window_t focused_id = XCB_NONE;
|
||||||
|
|
||||||
/* Because 'focused_id' might be reset to force input focus (after click to
|
/* Because 'focused_id' might be reset to force input focus, we separately keep
|
||||||
* raise), we separately keep track of the X11 window ID to be able to always
|
* track of the X11 window ID to be able to always tell whether the focused
|
||||||
* tell whether the focused window actually changed. */
|
* window actually changed. */
|
||||||
static xcb_window_t last_focused = XCB_NONE;
|
static xcb_window_t last_focused = XCB_NONE;
|
||||||
|
|
||||||
/* Stores coordinates to warp mouse pointer to if set */
|
/* Stores coordinates to warp mouse pointer to if set */
|
||||||
|
@ -430,16 +430,16 @@ void x_draw_decoration(Con *con) {
|
||||||
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &leftline);
|
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &leftline);
|
||||||
}
|
}
|
||||||
if (!(borders_to_hide & ADJ_RIGHT_SCREEN_EDGE)) {
|
if (!(borders_to_hide & ADJ_RIGHT_SCREEN_EDGE)) {
|
||||||
xcb_rectangle_t rightline = {r->width + br.width + br.x, 0, r->width, r->height};
|
xcb_rectangle_t rightline = {r->width + (br.width + br.x), 0, -(br.width + br.x), r->height};
|
||||||
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &rightline);
|
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &rightline);
|
||||||
}
|
}
|
||||||
if (!(borders_to_hide & ADJ_LOWER_SCREEN_EDGE)) {
|
if (!(borders_to_hide & ADJ_LOWER_SCREEN_EDGE)) {
|
||||||
xcb_rectangle_t bottomline = {0, r->height + br.height + br.y, r->width, r->height};
|
xcb_rectangle_t bottomline = {br.x, r->height + (br.height + br.y), r->width + br.width, -(br.height + br.y)};
|
||||||
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &bottomline);
|
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &bottomline);
|
||||||
}
|
}
|
||||||
/* 1pixel border needs an additional line at the top */
|
/* 1pixel border needs an additional line at the top */
|
||||||
if (p->border_style == BS_PIXEL && !(borders_to_hide & ADJ_UPPER_SCREEN_EDGE)) {
|
if (p->border_style == BS_PIXEL && !(borders_to_hide & ADJ_UPPER_SCREEN_EDGE)) {
|
||||||
xcb_rectangle_t topline = {br.x, 0, con->rect.width + br.width + br.x, br.y};
|
xcb_rectangle_t topline = {br.x, 0, r->width + br.width, br.y};
|
||||||
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &topline);
|
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &topline);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,10 +453,10 @@ void x_draw_decoration(Con *con) {
|
||||||
xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->indicator});
|
xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->indicator});
|
||||||
if (p->parent_layout == L_SPLITH)
|
if (p->parent_layout == L_SPLITH)
|
||||||
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]) {
|
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]) {
|
||||||
{r->width + br.width + br.x, br.y, r->width, r->height + br.height}});
|
{r->width + (br.width + br.x), br.y, -(br.width + br.x), r->height + br.height}});
|
||||||
else if (p->parent_layout == L_SPLITV)
|
else if (p->parent_layout == L_SPLITV)
|
||||||
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]) {
|
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]) {
|
||||||
{br.x, r->height + br.height + br.y, r->width - (2 * br.x), r->height}});
|
{br.x, r->height + (br.height + br.y), r->width + br.width, -(br.height + br.y)}});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -473,12 +473,12 @@ void x_draw_decoration(Con *con) {
|
||||||
/* 5: draw two unconnected horizontal lines in border color */
|
/* 5: draw two unconnected horizontal lines in border color */
|
||||||
xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->border});
|
xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->border});
|
||||||
Rect *dr = &(con->deco_rect);
|
Rect *dr = &(con->deco_rect);
|
||||||
int deco_diff_l = 2;
|
adjacent_t borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders;
|
||||||
int deco_diff_r = 2;
|
int deco_diff_l = borders_to_hide & ADJ_LEFT_SCREEN_EDGE ? 0 : con->current_border_width;
|
||||||
if (parent->layout == L_TABBED) {
|
int deco_diff_r = borders_to_hide & ADJ_RIGHT_SCREEN_EDGE ? 0 : con-> current_border_width;
|
||||||
if (TAILQ_PREV(con, nodes_head, nodes) != NULL)
|
if (parent->layout == L_TABBED ||
|
||||||
|
(parent->layout == L_STACKED && TAILQ_NEXT(con, nodes) != NULL)) {
|
||||||
deco_diff_l = 0;
|
deco_diff_l = 0;
|
||||||
if (TAILQ_NEXT(con, nodes) != NULL)
|
|
||||||
deco_diff_r = 0;
|
deco_diff_r = 0;
|
||||||
}
|
}
|
||||||
xcb_segment_t segments[] = {
|
xcb_segment_t segments[] = {
|
||||||
|
@ -1023,7 +1023,7 @@ void x_push_changes(Con *con) {
|
||||||
values[0] = CHILD_EVENT_MASK & ~(XCB_EVENT_MASK_FOCUS_CHANGE);
|
values[0] = CHILD_EVENT_MASK & ~(XCB_EVENT_MASK_FOCUS_CHANGE);
|
||||||
xcb_change_window_attributes(conn, focused->window->id, XCB_CW_EVENT_MASK, values);
|
xcb_change_window_attributes(conn, focused->window->id, XCB_CW_EVENT_MASK, values);
|
||||||
}
|
}
|
||||||
xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, to_focus, last_timestamp);
|
xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, to_focus, XCB_CURRENT_TIME);
|
||||||
if (focused->window != NULL) {
|
if (focused->window != NULL) {
|
||||||
values[0] = CHILD_EVENT_MASK;
|
values[0] = CHILD_EVENT_MASK;
|
||||||
xcb_change_window_attributes(conn, focused->window->id, XCB_CW_EVENT_MASK, values);
|
xcb_change_window_attributes(conn, focused->window->id, XCB_CW_EVENT_MASK, values);
|
||||||
|
@ -1041,7 +1041,7 @@ void x_push_changes(Con *con) {
|
||||||
|
|
||||||
if (focused_id == XCB_NONE) {
|
if (focused_id == XCB_NONE) {
|
||||||
DLOG("Still no window focused, better set focus to the root window\n");
|
DLOG("Still no window focused, better set focus to the root window\n");
|
||||||
xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, last_timestamp);
|
xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
|
||||||
ewmh_update_active_window(XCB_WINDOW_NONE);
|
ewmh_update_active_window(XCB_WINDOW_NONE);
|
||||||
focused_id = root;
|
focused_id = root;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,4 +8,3 @@ inc
|
||||||
META.yml
|
META.yml
|
||||||
i3-cfg-for-*
|
i3-cfg-for-*
|
||||||
-
|
-
|
||||||
Xdummy.so
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ WriteMakefile(
|
||||||
'AnyEvent::I3' => '0.15',
|
'AnyEvent::I3' => '0.15',
|
||||||
'X11::XCB' => '0.09',
|
'X11::XCB' => '0.09',
|
||||||
'Inline' => 0,
|
'Inline' => 0,
|
||||||
|
'Inline::C' => 0,
|
||||||
'ExtUtils::PkgConfig' => 0,
|
'ExtUtils::PkgConfig' => 0,
|
||||||
'Test::More' => '0.94',
|
'Test::More' => '0.94',
|
||||||
'IPC::Run' => 0,
|
'IPC::Run' => 0,
|
||||||
|
|
1930
testcases/Xdummy
1930
testcases/Xdummy
File diff suppressed because it is too large
Load Diff
|
@ -19,7 +19,7 @@ use Time::HiRes qw(time);
|
||||||
use IO::Handle;
|
use IO::Handle;
|
||||||
# these are shipped with the testsuite
|
# these are shipped with the testsuite
|
||||||
use lib qw(lib);
|
use lib qw(lib);
|
||||||
use StartXDummy;
|
use StartXServer;
|
||||||
use StatusLine;
|
use StatusLine;
|
||||||
use TestWorker;
|
use TestWorker;
|
||||||
# the following modules are not shipped with Perl
|
# the following modules are not shipped with Perl
|
||||||
|
@ -43,7 +43,7 @@ sub Log { say $log "@_" }
|
||||||
|
|
||||||
my %timings;
|
my %timings;
|
||||||
my $help = 0;
|
my $help = 0;
|
||||||
# Number of tests to run in parallel. Important to know how many Xdummy
|
# Number of tests to run in parallel. Important to know how many Xephyr
|
||||||
# instances we need to start (unless @displays are given). Defaults to
|
# instances we need to start (unless @displays are given). Defaults to
|
||||||
# num_cores * 2.
|
# num_cores * 2.
|
||||||
my $parallel = undef;
|
my $parallel = undef;
|
||||||
|
@ -55,11 +55,11 @@ my %options = (
|
||||||
coverage => 0,
|
coverage => 0,
|
||||||
restart => 0,
|
restart => 0,
|
||||||
);
|
);
|
||||||
my $keep_xdummy_output = 0;
|
my $keep_xserver_output = 0;
|
||||||
|
|
||||||
my $result = GetOptions(
|
my $result = GetOptions(
|
||||||
"coverage-testing" => \$options{coverage},
|
"coverage-testing" => \$options{coverage},
|
||||||
"keep-xdummy-output" => \$keep_xdummy_output,
|
"keep-xserver-output" => \$keep_xserver_output,
|
||||||
"valgrind" => \$options{valgrind},
|
"valgrind" => \$options{valgrind},
|
||||||
"strace" => \$options{strace},
|
"strace" => \$options{strace},
|
||||||
"xtrace" => \$options{xtrace},
|
"xtrace" => \$options{xtrace},
|
||||||
|
@ -86,6 +86,9 @@ foreach my $binary (@binaries) {
|
||||||
die "$binary is not an executable" unless -x $binary;
|
die "$binary is not an executable" unless -x $binary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qx(Xephyr -help 2>&1);
|
||||||
|
die "Xephyr was not found in your path. Please install Xephyr (xserver-xephyr on Debian)." if $?;
|
||||||
|
|
||||||
@displays = split(/,/, join(',', @displays));
|
@displays = split(/,/, join(',', @displays));
|
||||||
@displays = map { s/ //g; $_ } @displays;
|
@displays = map { s/ //g; $_ } @displays;
|
||||||
|
|
||||||
|
@ -97,9 +100,9 @@ my @testfiles = @ARGV;
|
||||||
|
|
||||||
my $numtests = scalar @testfiles;
|
my $numtests = scalar @testfiles;
|
||||||
|
|
||||||
# No displays specified, let’s start some Xdummy instances.
|
# No displays specified, let’s start some Xephyr instances.
|
||||||
if (@displays == 0) {
|
if (@displays == 0) {
|
||||||
@displays = start_xdummy($parallel, $numtests, $keep_xdummy_output);
|
@displays = start_xserver($parallel, $numtests, $keep_xserver_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
# 1: create an output directory for this test-run
|
# 1: create an output directory for this test-run
|
||||||
|
@ -115,8 +118,7 @@ symlink("$outdir", "latest") or die "Could not symlink latest to $outdir";
|
||||||
# connect to all displays for two reasons:
|
# connect to all displays for two reasons:
|
||||||
# 1: check if the display actually works
|
# 1: check if the display actually works
|
||||||
# 2: keep the connection open so that i3 is not the only client. this prevents
|
# 2: keep the connection open so that i3 is not the only client. this prevents
|
||||||
# the X server from exiting (Xdummy will restart it, but not quick enough
|
# the X server from exiting
|
||||||
# sometimes)
|
|
||||||
my @single_worker;
|
my @single_worker;
|
||||||
for my $display (@displays) {
|
for my $display (@displays) {
|
||||||
my $screen;
|
my $screen;
|
||||||
|
@ -131,7 +133,7 @@ for my $display (@displays) {
|
||||||
|
|
||||||
# Read previous timing information, if available. We will be able to roughly
|
# Read previous timing information, if available. We will be able to roughly
|
||||||
# predict the test duration and schedule a good order for the tests.
|
# predict the test duration and schedule a good order for the tests.
|
||||||
my $timingsjson = StartXDummy::slurp('.last_run_timings.json');
|
my $timingsjson = StartXServer::slurp('.last_run_timings.json');
|
||||||
%timings = %{decode_json($timingsjson)} if length($timingsjson) > 0;
|
%timings = %{decode_json($timingsjson)} if length($timingsjson) > 0;
|
||||||
|
|
||||||
# Re-order the files so that those which took the longest time in the previous
|
# Re-order the files so that those which took the longest time in the previous
|
||||||
|
@ -220,7 +222,7 @@ printf("\t%s with %.2f seconds\n", $_, $timings{$_})
|
||||||
if ($numtests == 1) {
|
if ($numtests == 1) {
|
||||||
say '';
|
say '';
|
||||||
say 'Test output:';
|
say 'Test output:';
|
||||||
say StartXDummy::slurp($logfile);
|
say StartXServer::slurp($logfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
END { cleanup() }
|
END { cleanup() }
|
||||||
|
@ -346,7 +348,7 @@ complete-run.pl [files...]
|
||||||
|
|
||||||
=head1 EXAMPLE
|
=head1 EXAMPLE
|
||||||
|
|
||||||
To run the whole testsuite on a reasonable number of Xdummy instances (your
|
To run the whole testsuite on a reasonable number of Xephyr instances (your
|
||||||
running X11 will not be touched), run:
|
running X11 will not be touched), run:
|
||||||
./complete-run.pl
|
./complete-run.pl
|
||||||
|
|
||||||
|
@ -365,11 +367,11 @@ will parallelize the tests:
|
||||||
# Run tests on the second X server
|
# Run tests on the second X server
|
||||||
./complete-run.pl -d :1
|
./complete-run.pl -d :1
|
||||||
|
|
||||||
# Run four tests in parallel on some Xdummy servers
|
# Run four tests in parallel on some Xephyr servers
|
||||||
./complete-run.pl -d :1,:2,:3,:4
|
./complete-run.pl -d :1,:2,:3,:4
|
||||||
|
|
||||||
Note that it is not necessary to specify this anymore. If omitted,
|
Note that it is not necessary to specify this anymore. If omitted,
|
||||||
complete-run.pl will start (num_cores * 2) Xdummy instances.
|
complete-run.pl will start (num_cores * 2) Xephyr instances.
|
||||||
|
|
||||||
=item B<--valgrind>
|
=item B<--valgrind>
|
||||||
|
|
||||||
|
@ -392,8 +394,8 @@ Exits i3 cleanly (instead of kill -9) to make coverage testing work properly.
|
||||||
|
|
||||||
=item B<--parallel>
|
=item B<--parallel>
|
||||||
|
|
||||||
Number of Xdummy instances to start (if you don’t want to start num_cores * 2
|
Number of Xephyr instances to start (if you don’t want to start num_cores * 2
|
||||||
instances for some reason).
|
instances for some reason).
|
||||||
|
|
||||||
# Run all tests on a single Xdummy instance
|
# Run all tests on a single Xephyr instance
|
||||||
./complete-run.pl -p 1
|
./complete-run.pl -p 1
|
||||||
|
|
|
@ -20,7 +20,7 @@ bindsym Mod1+h split h
|
||||||
bindsym Mod1+v split v
|
bindsym Mod1+v split v
|
||||||
|
|
||||||
# Fullscreen (Mod1+f)
|
# Fullscreen (Mod1+f)
|
||||||
bindsym Mod1+f fullscreen
|
bindsym Mod1+f fullscreen toggle
|
||||||
|
|
||||||
# Stacking (Mod1+s)
|
# Stacking (Mod1+s)
|
||||||
bindsym Mod1+s layout stacking
|
bindsym Mod1+s layout stacking
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package StartXDummy;
|
package StartXServer;
|
||||||
# vim:ts=4:sw=4:expandtab
|
# vim:ts=4:sw=4:expandtab
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
|
@ -7,7 +7,7 @@ use Exporter 'import';
|
||||||
use Time::HiRes qw(sleep);
|
use Time::HiRes qw(sleep);
|
||||||
use v5.10;
|
use v5.10;
|
||||||
|
|
||||||
our @EXPORT = qw(start_xdummy);
|
our @EXPORT = qw(start_xserver);
|
||||||
|
|
||||||
my @pids;
|
my @pids;
|
||||||
my $x_socketpath = '/tmp/.X11-unix/X';
|
my $x_socketpath = '/tmp/.X11-unix/X';
|
||||||
|
@ -19,15 +19,15 @@ sub slurp {
|
||||||
<$fh>;
|
<$fh>;
|
||||||
}
|
}
|
||||||
|
|
||||||
# forks an Xdummy or Xdmx process
|
# forks an X server process
|
||||||
sub fork_xserver {
|
sub fork_xserver {
|
||||||
my $keep_xdummy_output = shift;
|
my $keep_xserver_output = shift;
|
||||||
my $displaynum = shift;
|
my $displaynum = shift;
|
||||||
my $pid = fork();
|
my $pid = fork();
|
||||||
die "Could not fork: $!" unless defined($pid);
|
die "Could not fork: $!" unless defined($pid);
|
||||||
if ($pid == 0) {
|
if ($pid == 0) {
|
||||||
# Child, close stdout/stderr, then start Xdummy.
|
# Child, close stdout/stderr, then start Xephyr
|
||||||
if (!$keep_xdummy_output) {
|
if (!$keep_xserver_output) {
|
||||||
close STDOUT;
|
close STDOUT;
|
||||||
close STDERR;
|
close STDERR;
|
||||||
}
|
}
|
||||||
|
@ -60,16 +60,17 @@ sub wait_for_x {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
=head2 start_xdummy($parallel)
|
=head2 start_xserver($parallel)
|
||||||
|
|
||||||
Starts C<$parallel> (or number of cores * 2 if undef) Xdummy processes (see
|
Starts C<$parallel> (or number of cores * 2 if undef) Xephyr processes (see
|
||||||
the file ./Xdummy) and returns two arrayrefs: a list of X11 display numbers to
|
http://www.freedesktop.org/wiki/Software/Xephyr/) and returns two arrayrefs: a
|
||||||
the Xdummy processes and a list of PIDs of the processes.
|
list of X11 display numbers to the Xephyr processes and a list of PIDs of the
|
||||||
|
processes.
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
sub start_xdummy {
|
sub start_xserver {
|
||||||
my ($parallel, $numtests, $keep_xdummy_output) = @_;
|
my ($parallel, $numtests, $keep_xserver_output) = @_;
|
||||||
|
|
||||||
my @displays = ();
|
my @displays = ();
|
||||||
my @childpids = ();
|
my @childpids = ();
|
||||||
|
@ -78,11 +79,8 @@ sub start_xdummy {
|
||||||
my $child = waitpid -1, POSIX::WNOHANG;
|
my $child = waitpid -1, POSIX::WNOHANG;
|
||||||
@pids = grep { $_ != $child } @pids;
|
@pids = grep { $_ != $child } @pids;
|
||||||
return unless @pids == 0;
|
return unless @pids == 0;
|
||||||
print STDERR "All Xdummy processes died.\n";
|
print STDERR "All X server processes died.\n";
|
||||||
print STDERR "Use ./complete-run.pl --parallel 1 --keep-xdummy-output\n";
|
print STDERR "Use ./complete-run.pl --parallel 1 --keep-xserver-output\n";
|
||||||
print STDERR "";
|
|
||||||
print STDERR "A frequent cause for this is missing the DUMMY Xorg module,\n";
|
|
||||||
print STDERR "package xserver-xorg-video-dummy on Debian.\n";
|
|
||||||
exit 1;
|
exit 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -104,16 +102,13 @@ sub start_xdummy {
|
||||||
my ($displaynum) = map { /(\d+)$/ } reverse sort glob($x_socketpath . '*');
|
my ($displaynum) = map { /(\d+)$/ } reverse sort glob($x_socketpath . '*');
|
||||||
$displaynum++;
|
$displaynum++;
|
||||||
|
|
||||||
say "Starting $parallel Xdummy instances, starting at :$displaynum...";
|
say "Starting $parallel Xephyr instances, starting at :$displaynum...";
|
||||||
|
|
||||||
my @sockets_waiting;
|
my @sockets_waiting;
|
||||||
for (1 .. $parallel) {
|
for (1 .. $parallel) {
|
||||||
# We use -config /dev/null to prevent Xdummy from using the system
|
my $socket = fork_xserver($keep_xserver_output, $displaynum,
|
||||||
# Xorg configuration. The tests should be independant from the
|
'Xephyr', ":$displaynum", '-screen', '1280x800',
|
||||||
# actual system X configuration.
|
'-nolisten', 'tcp');
|
||||||
my $socket = fork_xserver($keep_xdummy_output, $displaynum,
|
|
||||||
'./Xdummy', ":$displaynum", '-config', '/dev/null',
|
|
||||||
'-configdir', '/dev/null', '-nolisten', 'tcp');
|
|
||||||
push(@displays, ":$displaynum");
|
push(@displays, ":$displaynum");
|
||||||
push(@sockets_waiting, $socket);
|
push(@sockets_waiting, $socket);
|
||||||
$displaynum++;
|
$displaynum++;
|
|
@ -337,6 +337,7 @@ sub open_window {
|
||||||
$args{name} //= 'Window ' . counter_window();
|
$args{name} //= 'Window ' . counter_window();
|
||||||
|
|
||||||
my $window = $x->root->create_child(%args);
|
my $window = $x->root->create_child(%args);
|
||||||
|
$window->add_hint('input');
|
||||||
|
|
||||||
if ($before_map) {
|
if ($before_map) {
|
||||||
# TODO: investigate why _create is not needed
|
# TODO: investigate why _create is not needed
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue