i3 - Move container to next/previous ws (also if nonexisting) - window-managers

I'm running i3 version 4.16
I would like to bind a shortcut to move the current container to the next/previous workspace whether existing or not.
With help from this post using jq, I have a 'sort of' working solution but it is very flaky (sometimes it switches to the workspace without moving the container and sometimes it moves a container from the next ws to the current ws before switching ws)
Can anyone come up with a better solution ???
My i3 config:
set $ws-right "$(( $( i3-msg -t get_workspaces | jq '.[] | select(.focused).num' ) + 1))";
set $ws-left "$(( $( i3-msg -t get_workspaces | jq '.[] | select(.focused).num' ) - 1))";
bindsym shift+control+$mod+Left exec i3-msg move container to workspace $ws-left; exec i3-msg workspace $ws-left
bindsym shift+control+$mod+Right exec i3-msg move container to workspace $ws-right; exec i3-msg workspace $ws-right
Thx for your time ;)

Just for info
I ended up using a shell script to avoid asynchronously running the two commands
.i3MoveNext.sh:
wsNext=$(( $( i3-msg -t get_workspaces | jq '.[] | select(.focused).num' ) + $1))
i3-msg move container to workspace $wsNext
i3-msg workspace $wsNext
and then bind the preferred key combo like:
bindsym shift+control+$mod+Left exec ~/.i3MoveNext.sh -1
bindsym shift+control+$mod+Right exec ~/.i3MoveNext.sh 1

You can achieve this result also by using native i3 messages, such as:
bindsym $mod+Shift+n move container to workspace next_on_output; workspace next_on_output
bindsym $mod+Shift+p move container to workspace prev_on_output; workspace prev_on_output
The advantage of this solution is, that it works per display and also if your display names have icons in it or different names.
Your script could also be improved like this:
wsNext=$(( $( i3-msg -t get_workspaces | jq '.[] | select(.focused).num' ) + $1))
echo i3-msg move container to workspace number $wsNext
echo i3-msg workspace number $wsNext
By using workspace number, your script also works for different workspace names.

Related

bspwm workspaces not working after activating other monitor

I'm using almost the default bspwmrcand sxhkdrc.
I'm working on a laptop and as far as I'm not connecting it to my monitor everything works just fine.
xrandr --output HDMI1 --mode 1920x1080 now breaks it. If I want to go to workspace one with Super+1 then nothing happens. Instead workspace one windows are now reachable on workspace two.
Thanks in advance!
You have to decide on xrandr output how many monitors are attached while you start BSPWM.
BSPWM is tiling window manager and it does its job very good.
start_from=1
monitors_connected=$(bspc query -M | wc -l)
per_monitor=$(( 10 / monitors_connected ))
for monitor in $(bspc query -M); do
bspc monitor $monitor -d $(seq $start_from $(( start_from + per_monitor - 1)))
start_from=$(( start_from + per_monitor ))
done
In your case, it will put first 5 workspaces on the first monitor and other 5 on external monitor.
The code above you can include inside your bspwmrc before you start some status bar program.
while pgrep -x polybar >/dev/null; do sleep 1; done
xrandr -q | awk '/ connected / {print $1}' | while read -r monitor _; do
polybar -r "$monitor" &
done
so polybar for every connected monitor will run. If there is only one monitor, nothing will happen.

How to detect if i3-wm is being run or GNOME is being run in bash

I've been using i3-wm for about six months now, and I had to switch to GNOME because Discord was crashing a lot in i3. I had previously used the i3-msg command in my bashrc to make sure the borders of the terminal wouldn't be visible, as to use the entire screen space for the terminal. The specific command I run is:
i3-msg -q border toggle
The problem is, when I use GNOME and I open up a terminal, the i3-msg command runs, and causes an error message evidently caused by the fact that i3 isn't running. The ideal scenario would be to add an if statement that checks if i3 is running, and if it is, then run the i3-msg command.
My question: What is the most convenient way to determine which window manager / Desktop Environment is currently running in my system?
When i3 is active, there should be a proces called "i3". You could check that with pgrep.
if pgrep -x "i3" > /dev/null
then
echo "i3 is running"
fi
-x is short for --exact – without it the if clause would still work, as long as no non-i3 process' name contains i3.
Omitting > /dev/null would print out the pid(s) found by pgrep.
Instead of pgrep you could also use pidof or ps -C. Instead of idiomatic if-then-fi you could also just use && like pidof i3 > /dev/null && echo "i3 is running" || echo "i3 is not running"

How to start up xmonad with apps on other workspaces

