wezterm icon indicating copy to clipboard operation
wezterm copied to clipboard

Dropdown/Guake/Visor/hotkey terminal feature

Open halfelf opened this issue 2 years ago • 13 comments

Is your feature request related to a problem? Please describe. The so-called "dropdown" terminal window can be activated or deactivated by a single keystroke. Many terminal emulators have integrated this feature, for example, KDE's yakuake, quake style guake in Gnome, tilda. Also iTerm2 could set to this mode by enable a system wide hotkey in config.

And here's a short video shows how this feature works in xfce-terminal.

This is a so important feature for terminal emulator that there is even an independent project to help people to get it.

Describe the solution you'd like A new key assignment:

keys = {
    # press F1 to popup or hide wezterm window
    {key="F1", mods="", action="ToggleDropdown"}
  }

halfelf avatar Mar 23 '22 05:03 halfelf

Related:

  • https://github.com/wez/wezterm/issues/1443
  • https://github.com/wez/wezterm/issues/1308

wez avatar Mar 23 '22 14:03 wez

any news about this?

catalin560 avatar Apr 05 '22 06:04 catalin560

If there was news it would be here, and mentioned in the ChangeLog

wez avatar Apr 05 '22 14:04 wez

Bump, I'm really waiting for this feature too

prgres avatar Jul 07 '22 09:07 prgres

Folks, I have limited time for hacking on things that I don't or won't use. This is one of those features. I spend my available time how I want to spend it because it is my time, not yours! If you thumbs-down a simple statement about where you would find information about progress, you are showing a level of entitlement that simply demotivates me from wanting to work on it. "Bump" comments are in a similar category.

If you'd like to see this implemented, by all means thumb-up the original post at the top; that's useful when it comes to triaging issues because I can see a simple counter without having to scan the issues.

Keep in mind that there is only one of me and I'm doing this for fun. When you are toxic and entitled you make this not fun and I'd rather do something else with my time.

If you're really motivated to see this feature get implemented, this is open source and I'd love to help shepherd a contributor through a PR.

wez avatar Jul 07 '22 14:07 wez

working linux XOrg setup:

~/.config/wezterm/wezterm.lua

local wezterm = require 'wezterm';

