From 68607ab3b45d8f052390fb739ea85a895d34ea1e Mon Sep 17 00:00:00 2001 From: Timur Demin Date: Fri, 29 Jan 2021 22:42:22 +0500 Subject: [PATCH] Apply desktop patch This adds desktop manager windows (the ones of type _NET_WM_WINDOW_TYPE_DESKTOP) support. As this is a fork, this also replaces the README. --- README.desktop.md | 46 +++++++++ README.gaps.md | 114 +++++++++++++++++++++++ README.md | 119 ++---------------------- include/i3-atoms_NET_SUPPORTED.xmacro.h | 1 + include/x.h | 3 + src/click.c | 8 +- src/commands.c | 8 ++ src/con.c | 3 + src/handlers.c | 15 +++ src/manage.c | 11 +++ src/tree.c | 2 + src/x.c | 8 ++ 12 files changed, 228 insertions(+), 110 deletions(-) create mode 100644 README.desktop.md create mode 100644 README.gaps.md diff --git a/README.desktop.md b/README.desktop.md new file mode 100644 index 000000000..c5fc1166e --- /dev/null +++ b/README.desktop.md @@ -0,0 +1,46 @@ +i3-desktop.patch +================= + +This is a small patch for the i3 window manager to support the use of a +desktop window (with wallpaper and icons) created by a file manager like +Nautilus, PCManFM and others. + +How to use +=========== +* Download and unzip the i3 source code (this patch is made for version + 4.11, can't say if/when newer versions will break it). +* Apply the patch: + + ```shell + $ patch -p1 < /path/to/i3-desktop.patch + ``` +* Compile, install and restart i3 +* Launch a desktop manager, e.g. + + ```shell + $ pcmanfm --desktop + ``` + or + ```shell + $ nautilus + ``` + (nautilus should start the desktop manager automatically, unless you have + previously disabled it). +* Go to an empty workspace, you should see the icons of your ~/Desktop + folder on the desktop. If you open any tiling window it should hide + the desktop. + +Todo +==== +* Test what happens on a dual monitor setup. +* The code does not handle the case where the window desktop is closed if + e.g. it crashes or the user kills it. Nothing really bad should happen, + but it should be handled properly. +* It looks like the height for the i3bar is not subtracted from the desktop + window (so, i3bar may partially cover some elements). +* More desktop managers should be tested. + +Contacts +========= +You can send bug reports, comments, suggestions, improvements to: + alessandro.g89@gmail.com diff --git a/README.gaps.md b/README.gaps.md new file mode 100644 index 000000000..d94d62c6f --- /dev/null +++ b/README.gaps.md @@ -0,0 +1,114 @@ +[![Travis](https://img.shields.io/travis/Airblader/i3.svg)](https://travis-ci.org/Airblader/i3) +[![Issues](https://img.shields.io/github/issues/Airblader/i3.svg)](https://github.com/Airblader/i3/issues) +[![Forks](https://img.shields.io/github/forks/Airblader/i3.svg)](https://github.com/Airblader/i3/network) +[![Stars](https://img.shields.io/github/stars/Airblader/i3.svg)](https://github.com/Airblader/i3/stargazers) + +# i3-gaps + +## What is i3-gaps? + +i3-gaps is a fork of [i3wm](https://www.i3wm.org), a tiling window manager for X11. It is kept up to date with upstream, adding a few additional features such as gaps between windows (see below for a complete list). + +![i3](http://i.imgur.com/y8sZE6o.jpg) + +## How do I install i3-gaps? + +Please refer to the [wiki](https://github.com/Airblader/i3/wiki/installation). + +## Where can I get help? + +For bug reports or feature requests regarding i3-gaps specifically, open an issue on [GitHub](https://www.github.com/Airblader/i3). If your issue is with core i3 functionality, please report it [upstream](https://www.github.com/i3/i3). + +For support & all other kinds of questions, you can ask your question on the official [subreddit /r/i3wm](https://www.reddit.com/r/i3wm). + +# Features + +## i3 + +### gaps + +*Note:* In order to use gaps you need to disable window titlebars. This can be done by adding the following line to your config. + +``` +# You can also use any non-zero value if you'd like to have a border +for_window [class=".*"] border pixel 0 +``` + +Gaps are the namesake feature of i3-gaps and add spacing between windows/containers. Gaps come in two flavors, inner and outer gaps wherein inner gaps are those between two adjacent containers (or a container and an edge) and outer gaps are an additional spacing along the screen edges. Gaps can be configured in your config either globally or per workspace, and can additionally be changed during runtime using commands (e.g., through `i3-msg`). + +*Note:* Outer gaps are added to the inner gaps, i.e., the gaps between a screen edge and a container will be the sum of outer and inner gaps. + +#### Configuration + +You can define gaps either globally or per workspace using the following syntax. Note that the gaps configurations should be ordered from least specific to most specific as some directives can overwrite others. + +``` +gaps [inner|outer|horizontal|vertical|top|left|bottom|right] +workspace gaps [inner|outer|horizontal|vertical|top|left|bottom|right] +``` + +The `inner` and `outer` keywords are as explained above. With `top`, `left`, `bottom` and `right` you can specify outer gaps on specific sides, and `horizontal` and `vertical` are shortcuts for the respective sides. `` stands for a numeric value in pixels and `` for either a workspace number or a workspace name. + +#### Commands + +Gaps can be modified at runtime with the following command syntax: + +``` +gaps inner|outer|horizontal|vertical|top|right|bottom|left current|all set|plus|minus|toggle + +# Examples +gaps inner all set 20 +gaps outer current plus 5 +gaps horizontal current plus 40 +gaps outer current toggle 60 +``` + +With `current` or `all` you can change gaps either for only the currently focused or all currently existing workspaces (note that this does not affect the global configuration itself). + +You can find an example configuration in the [wiki](https://github.com/Airblader/i3/wiki/Example-Configuration). + +### Smart Gaps + +Gaps can be automatically turned on/off on a workspace in certain scenarios using the following config directives: + +``` +# Only enable gaps on a workspace when there is at least one container +smart_gaps on + +# Only enable outer gaps when there is exactly one container +smart_gaps inverse_outer +``` + +### Smart Borders + +Smart borders will draw borders on windows only if there is more than one window in a workspace. This feature can also be enabled only if the gap size between window and screen edge is `0`. + +``` +# Activate smart borders (always) +smart_borders on + +# Activate smart borders (only when there are effectively no gaps) +smart_borders no_gaps +``` + +### Smart Edge Borders + +This extends i3's `hide_edge_borders` with a new option. When set, edge-specific borders of a container will be hidden if it's the only container on the workspace and the gaps to the screen edge is `0`. + +``` +# Hide edge borders only if there is one window with no gaps +hide_edge_borders smart_no_gaps +``` + +## i3bar + +### Bar Height + +The height of an i3bar instance can be specified explicitly by defining the `height` key in the bar configuration. If not set, the height will be calculated automatically depending on the font size. + +``` +bar { + # Height in pixels + height 25 +} +``` diff --git a/README.md b/README.md index d94d62c6f..4300aaf08 100644 --- a/README.md +++ b/README.md @@ -1,114 +1,15 @@ -[![Travis](https://img.shields.io/travis/Airblader/i3.svg)](https://travis-ci.org/Airblader/i3) -[![Issues](https://img.shields.io/github/issues/Airblader/i3.svg)](https://github.com/Airblader/i3/issues) -[![Forks](https://img.shields.io/github/forks/Airblader/i3.svg)](https://github.com/Airblader/i3/network) -[![Stars](https://img.shields.io/github/stars/Airblader/i3.svg)](https://github.com/Airblader/i3/stargazers) +# i3-gaps-desktop -# i3-gaps +This is a fork of [i3-gaps](README.gaps.md) window manager that supports desktop +manager windows (those of type `_NET_WM_WINDOW_TYPE_DESKTOP`). This includes +SpaceFM, PCmanFM, and other desktop managers. -## What is i3-gaps? +The patch adding their support has originally been written by Alessandro Grassi +and published in the [i3-extras](https://github.com/ashinkarov/i3-extras) repo. +I've ported it to the latest i3-gaps. -i3-gaps is a fork of [i3wm](https://www.i3wm.org), a tiling window manager for X11. It is kept up to date with upstream, adding a few additional features such as gaps between windows (see below for a complete list). +See [the original patch README](README.desktop.md). -![i3](http://i.imgur.com/y8sZE6o.jpg) +## Copying -## How do I install i3-gaps? - -Please refer to the [wiki](https://github.com/Airblader/i3/wiki/installation). - -## Where can I get help? - -For bug reports or feature requests regarding i3-gaps specifically, open an issue on [GitHub](https://www.github.com/Airblader/i3). If your issue is with core i3 functionality, please report it [upstream](https://www.github.com/i3/i3). - -For support & all other kinds of questions, you can ask your question on the official [subreddit /r/i3wm](https://www.reddit.com/r/i3wm). - -# Features - -## i3 - -### gaps - -*Note:* In order to use gaps you need to disable window titlebars. This can be done by adding the following line to your config. - -``` -# You can also use any non-zero value if you'd like to have a border -for_window [class=".*"] border pixel 0 -``` - -Gaps are the namesake feature of i3-gaps and add spacing between windows/containers. Gaps come in two flavors, inner and outer gaps wherein inner gaps are those between two adjacent containers (or a container and an edge) and outer gaps are an additional spacing along the screen edges. Gaps can be configured in your config either globally or per workspace, and can additionally be changed during runtime using commands (e.g., through `i3-msg`). - -*Note:* Outer gaps are added to the inner gaps, i.e., the gaps between a screen edge and a container will be the sum of outer and inner gaps. - -#### Configuration - -You can define gaps either globally or per workspace using the following syntax. Note that the gaps configurations should be ordered from least specific to most specific as some directives can overwrite others. - -``` -gaps [inner|outer|horizontal|vertical|top|left|bottom|right] -workspace gaps [inner|outer|horizontal|vertical|top|left|bottom|right] -``` - -The `inner` and `outer` keywords are as explained above. With `top`, `left`, `bottom` and `right` you can specify outer gaps on specific sides, and `horizontal` and `vertical` are shortcuts for the respective sides. `` stands for a numeric value in pixels and `` for either a workspace number or a workspace name. - -#### Commands - -Gaps can be modified at runtime with the following command syntax: - -``` -gaps inner|outer|horizontal|vertical|top|right|bottom|left current|all set|plus|minus|toggle - -# Examples -gaps inner all set 20 -gaps outer current plus 5 -gaps horizontal current plus 40 -gaps outer current toggle 60 -``` - -With `current` or `all` you can change gaps either for only the currently focused or all currently existing workspaces (note that this does not affect the global configuration itself). - -You can find an example configuration in the [wiki](https://github.com/Airblader/i3/wiki/Example-Configuration). - -### Smart Gaps - -Gaps can be automatically turned on/off on a workspace in certain scenarios using the following config directives: - -``` -# Only enable gaps on a workspace when there is at least one container -smart_gaps on - -# Only enable outer gaps when there is exactly one container -smart_gaps inverse_outer -``` - -### Smart Borders - -Smart borders will draw borders on windows only if there is more than one window in a workspace. This feature can also be enabled only if the gap size between window and screen edge is `0`. - -``` -# Activate smart borders (always) -smart_borders on - -# Activate smart borders (only when there are effectively no gaps) -smart_borders no_gaps -``` - -### Smart Edge Borders - -This extends i3's `hide_edge_borders` with a new option. When set, edge-specific borders of a container will be hidden if it's the only container on the workspace and the gaps to the screen edge is `0`. - -``` -# Hide edge borders only if there is one window with no gaps -hide_edge_borders smart_no_gaps -``` - -## i3bar - -### Bar Height - -The height of an i3bar instance can be specified explicitly by defining the `height` key in the bar configuration. If not set, the height will be calculated automatically depending on the font size. - -``` -bar { - # Height in pixels - height 25 -} -``` +See [LICENSE](LICENSE). diff --git a/include/i3-atoms_NET_SUPPORTED.xmacro.h b/include/i3-atoms_NET_SUPPORTED.xmacro.h index b491da98a..3c701464f 100644 --- a/include/i3-atoms_NET_SUPPORTED.xmacro.h +++ b/include/i3-atoms_NET_SUPPORTED.xmacro.h @@ -15,6 +15,7 @@ xmacro(_NET_WM_STATE) \ xmacro(_NET_WM_WINDOW_TYPE) \ xmacro(_NET_WM_WINDOW_TYPE_NORMAL) \ xmacro(_NET_WM_WINDOW_TYPE_DOCK) \ +xmacro(_NET_WM_WINDOW_TYPE_DESKTOP) \ xmacro(_NET_WM_WINDOW_TYPE_DIALOG) \ xmacro(_NET_WM_WINDOW_TYPE_UTILITY) \ xmacro(_NET_WM_WINDOW_TYPE_TOOLBAR) \ diff --git a/include/x.h b/include/x.h index d01709ed5..ddb4b86fd 100644 --- a/include/x.h +++ b/include/x.h @@ -15,6 +15,9 @@ /** Stores the X11 window ID of the currently focused window */ extern xcb_window_t focused_id; +/** Stores window ID of the desktop window */ +extern xcb_window_t desktop_window; + /** * Initializes the X11 part for the given container. Called exactly once for * every container from con_new(). diff --git a/src/click.c b/src/click.c index 7a2c0329f..40edc1f58 100644 --- a/src/click.c +++ b/src/click.c @@ -350,7 +350,13 @@ void handle_button_press(xcb_button_press_event_t *event) { return; } - ELOG("Clicked into unknown window?!\n"); + if (desktop_window != XCB_NONE && event->event == desktop_window) { + Con *ws = con_get_workspace(focused); + con_focus(ws); + return route_click(ws, event, mod_pressed, CLICK_INSIDE); + } else { + ELOG("Clicked into unknown window?!\n"); + } xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time); xcb_flush(conn); return; diff --git a/src/commands.c b/src/commands.c index 26ea876b4..7bbd6de77 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1331,6 +1331,13 @@ void cmd_focus_window_mode(I3_CMD, const char *window_mode) { Con *ws = con_get_workspace(focused); Con *current; bool success = false; + + if (strcmp(window_mode, "tiling") == 0 && TAILQ_EMPTY(&(ws->nodes_head)) && desktop_window != XCB_NONE) { + con_focus(ws); + success = true; + goto end; + } + TAILQ_FOREACH (current, &(ws->focus_head), focused) { if ((to_floating && current->type != CT_FLOATING_CON) || (!to_floating && current->type == CT_FLOATING_CON)) @@ -1341,6 +1348,7 @@ void cmd_focus_window_mode(I3_CMD, const char *window_mode) { break; } +end: if (success) { cmd_output->needs_tree_render = true; ysuccess(true); diff --git a/src/con.c b/src/con.c index 22f87c158..445d29001 100644 --- a/src/con.c +++ b/src/con.c @@ -1431,6 +1431,9 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool return; } + if (focused->type == CT_WORKSPACE && desktop_window != XCB_NONE) + return; + Con *target = con_descend_focused(workspace); _con_move_to_con(con, target, true, fix_coordinates, dont_warp, ignore_focus, true); } diff --git a/src/handlers.c b/src/handlers.c index eba5fe297..5cffea1d9 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -147,6 +147,21 @@ static void handle_enter_notify(xcb_enter_notify_event_t *event) { enter_child = true; } + if (desktop_window != XCB_NONE && event->event == desktop_window) { + /* this check stops the desktop window from stealing the focus + * when a floating windw is focused through a command/keybinding but + * the pointer is still on the desktop window */ + if (event->detail == XCB_NOTIFY_DETAIL_NONLINEAR) { + if (config.disable_focus_follows_mouse == false) { + Con *ws = con_get_workspace(focused); + con_focus(ws); + tree_render(); + } + } else { + return; + } + } + /* If we cannot find the container, the user moved their cursor to the root * window. In this case and if they used it to a dock, we need to focus the * workspace on the correct output. */ diff --git a/src/manage.c b/src/manage.c index 82c0ff851..2e1f565e1 100644 --- a/src/manage.c +++ b/src/manage.c @@ -235,6 +235,17 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki /* read the preferred _NET_WM_WINDOW_TYPE atom */ cwindow->window_type = xcb_get_preferred_window_type(type_reply); + /* map _NET_WM_WINDOW_TYPE_DESKTOP windows and receive focus events but + * don't manage them */ + if (xcb_reply_contains_atom(type_reply, A__NET_WM_WINDOW_TYPE_DESKTOP)) { + desktop_window = window; + xcb_map_window(conn, window); + values[0] = XCB_EVENT_MASK_ENTER_WINDOW; + xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, values); + xcb_flush(conn); + goto geom_out; + } + /* Where to start searching for a container that swallows the new one? */ Con *search_at = croot; diff --git a/src/tree.c b/src/tree.c index dad205a53..c7e200624 100644 --- a/src/tree.c +++ b/src/tree.c @@ -14,6 +14,8 @@ struct Con *focused; struct all_cons_head all_cons = TAILQ_HEAD_INITIALIZER(all_cons); +xcb_window_t desktop_window = XCB_NONE; + /* * Create the pseudo-output __i3. Output-independent workspaces such as * __i3_scratch will live there. diff --git a/src/x.c b/src/x.c index 48af5f374..80e8efd0d 100644 --- a/src/x.c +++ b/src/x.c @@ -492,6 +492,8 @@ void x_draw_decoration(Con *con) { /* find out which colors to use */ if (con->urgent) p->color = &config.client.urgent; + else if (focused->type == CT_WORKSPACE && desktop_window != XCB_NONE) + p->color = &config.client.focused_inactive; else if (con == focused || con_inside_focused(con)) p->color = &config.client.focused; else if (con == TAILQ_FIRST(&(parent->focus_head))) @@ -1275,6 +1277,12 @@ void x_push_changes(Con *con) { if (focused->window != NULL) to_focus = focused->window->id; + if (focused->type == CT_WORKSPACE && desktop_window != XCB_NONE) { + to_focus = desktop_window; + /* assume that the desktop window is mapped */ + focused->mapped = true; + } + if (focused_id != to_focus) { if (!focused->mapped) { DLOG("Not updating focus (to %p / %s), focused window is not mapped.\n", focused, focused->name);