I'm trying to write an xmonad.hs which, at startup, launches some apps on some workspaces. Several of these apps (e.g., atop) will run within a terminal (urxvt being my preference).
This has been asked a few times before, e.g, here, here, and is obliquely touched on on the XMonad FAQ.
However, these rely on:
using spawnOn from XMonad.Actions.SpawnOn, which flat doesn't work (testing with urxvt, and also xclock as a simple example); it gets sent to the current workspace.
using spawn prog >> windows $ greedyView <workspace>, which kinda works, but has major timing issues - e.g., if you run two in succession, with different workspaces, both progs end up on the latter workspace. FWIW, I experimented with using threaddelay to assist; it didn't make any discernable difference, even with a 10s delay between spawns (I remembered that threadDelay is in microseconds, and so used 10000000).
rely on using general hooks for programs - meaning that whenever I start them up, they'll get sent to the given workspace. That's not what I want; I just want them placed there at startup.
Relatedly, it surprises me that the API doesn't let me start up an app and then give me a reference to that app/window (potentially with a timeout); so that I can confidently send that app/window to a workspace.
Any help would be greatly appreciated.
Install wmctrl
sudo apt install wmctrl
And create a script (in this example thunderbird on the second workspace (-t 1)):
#!/bin/sh
(thunderbird &) & sleep 5 &&
sh -c "wmctrl -i -r `wmctrl -l | grep Thunderbird` -t 1"
To know your application name on wmctrl you can view it by taping on your terminal :
wmctrl -l
And replace it with the correct name in the script.
Be carrefull with the capital letter ("Thunderbird" not "thunderbird") !!
Other example with firefox on the 3d workspace (-t 2):
#!/bin/sh
(firefox &) & sleep 5 &&
sh -c "wmctrl -i -r `wmctrl -l | grep Firefox` -t 2"
Bonus :
Here is the command to execute at start-up :
sh -c "thunderbird & sleep 5 && wmctrl -i -r `wmctrl -l | grep Thunderbird` -t 1"
Work on Debain 10 with Cinnamon. But should work for all
You can spawn the app via startupHook and then use a manageHook to handle window placement.
manageHook such as:
, className =? "deluge" --> doShift ( myWorkspaces !! 3 )
The above will actually spawn deluge on Workspace 4. In my startupHook I have
spawnOnce "deluge-gtk" to launch the app on start-up.
You would want to import XMonad.Util.SpawnOnce. doShift comes from the default XMonad.ManageHooks You could also take a look at XMonad.Actions.SpawnOn but I only use manageSpawn from that module.

Bash tool to put an icon on taskbar?