wezterm.on("format-window-title", function(tab, pane, tabs, panes, config)
  local zoomed = ""
  if tab.active_pane.is_zoomed then
    zoomed = "[Z] "
  end

  local index = ""
  if #tabs > 1 then
    index = string.format("[%d/%d] ", tab.tab_index + 1, #tabs)
  end

  return "wm_wezterm: " .. zoomed .. index .. tab.active_pane.title
end)

/any/location/script.sh

#!/usr/bin/env bash

TERMINAL_BIN="${1}"
NAME_WINDOW_SEARCH="${2}"

function toggle() {
  local WINDOW_ID=$(xdotool search --name "${NAME_WINDOW_SEARCH}")

  if [[ ${#WINDOW_ID} -gt 0 ]]; then
    xprop -id "${WINDOW_ID}" | grep _NET_WM_STATE_FOCUSED 1>/dev/null
    if [[ $? -eq 0 ]]; then
      # echo "Focused->Hidden"
      xdotool windowminimize "${WINDOW_ID}"
    else
      # echo "Hidden/Unfocused->Focused"
      xdotool windowactivate "${WINDOW_ID}"
    fi
  else
    sh -c "${TERMINAL_BIN}" &
  fi
}

toggle

I am using KDE, so I created a global shortcut which executes script.sh "wezterm" "wm_wezterm" every time the keyboard shortcut is pressed.

beckend avatar Jul 07 '22 15:07 beckend

Bump, I'm really waiting for this feature too

sadly, it doesn't look like it will be implemented any time soon 😢 I'm sticking with Yakuake or Guake on Linux and Windows Terminal which has a dropdown mode.

catalin560 avatar Jul 07 '22 16:07 catalin560

@beckend neat! Have you experimented with wezterm start --class to set a different window class? That might help you avoid needing to change the window title

wez avatar Jul 07 '22 18:07 wez

--class hello, Tried it now, the --class seems not to work, rather I have no idea what it does.

wmctrl -l
...
0x0180001d -1 workstation-1.localdomain.internal Plasma
0x06e00003  0                                N/A wmctrl -l ~

I always get N/A on wezterm, that is why I had to use the title functionality and enforce a consistent title to match. This is the output as expected from the title callback:

wmctrl -l
0x05e00003  0                                N/A wm_wezterm: wmctrl -l ~

beckend avatar Jul 08 '22 06:07 beckend

Probably something wrong wih wmctrl? I was able to get the classname with both xdotool and xprop. Manipulating the window with xdo using the class name worked too.

MuhammedZakir avatar Jul 08 '22 08:07 MuhammedZakir

So here is how I handle this... I use wezterm as a status bar/drop down/quake console. Screenshot_2022-07-06-12-37-59_1920x1080

Screenshot_2022-07-08-09-12-37_1920x1080

This script launches my wezterms with specific class names and specific configurations bspterm

this script is used like this

  • wezbar collapse to collapse the bar
  • wezbar expand to expand it to about 40% (see the script)
  • wezbar runline focuses the bar without expanding to use it as a run/launcher

wezbar

example keybindings for wezbar to expand/collapse/runline/launch etc...

##---------- Keybindings for bspwm ----------##

# Terminal (wez)
super + Return
	bspterm

# Terminal (wezbar)
super + shift + Return
	bspterm -f

# Terminal (wez fullscreen'd)
super + t
	bspterm -s	

super + alt + b
    ~/.local/bin/wezbar collapse
super + alt + n
    ~/.local/bin/wezbar expand
super + alt + r
    ~/.local/bin/wezbar runline
super + M
    ~/.local/bin/wezmodal zsh
super + S 
    bspterm -m
super + V 
    ~/.local/bin/wezmodal fzf-clipcat

bspwmrc snippet for the rules applied....

bspc rule -a wezterm desktop='^1' follow=on focus=on
bspc rule -a wezbar desktop='^1' rectangle=1920x40+0+0 manage=off state=floating border=off
bspc rule -a wezssh desktop='^3' rectangle=1884x982+16+16 
bspc rule -a wezdebug desktop='^7' rectangle=1884x982+16+16 
# bspc rule -a wezmodal state=floating manage=off
bspc rule -a wezmodal rectangle=800x600+500+300 border=off follow=on sticky=on focus=on center=on state=floating

and my wezterm configs (beware it is a fucking mess because i haven't cleaned it and im always hacking on shit) .config/wezterm

but it works

digitallyserviced avatar Jul 09 '22 01:07 digitallyserviced

This is my Swaywm solution to toggle wezterm visibility by pressing F1

~/.config/sway/config

set {
  $term_id   quake_term
  $term      wezterm start --class $term_id
  $t_pos     border none, move position center, resize set width 100 ppt height 100 ppt
}

for_window {
  [app_id=$term_id] {
    move to scratchpad;
    [app_id=$term_id] scratchpad show;
    $t_pos
  }
}

bindsym --to-code {
  # Scratchpad bindsym
  ## Main terminal
  F1 exec swaymsg [app_id=$term_id]   scratchpad show || exec $term ,  $t_pos
}

exec $term

Crandel avatar Aug 15 '22 13:08 Crandel

tdrop also works. My wezterm has a unix domain configured

-- in ~/.config/wezterm/wezterm.lua
unix_domains = {
  {
    name = 'dropdown',
  }
},

, and it'll be connected to it when it was triggered by a hotkey handled by sxhkd.

# in ~/.config/sxhkd/sxhkdrc
F11
  tdrop -mat -w -4 -h -38 --pre-unmap-hook='wmctrl -ir $wid -b remove,maximized_vert; xdotool windowsize $wid $width 10%' --class 'wezterm.dropdown' -f 'connect dropdown --class wezterm.dropdown' wezterm

F12
  tdrop -mat -w -4 -h 45% --pre-unmap-hook='wmctrl -ir $wid -b remove,maximized_vert; xdotool windowsize $wid $width 10%' --class 'wezterm.dropdown' -f 'connect dropdown --class wezterm.dropdown' wezterm

I have the --pre-unmap-hook arg since tdrop doesn't seem to work nicely with a snapping behavior and/or maximize/unmaximize behavior of the WM I use, so you may be able to just do tdrop -mat -w -4 -h -38 --class 'wezterm.dropdown' -f 'connect dropdown --class wezterm.dropdown' wezterm instead.

MiSawa avatar Sep 18 '22 13:09 MiSawa

Found a solution for mac, but not perfect.

Open Automator app, create a Quick Action task, with no input, and applying for all Applications.

Drag AppleScript to work flow and edit it like this:

tell application "WezTerm"
	activate
end tell

Save and give it a unique name like activate_wezterm. Generally this will add a service item for all apps under their service menu.

Then, open System Preferences, click Keyboard, then click Shortcuts.

Select App Shortcuts on the left, click the Add(+) button. In the Menu Title field, type activate_wezterm or the name you choose before, set a shortcut for it and all done.

To hide the front most window is similar and I just omit the procedure here.

Cons:

  • The global shortcut could be conflict with some application's internal settings, e.g. Firefox has F2 as edit html, which will override our setting if we set F2 as a global shortcut.
  • The global shortcut has a significant delay compared to iTerm2's toggle hotkey. I guess about 200-300ms. (I'm using 2021 MBP, M1Max)

halfelf avatar Sep 23 '22 07:09 halfelf

@halfelf, thank you for the workaround.

I followed the steps you described and after I press the shortcut keys, it switches me to an already openned WezTerm window or it will open a new one. In my case I configured WezTerm to open only on a specific virtual desktop, this means the shortcut will just switch to desktop 2 where the terminal is running.

This behaviour is different from the one described in this issue. Am I doing something wrong?

Thanks!

danielnbalasoiu avatar Sep 23 '22 08:09 danielnbalasoiu

@danielnbalasoiu I believe you're on the right track. I haven't tested it on a virtual desktop yet, and indeed I have seen some people talked about activate command has a strange behavior when I trying to solve this problem.

You could try this script or another one to focus on a window. And this may help locate desktop.

halfelf avatar Sep 23 '22 09:09 halfelf

If there are any yabai, skhd, and fish shell users on macOS - I wrote this quick script to quickly show and hide WezTerm (not quite Quake style as it still follows the tiling structure but you can customize it from here).

It uses an extra space as a hidden one (I use only 3 spaces, so 4 is hidden for me).

#!/usr/bin/env fish

set hidden_space 4

function main -a app
	set window (yabai -m query --windows | jq --arg app $app '.[] | select(.app==$app)')
	set is_visible (echo $window | jq '."is-visible"')
	set id (echo $window | jq '.id')

	if test -z "$is_visible"
		osascript \
			-e "on run (argv)" \
			-e "tell application (item 1 of argv) to activate" \
			-e "end" \
			-- "$app"
	else if $is_visible
		yabai -m window $id --space $hidden_space
		echo "$app hidden"
	else
		yabai -m window $id --space mouse && yabai -m window --focus $id
		echo "$app revealed"
	end
end

main $argv

and then in skhdrc:

alt - return: ~/.config/scripts/dropdown.fish WezTerm

keturiosakys avatar Oct 27 '22 22:10 keturiosakys

As a temporary workaround on macOS, I highly recommend using hammerspoon.

This will allow you to target any app with a key combination and either spawn it or show it.

Something as easy as this:

hs.hotkey.bind({"shift", "alt", "ctrl"}, "A", function()
  hs.application.launchOrFocus("Wezterm")
end)

I haven't tried that yet, but I'm sure you could use some launch arguments for position and name to spawn a separate Wezterm entity.

cd-a avatar Apr 06 '23 05:04 cd-a

@cd-a Thanks a lot! Hammerspoon responses keystroke much faster than my poor applescript.

For a classic dropdown behavior, I have this conf and tested:

hs.hotkey.bind({}, "F2", function()
    wez = hs.application.find("Wezterm")
    if wez then
        if wez:isFrontmost() then
            wez:hide()
        else
            wez:activate()
        end
    end
end)

halfelf avatar Apr 12 '23 02:04 halfelf

+1. Guake is the best at the moment with the tmux like splits, gui configurator, and quake functionality but it's. so. slow.

noahbliss avatar Jun 16 '23 23:06 noahbliss

For anyone using Karabiner, here's a complex modification config using ctrl+esc

gist link

{
  "title": "Quake-style Hotkey Window for Wezterm",
  "rules": [
    {
      "description": "Launch/show Wezterm if it is not in foreground",
      "manipulators": [
        {
          "type": "basic",
          "from": {
            "key_code": "escape",
            "modifiers": {
              "mandatory": ["control"]
            }
          },
          "to": [
            {
              "shell_command": "open '/Applications/WezTerm.app'"
            }
          ],
          "conditions": [
            {
              "type": "frontmost_application_unless",
              "bundle_identifiers": ["^com\\.github\\.wez\\.wezterm$"]
            }
          ]
        }
      ]
    },
    {
      "description": "Minimize Wezterm if it is in foreground",
      "manipulators": [
        {
          "type": "basic",
          "from": {
            "key_code": "escape",
            "modifiers": {
              "mandatory": ["control"]
            }
          },
          "to": [
            {
              "key_code": "h",
              "modifiers": ["command"]
            }
          ],
          "conditions": [
            {
              "type": "frontmost_application_if",
              "bundle_identifiers": ["^com\\.github\\.wez\\.wezterm$"]
            }
          ]
        }
      ]
    }
  ]
}

svallory avatar Sep 17 '23 01:09 svallory

Thank you to everyone for offering creative workarounds. Thank you @wez for creating this cool app!

jmweir avatar Oct 12 '23 18:10 jmweir

In Windows, script of AutoHotKey will works like Quake Mode. I tested below script on AutoHotKey v2+:

#SingleInstance Force
#Warn

; Load a few environment variables
userprofile := EnvGet("USERPROFILE")

; read config file
configFile := userprofile . "\.QuakeLikeConsole.ini"
LoadConfig()

; Tray icon customization
A_IconTip := "Quake Like Console (key: " . key . ")"
Tray:= A_TrayMenu
Tray.Delete() ; remove standard Menu items
Tray.Add("Quake Like Console", Dummy)
Tray.Add("") ; separator
Tray.Add("&Suspend", ToggleSuspend)
Tray.Add("&Open config file", OpenConfig)
Tray.Add("") ; separator
Tray.Add("E&xit", ButtonExit)
Return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Handle Hotkey events
ToggleConsole(ThisHotkey)
{
    dim := GetActiveMonitorDimention()
    ;MsgBox( "monIndex: " . dim.index . " x: " . dim.x . " y: " . dim.y . " width: " . dim.width . " height: " . dim.height)
    DetectHiddenWindows(true)
    local hwnd_id := WinExist(windowMatcher)
    if (hwnd_id != 0) {
        DetectHiddenWindows(false)
        if WinActive() {
            WinHide()
        } else {
            DetectHiddenWindows(true)
            ; show window on top of monitor with your mouse cursor.
            ; window size set to width (1.0 * monitor pixel width) x height (heightRatio * monitor pixel height)
            WinMove(dim.x, dim.y, dim.width, dim.height * Float(heightRatio), hwnd_id)
            WinShow()
            WinActivate()
        }
    } else {
        Try {
            Run(command, workingDir)  ; see http://ahkscript.org/docs/commands/Run.htm
        } Catch Error as e {
            TrayTip("Could not execute command", command, 3)
            Throw e
        }
    }
    Return
}

GetActiveMonitorDimention()
{
    dim := {index: 0, x: 0, y: 0, width: 0, height: 0}
    CoordMode("Mouse", "Screen")
    MouseGetPos(&x, &y)
    local count := MonitorGetCount()
    Loop count {
        MonitorGet(A_Index, &Left, &Top, &Right, &Bottom)
        if (x >= Left) && (x <= Right) && (y <= Bottom) && (y >= Top) {
            dim.index := A_Index
            dim.x := Left
            dim.y := Top
            dim.width := Abs(Right - Left)
            dim.height := Abs(Top - Bottom)
        }
    } Until (dim.width > 0)
    return dim
}

LoadConfig() {
    global key, configFile, command, heightRatio, workingDir, windowMatcher, userprofile

    Try {
        Hotkey(key, "Toggle")
    }

    command := IniRead(configFile, "Settings", "command", A_Space)
    if (command = "") {
        command := userprofile . "\wezterm\wezterm-gui.exe"
        IniWrite(command, configFile, "Settings", "command")
    }

    workingDir := IniRead(configFile, "Settings", "workingDir", A_Space)
    if (workingDir = "") {
        workingDir := userprofile
        IniWrite(workingDir, configFile, "Settings", "workingDir")
    }

    windowMatcher := IniRead(configFile, "Settings", "windowMatcher", A_Space)
    if (windowMatcher = "") {
        windowMatcher := "ahk_exe wezterm-gui.exe"
        IniWrite(windowMatcher, configFile, "Settings", "windowMatcher")
    }

    key := IniRead(configFile, "Settings", "key", A_Space)
    if (key = "") {
        key := "^;"
        IniWrite(key, configFile, "Settings", "key")
    }

    heightRatio := IniRead(configFile, "Settings", "heightRatio", A_Space)
    if (heightRatio = "") {
        heightRatio := "0.6"
        IniWrite(heightRatio, configFile, "Settings", "heightRatio")
    }

    IniWrite("see https://www.autohotkey.com/docs/Hotkeys.htm", configFile, "Help", "key")
    IniWrite("see " . website, configFile, "Help", "windowMatcher")

    ; keyboard key (or key-combination) to toggle the console
    Try {
        Hotkey(key, ToggleConsole)
    } Catch Error {
        TrayTip("Invalid key", "using default: <C-;>", 3)
        key := "^;"
        IniWrite("^;", configFile, "Settings", "key")
        Hotkey(key, ToggleConsole)
    }
}


ButtonExit(A_ThisMenuItem, A_ThisMenuItemPos, MyMenu)
{
    ExitApp()
    Return
}


ToggleSuspend(A_ThisMenuItem, A_ThisMenuItemPos, MyMenu)
{
    Suspend(-1)
    Tray.ToggleCheck("&Suspend")
    Return
}


OpenConfig(A_ThisMenuItem, A_ThisMenuItemPos, MyMenu)
{
    RunWait(configFile)
    LoadConfig()
    Reload()
    Return
}


Dummy(A_ThisMenuItem, A_ThisMenuItemPos, MyMenu)
{
    Return
}

limitation: window will appear in primary monitor at first call.

Note: this script load configuration from %USERPROFILE%\.QuakeLikeConsole.ini. please configuration for your wezterm-gui.exe installation path and hot key (default is set to <C-;>)

orumin avatar Nov 15 '23 18:11 orumin

For the ones interested. Using AutoHotKeys v2 in windows 10 this works for me:

#SingleInstance Force
#`::ToggleApp()
ToggleApp() {
    AppClass := "org.wezfurlong.wezterm"

    ifWinActive, ahk_class %AppClass%
    {
         WinMinimize, ahk_class %AppClass%
    }
    else
    {
         WinActivate, ahk_class %AppClass%
    }
    IfWinNotExist, ahk_class %AppClass%
    {
        Run, "C:\Program Files\WezTerm\wezterm-gui.exe" ; Replace with the actual path to your application
    }
    return
}

calvinchoy avatar Nov 28 '23 22:11 calvinchoy

Someone has a script that works with Plasma Wayland? 😅

SuperSandro2000 avatar Dec 25 '23 23:12 SuperSandro2000

Someone has a script that works with Plasma Wayland? 😅

You can use mine. Works with KWin5 on Wayland. https://github.com/Xinkai/kwin-wezterm-toggle

Xinkai avatar Dec 26 '23 04:12 Xinkai

I got annoyed and whipped something together over my holiday break.

Qurop. While I intend for it to be an all-purpose solution for creating dropdown windows, I've only been testing it with Wezterm. It's very much v0.1.0 and has only been tested on KDE w/Xorg. It's close enough to my Yakuake config that I don't miss it anymore. I'd happily accept additional PRs to shore it up for additional DE's and even Wayland.

arusahni avatar Jan 01 '24 00:01 arusahni

This is the AutoHotkey script I'm using. Nothing fancy, but supports Windows virtual desktops:

; Show/hide wezterm
^AppsKey::
  if (WinActive("ahk_class org.wezfurlong.wezterm") = "0x0")
  {
    ; Hide and show window to make sure it opens on the current
    ; desktop. Otherwise it will open on the desktop where it was
    ; minimized and focus will be moved to that desktop.
    WinHide, ahk_class org.wezfurlong.wezterm
    WinShow, ahk_class org.wezfurlong.wezterm
    WinActivate, ahk_class org.wezfurlong.wezterm
  }
  else
  {
    WinMinimize, ahk_class org.wezfurlong.wezterm
    ; "Deactivate" wezterm by activating the taskbar so that the next
    ; hotkey press for wezterm will activate wezterm again instead of
    ; trying to minimize it.
    WinActivate, ahk_class Shell_TrayWnd
  }
return

peterjaric avatar Jan 08 '24 10:01 peterjaric

this is good

micsama avatar Jan 08 '24 16:01 micsama

I spent some time using hammerspoon to complete a script. You can try it out, and I think the effect is pretty good. I can continue to optimize it. It performs well on macos when Stage Manager is off.(if stage manager is on,maybe wezterm could become a floating window, then the effect would be better,)

hs.hotkey.bind({ "Alt" }, "`", function()
	wez = hs.application.find("Wezterm")
	if wez then
		if wez:isFrontmost() then
			wez:hide()
		else
			local nowspace = hs.spaces.focusedSpace()
			local screen = hs.screen.mainScreen()
			wez_window = wez:mainWindow()
			hs.spaces.moveWindowToSpace(wez_window, nowspace)
			local max = screen:fullFrame()
			local f = wez_window:frame()
			f.x = max.x
			f.y = max.y
			f.w = max.w
			f.h = max.h * 0.55
			hs.timer.doAfter(0.2, function()
				wez_window:setFrame(f)
			end)
			wez_window:focus()
		end
	end
end)

micsama avatar Jan 10 '24 04:01 micsama