I am trying to create a tiny application on my Ubuntu machine. What I want to do is put to an icon on my taskbar beside the volume, and internet connectivity options. I understand that there is a notify-send command in bash that I can use, or even switch to Qt but that seems to be an overkill for the problem. Concretely, is there a way to create an icon on Ubuntu taskbar with bash, and change its color or text periodically?
For a very shell friendly way to quickly get tray apps working use yad (specifically yad --notification). It allows you to dynamically change icons, set click event handlers and build a custom context menu. For example:
yad --notification --command='echo hello world' --image=myicon.png
Will echo 'hello world' on click. Or:
yad --notification --command='echo hello world' --image=myicon.png --listen
Will read it's standard input waiting for commands to change icons, change visibility, open menu, trigger action and more.
More advanced example using yad.
Shows how to handle click in the same script and how to change the icon on the click. Also shows how to create a simple update loop and exit cleanly.
# stop on error (always good practice)
set -e
# create a FIFO file, used to manage the I/O redirection from shell
PIPE=$(mktemp -u --tmpdir ${0##*/}.XXXXXXXX)
mkfifo $PIPE
export PIPE
# attach a file descriptor to the file
exec 3<> $PIPE
# add handler to manage process shutdown
function on_exit() {
# send command to yad through pipe
echo "quit" >&3
rm -f $PIPE
}
trap on_exit EXIT
function update_icon() {
exec 3<> $PIPE # just in case
echo "icon:/path/to/some/icon" >&3
}
export -f update_icon
# add handler for tray icon left click
function on_click() {
exec 3<> $PIPE # required
echo "clicked"
echo "icon:/path/to/icon" >&3
update_icon
}
export -f on_click
# add handler for right click menu Quit entry function
on_quit() {
# signal to the script that it should end when this file is created
echo "quit" > ./quit.txt
exec 3<> $PIPE # required
echo "quit" >&3
}
export -f on_quit
# Use a file to indicate a quit command to the script
# Make sure it is gone before we start the program to avoid immediate exit
rm -f quit.txt
# create the notification icon with right click menu and Quit option
yad --notification \
--listen \
--image="/path/to/icon" \
--text="demo tray icon" \
--menu="Quit!bash -c on_quit" \
--no-middle \
--command="bash -c on_click" <&3 &
# allow user to end the loop from icon right click Quit menu option
while [ ! -f "quit.txt" ]; do
#echo "Press [CTRL+C] to stop.."
update_icon
sleep 60
done
# clean up after quit
rm quit.txt
Based on:
https://code.google.com/archive/p/yad/wikis/NotificationIcon.wiki
https://sourceforge.net/p/yad-dialog/wiki/NotificationIcon/
https://groups.google.com/g/yad-common/c/DT8q1vJ-yOQ
I'm not a bash expert, so fixes are welcome.
yad is available for many distros as a standard package. On Debian for example it can be installed by apt install yad.
Take a look at alltray; maybe it's your solution.
From its website:
Description
With AllTray you can dock any application with no native tray icon (like Evolution, Thunderbird, Terminals) into the system tray. A high-light feature is that a click on the "close" button will minimize back to system tray. It works well with Gnome, KDE, XFCE 4*, Fluxbox* and WindowMaker*. Xmms is supported in particular.
Options
These programs follow the usual GNU command line syntax, with long
options starting with two dashes ('-'). A summary of options is
included below.
-h --help | Show summary of options.
-v --version | Show version of program.
-d --debug | Show debug messages.
-s --show | Do not hide window after start.
-i --icon | Use a PNG image as an icon.
-l --large_icons | allow large icons (> 24x24).
-st --sticky | visible on all workspaces.
-x --borderless | Remove windows decorations border, title, frame... from parent.
-m --menu | Add entry "menu text:command" to popdown menu.
-t --title | Show title change for seconds. Probably most usefull for xmms.
-g --geometry | initial position. see man X.
Installation (command in a terminal)
- in Ubuntu:
sudo apt-get install alltray
- in Fedora:
sudo dnf install alltray
Cheers

Can tmux pane remote ssh-connection

Can I run tmux locally and connect via ssh to remote machine.. and after that any new pane and/or screen to be with the remote-machine-shell...
What I'm saying I can't install tmux on the remote machine, but I don't want to do a ssh connection from every pane, but ssh-login just once.
Is such thing possible..
thanks
If you want to login just once, you can use ControlMaster feature of ssh. Add some config like this to your ~/.ssh/config:
ControlMaster auto
ControlPath /tmp/ssh_mux_%h_%p_%r
If you login to the same server (as the same user) multiple times (either in one tmux or not), ssh will reuse the connection so that you don't need to make connection and login again.
lilydjwg explained something I never really understood before. Knowing about the ControlMaster setting makes the following much more reasonable, as it simplifies making multiple ssh connections. You only need to authenticate once, and the remote host doesn't need to have an sshd process running for each connection.
In your .tmux.conf file:
# What host do you usually log in to?
# We'll ssh there by default each time a new window or pane is opened.
REMOTE_HOST=your.usual.host
set-option -g default-command "ssh $REMOTE_HOST"
# Simple interface to change which host is connected to when you create
# a new window or pane.
bind-key C-h command-prompt -p "Set remote host: " -I $REMOTE_HOST "set-option default-command 'ssh %%'"
# In case you really do want a new window with a local shell.
bind-key C new-window ""
I don't think tmux can. One workaround would be to add something like this to tmux.conf.
bind-key X new-window "ssh HOST"
Then new windows would start at the remote host.
I am using tmux 1.8 and did not find a built-in solution. These workarounds fit at least for my common use cases:
Capture the full pane content and search for the last ssh command in it (I use the knowledge about the ending of my prompt to detect the command more or less reliably)
If this fails I check the command the pane might have been created with by using the shell-command option of tmux new-window or split-window commands
My reconnect.sh script looks like this. The most dirty thing about it is the way to get the last ssh command from the buffer. Up to now "> ssh " was enough for my situations to reliably detect a line containing a ssh connection request but any better solution would be appreciated.
#!/bin/bash
# #TODO: change this according to your own prompt
# This is used to find lines connect ssh command in the pane buffer
PROMPT_SEPARATOR="> "
# get current pane buffer size and dimensions
HISTORY_LIMIT=`tmux display-message -p "#{history_limit}"`
VISIBLE_LINES=`tmux display-message -p "#{pane_height}"`
# search last ssh command in pane content
LINE=`tmux capture-pane -p -J -S -$HISTORY_LIMIT -E $VISIBLE_LINES | grep "${PROMPT_SEPARATOR}ssh " | tail -1`
if [ -n "$LINE" ]; then
echo $LINE | sed "s/.*$PROMPT_SEPARATOR//;"
else
# fall back to the command that might have been used to create the pane
# (not necessarily ssh but helpful anyway)
tmux list-panes -F "#{pane_active} #{pane_start_command}" | grep "^1 " | tail -1 | cut -d ' ' -f2-
fi
I saved this script in my ~/.tmux directory and changed key bindings for various split-window and new-window shortcuts in my .tmux.conf similar to this:
# try to reconnect to remote host when creating new window
bind c run-shell 'CMD=`~/.tmux/reconnect.sh`; tmux new-window "$CMD"'

Resources