kovidgoyal / kitty

Cross-platform, fast, feature-rich, GPU based terminal
https://sw.kovidgoyal.net/kitty/
GNU General Public License v3.0
22.51k stars 913 forks source link

Add pulldown mode like tilda, quake and other terminals. #45

Closed leiserfg closed 7 years ago

kovidgoyal commented 7 years ago

This is not possible to do in a cross-platform manner, as it depends on the details of the desktop environment/window manager. Really this function should be implemented in the window manager, so it can be performed with any window.

For instance, I use a tiling window manager, and I have a key binding to instantly bring up a terminal.

leiserfg commented 7 years ago

Self reply: https://github.com/gotbletu/shownotes/blob/master/any_term_dropdown.sh

ibrokemypie commented 7 years ago

also, tdrop works well

dbousamra commented 5 years ago

Does anyone have a method for OSX?

Edit: I have solved this using BetterTouchTool

NightMachinery commented 5 years ago

@dbousamra How did you do this with BTT?

zx1986 commented 5 years ago

@dbousamra Could you do this on https://github.com/jwilm/alacritty ???

laggardkernel commented 5 years ago

@zx1986 I recommend Alfred. Create a Hotkey workflow for any app you want to launch it or toggle visibility of it.

harryjubb commented 5 years ago

@NightMachinary

How did you do this with BTT?

I've got it working with a global keyboard trigger to "show/hide a specific application", but would be great to hear if anyone is doing it differently.

image

spejamchr commented 4 years ago

There's also https://github.com/koekeishiya/skhd

I build kitty in ~/git/other/kitty/, and start kitty with ctrl+return using this line in my .skhdrc

ctrl - return : open ~/git/other/kitty/kitty/launcher/kitty.app -n --args --single-instance --title="zsh"
mk12 commented 4 years ago

I tried many ways of doing this and eventually settled on Hammerspoon. I prefer it because it's free, flexible, easy to set up programmatically in my dotfiles, and fast. (That last point is important. For example, you can avoid installing external software and instead make a shortcut in System Preferences for an Automator "Service", but there's a second-long delay after pressing the keys.)

Putting this in ~/.hammerspoon/init.lua will show/hide kitty when you press CtrlSpace:

hs.hotkey.bind({"ctrl"}, "space", function()
    local app = hs.application.get("kitty")
    if app then
        if not app:mainWindow() then
            app:selectMenuItem({"kitty", "New OS window"})
        elseif app:isFrontmost() then
            app:hide()
        else
            app:activate()
        end
    else
        hs.application.launchOrFocus("kitty")
    end
end)

Or, if you want it to always open a new window:

hs.hotkey.bind({"ctrl"}, "space", function()
    local app = hs.application.get("kitty")
    if app then
        app:selectMenuItem({"kitty", "New OS window"})
    else
        hs.application.launchOrFocus("kitty")
    end
end)
exoticus commented 4 years ago

@mk12 Very nice this inspired me to make this which turns Kitty into a drop down quake and iTerm like style, i bound it to F15 which is the Break/Pause key because c+spc is frequently used to trigger auto complete in vscode and other editor. paired with hide_window_decorations yes in kitty.conf it makes for a very viable alternative to iTerm

hs.hotkey.bind({}, "F15", function()
  local app = hs.application.get("kitty")

  if app then
      if not app:mainWindow() then
          app:selectMenuItem({"kitty", "New OS window"})
      elseif app:isFrontmost() then
          app:hide()
      else
          app:activate()
      end
  else
      hs.application.launchOrFocus("kitty")
      app = hs.application.get("kitty")
  end

  app:mainWindow():moveToUnit'[100,50,0,0]'
  app:mainWindow().setShadows(false)
end)
webberwang commented 4 years ago

@NightMachinary

How did you do this with BTT?

I've got it working with a global keyboard trigger to "show/hide a specific application", but would be great to hear if anyone is doing it differently.

image

This doesn't seem to work if kitty is in legacy full screen mode

macos_traditional_fullscreen true

kovidgoyal commented 3 years ago

And specifically for i3: https://github.com/LandingEllipse/kitti3

Luflosi commented 3 years ago

I suggest adding it to integrations.rst.

denis-ryzhkov commented 3 years ago

Not a dropdown, but a very simple solution to run Kitty instead of default terminal via standard Ctrl+Alt+T shortcut:

This also allows to avoid manual desktop integration steps: https://sw.kovidgoyal.net/kitty/binary.html#desktop-integration-on-linux

seanaye commented 3 years ago

@webberwang @NightMachinary

Is there a way this will open over fullscreen apps in MacOS? I have the shortcut opening kitty in BTT but it switches me out of the 'space' if im in a fullscreen app

lukesmurray commented 3 years ago

I created a phoenix script which does this. But only in mac!

brew install --cask phoenix

copy the following file to ~/.phoenix.js

https://github.com/lukesmurray/bootstrap/blob/master/.phoenix.js

Use cmd+` (backtick) to get the dropdown.

These are the relevant lines. You can make your own shortcut

https://github.com/lukesmurray/bootstrap/blob/a4610f273dac7ca9fba3881a4fe74a9e4c20b699/.phoenix.js#L50-L57

typkrft commented 2 years ago

For those running yabai and skhd on macOS, this will give you a pretty good approximation of a drop down terminal. It binds CMD + `. It could probably be optimized and some yabai rules and signals could implement a little more logic and functionality, but it works well enough for me. Just drop this in your skhdrc and adjust to your liking.

# SKHD 
 default < cmd - 0x32 : /path/to/helper

# Helper File
# NOTE: Add a port if you control of that instance. 
function kitty_quake() {
  HOMEBREW=/opt/homebrew/bin
  QUAKE=($($HOMEBREW/yabai -m query --windows --space |jq '.[]|select(.title=="Quake")|.id,.minimized,.border'))

  if [[ ${#QUAKE} -eq 0 ]]; then
    open -a kitty.app -n --args -1 --title="Quake" -o \
      tab_title_template="Quake: {index}" --listen-on="tcp:localhost:$PORT" \
      &>/dev/null & disown; 
    while [[ ${#QUAKE[@]} -ne 3 ]];
      do
        QUAKE=($($HOMEBREW/yabai -m query --windows --space|jq '.[]|select(.title=="Quake")|.id,.minimized,.border'))
      done
    $HOMEBREW/yabai -m window "${QUAKE[0]}" --toggle sticky;
    $HOMEBREW/yabai -m window "${QUAKE[0]}" --move abs:0:0;
    $HOMEBREW/yabai -m window "${QUAKE[0]}" --resize abs:10000:450;
    $HOMEBREW/yabai -m window "${QUAKE[0]}" --opacity 0.95;
  elif [[ ${QUAKE[1]} -eq 1 ]]; then
    $HOMEBREW/yabai -m window "${QUAKE[0]}" --deminimize;
    $HOMEBREW/yabai -m window "${QUAKE[0]}" --move abs:0:0;
    $HOMEBREW/yabai -m window "${QUAKE[0]}" --resize abs:10000:450;
    $HOMEBREW/yabai -m window "${QUAKE[0]}" --opacity 0.95;
    $HOMEBREW/yabai -m window "${QUAKE[0]}" --focus "${QUAKE[0]}";
    [[ ${QUAKE[2]} -eq 0 ]] && $HOMEBREW/yabai -m window "${QUAKE[0]}" --toggle border || :
  elif [[ ${QUAKE[1]} -eq 0 ]]; then
    [[ ${QUAKE[2]} -eq 1 ]] && $HOMEBREW/yabai -m window "${QUAKE[0]}" --toggle border || :
    $HOMEBREW/yabai -m window "${QUAKE[0]}" --opacity 0.001;
    $HOMEBREW/yabai -m window "${QUAKE[0]}" --minimize;
  else
    echo quake failed
  fi
}
erikc96 commented 2 years ago

@typkrft This works really well, but I was playing with it and don't understand well enough, is there a way to modify this to run a command on startup?

typkrft commented 2 years ago

@typkrft This works really well, but I was playing with it and don't understand well enough, is there a way to modify this to run a command on startup?

This is kind of a constantly tweaked script for me. This needs to be cleaned up and improved a lot. The current iteration I'm using is below. I've added some comments to try and explain what's going on.

EDIT: See the comment below, which should clean this up a bit. EDIT: Implemented comment below. EDIT: Added dependency GetWindowID and removed calls to query all windows to try and avoid slowdowns.

function kitty_quake() {
  KITTY=$(pgrep -a -f ".*kitty.*")  
  QUAKE=$(osascript -l JavaScript -e 'Application("System Events").processes.whose({name: "kitty"}).windows.whose({name: "Quake"}).name()')

  if [[ $KITTY == "" ]]; then

    open -a kitty -n --args -1 --listen-on=unix:/tmp/kitty -o macos_quit_when_last_window_closed=no false

    while [[ $KITTY == "" ]]
    do
      KITTY=$(pgrep -a -f ".*kitty.*")  
    done

    open -a kitty -n --args -1 --listen-on=unix:/tmp/kitty --title=Quake

    while [[ $QUAKE != "Quake" ]]
    do
      QUAKE=$(osascript -l JavaScript -e 'Application("System Events").processes.whose({name: "kitty"}).windows.whose({name: "Quake"}).name()')
    done

    WINDOW_ID=$(GetWindowID kitty 'Quake')
    yabai -m window $WINDOW_ID --toggle sticky
    yabai -m window $WINDOW_ID --move abs:0:0
    yabai -m window $WINDOW_ID --resize abs:10000:450
    yabai -m window $WINDOW_ID --opacity 0.95
    exit 0    

  elif [[ $KITTY != "" && $QUAKE == "" ]]; then

    open -a kitty -n --args -1 --listen-on=unix:/tmp/kitty --title=Quake

    while [[ $QUAKE != "Quake" ]]
    do
      QUAKE=$(osascript -l JavaScript -e 'Application("System Events").processes.whose({name: "kitty"}).windows.whose({name: "Quake"}).name()')
    done

    WINDOW_ID=$(GetWindowID kitty 'Quake')
    yabai -m window $WINDOW_ID --toggle sticky
    yabai -m window $WINDOW_ID --move abs:0:0
    yabai -m window $WINDOW_ID --resize abs:10000:450
    yabai -m window $WINDOW_ID --opacity 0.95

  elif [[ $KITTY != "" && $QUAKE != "" ]]; then

    WINDOW_ID=$(GetWindowID kitty 'Quake')
    INFO=$(yabai -m query --windows --window $WINDOW_ID | jq '.border, .minimized')
    BORDER=$(echo $INFO | awk '{print $1}')
    MINI=$(echo $INFO | awk '{print $2}')

    if [[ $MINI -eq 1 ]]; then

      [[ $BORDER -eq 1 ]] && yabai -m window "$WNDOW_ID" --toggle border || :
      yabai -m window "$WINDOW_ID" --deminimize;
      yabai -m window "$WINDOW_ID" --opacity 0.95;
      [[ $BORDER -eq 0 ]] && yabai -m window "$WNDOW_ID" --toggle border || :
      yabai -m window "$WINDOW_ID" --focus "$WINDOW_ID";

    elif [[ $MINI -eq 0 ]]; then

      [[ $BORDER -eq 1 ]] && yabai -m window "$WINDOW_ID" --toggle border || :
      yabai -m window "$WINDOW_ID" --opacity 0.001;
      yabai -m window "$WINDOW_ID" --minimize;

    fi
  fi
}

There's still a lot that needs to be tweaked, could be simplified, or written more effectively. A few things that would be very helpful would be if kitty could be started without a window, If we could negate inherited window titles, maybe by setting --title to an empty string or something, and if kitty didn't activate, which includes de-minimizing the last window after the second to last window is closed. In addition, if you restart your mac it will likely reopen kitty depending on settings, and when you invoke kitty for the first time despite using the -1 flag it will open a new instance. macOS is particular and peculiar in the way it handles window management, so I've not created issues for any of these things.

Also querying with yabai can take a long time, proabaly due to this which causes opening and closing to take a while sometimes. This was somewhat addressed in the last edit.

kovidgoyal commented 2 years ago

If you want to start kitty without a window run it as

kitty -o macos_quit_when_last_window_closed=no false

notDavid commented 2 years ago

@webberwang @NightMachinary

Is there a way this will open over fullscreen apps in MacOS? I have the shortcut opening kitty in BTT but it switches me out of the 'space' if im in a fullscreen app

This does not open over fullscreen apps, but it does switch back to the fullscreen app when you hide Kitty. It's a somewhat ugly hack, but...: it works for me in BetterTouchTool

totaloncue commented 2 years ago

@mk12 @exoticus thank you for this suggestion!

I use the following setup to get Kitty working Quake style with opacity effects on full screens in macOS:

  1. In System Preferences, I choose to auto-hide and show the dock and menu bar. To achieve a full screen on any app, I simply maximize the app. This way, the app uses the entire screen space but stays on the current Space.
  2. I add a file in the Kitty config directory named macos-launch-services-cmdline with '--start-as=maximized'
  3. I edited the Hammerspoon config as below to open Kitty on the current space (with my preferred key-binding) and in full-screen mode


-- Kitty configuration
hs.hotkey.bind({"cmd"}, "g", function()

  -- Get current space 
  local currentSpace = hs.spaces.focusedSpace()

  -- Get kitty app
  local app = hs.application.get("kitty")

  -- If app already open:
  if app then

      -- If no main window, then open a new window
      if not app:mainWindow() then
          app:selectMenuItem("New OS Window", true)

      -- If app is already in front, then hide it
      elseif app:isFrontmost() then
          app:hide()

      -- If there is a main window somewhere, bring it to current space and to front
      else
          -- First move the main window to the current space
          hs.spaces.moveWindowToSpace(app:mainWindow(), currentSpace)
          -- Activate the app
          app:activate()
          -- Raise the main window and position correctly
          app:mainWindow():raise()
          app:mainWindow():moveToUnit('0.0,0.0,1.0,1.0')
      end

  -- If app not open, open it
  else
      hs.application.launchOrFocus("kitty")
      app = hs.application.get("kitty")
  end

  -- hs.spaces.gotoSpace(currentSpace)

end)
ibehnam commented 2 years ago

@mk12 Very nice this inspired me to make this which turns Kitty into a drop down quake and iTerm like style, i bound it to F15 which is the Break/Pause key because c+spc is frequently used to trigger auto complete in vscode and other editor. paired with hide_window_decorations yes in kitty.conf it makes for a very viable alternative to iTerm

hs.hotkey.bind({}, "F15", function()
  local app = hs.application.get("kitty")

  if app then
      if not app:mainWindow() then
          app:selectMenuItem({"kitty", "New OS window"})
      elseif app:isFrontmost() then
          app:hide()
      else
          app:activate()
      end
  else
      hs.application.launchOrFocus("kitty")
      app = hs.application.get("kitty")
  end

  app:mainWindow():moveToUnit'[100,50,0,0]'
  app:mainWindow().setShadows(false)
end)

This is awesome. Although I experienced two bugs with it:

(1) If you close Kitty using CMD+W, then this script won't work anymore. (2) The first run of Kitty doesn't show it at the top. It start at center. Subsequent show/hides of Kitty bring it to the top though.

caldotdev commented 1 year ago

I tried many ways of doing this and eventually settled on Hammerspoon. I prefer it because it's free, flexible, easy to set up programmatically in my dotfiles, and fast. (That last point is important. For example, you can avoid installing external software and instead make a shortcut in System Preferences for an Automator "Service", but there's a second-long delay after pressing the keys.)

Putting this in ~/.hammerspoon/init.lua will show/hide kitty when you press CtrlSpace:

hs.hotkey.bind({"ctrl"}, "space", function()
    local app = hs.application.get("kitty")
    if app then
        if not app:mainWindow() then
            app:selectMenuItem({"kitty", "New OS window"})
        elseif app:isFrontmost() then
            app:hide()
        else
            app:activate()
        end
    else
        hs.application.launchOrFocus("kitty")
    end
end)

Or, if you want it to always open a new window:

hs.hotkey.bind({"ctrl"}, "space", function()
    local app = hs.application.get("kitty")
    if app then
        app:selectMenuItem({"kitty", "New OS window"})
    else
        hs.application.launchOrFocus("kitty")
    end
end)

If you set macos_hide_from_tasks yes this doesn't work properly since there is no menu item to select when kitty is in focus.

I would therefore recommend to use the launchOrFocus method instead

-- toggle kitty
hs.hotkey.bind({"alt"}, "space", function()
    local app = hs.application.get("kitty")
    if app:isFrontmost() then
        app:hide()
    else
        hs.application.launchOrFocus(app:name())
    end
end)
erikc96 commented 1 year ago

wanted to share my revision of one of the scripts using skhd / yabai from above. (@typkrft )

function kitty_quake() {
  KITTY=$(pgrep -a -f ".*kitty.*")
  QUAKE=$(GetWindowID kitty "$1")

  if [[ $KITTY == "" ]]; then

    open -a kitty -n --args -1 --session="/Users/craigoej/.config/kitty/${1}.conf" --listen-on=unix:/tmp/kitty -o macos_quit_when_last_window_closed=no false

    while [[ $KITTY == "" ]]
    do
      KITTY=$(pgrep -a -f ".*kitty.*")
    done

    open /Applications/kitty.app -n --args --single-instance --session "/Users/craigoej/.config/kitty/${1}.conf" --listen-on=unix:/tmp/kitty

    while [[ $QUAKE != "$1" ]]
    do
      QUAKE=$(GetWindowID kitty "$1")
    done

    WINDOW_ID=$(GetWindowID kitty "${1}")
    yabai -m window $WINDOW_ID --toggle sticky
    yabai -m window $WINDOW_ID --move abs:0:0
    yabai -m window $WINDOW_ID --resize abs:10000:450
    yabai -m window $WINDOW_ID --opacity 0.95
    exit 0

  elif [[ $KITTY != "" && $QUAKE == "" ]]; then

    open /Applications/kitty.app -n --args --single-instance --session "/Users/craigoej/.config/kitty/${1}.conf" --listen-on=unix:/tmp/kitty

    while [[ $QUAKE != "${1}" ]]
    do
      QUAKE=$(GetWindowID kitty "$1")
    done

    WINDOW_ID=$(GetWindowID kitty "${1}")
    yabai -m window $WINDOW_ID --toggle sticky
    yabai -m window $WINDOW_ID --move abs:0:0
    yabai -m window $WINDOW_ID --resize abs:10000:450
    yabai -m window $WINDOW_ID --opacity 0.95

  elif [[ $KITTY != "" && $QUAKE != "" ]]; then

    WINDOW_ID=$(GetWindowID kitty "${1}")
    INFO=$(yabai -m query --windows --window $WINDOW_ID )
    BORDER=$(echo $INFO | jq '.["has-border"]')
    FOCUS=$(echo $INFO | jq '.["has-focus"]')
    MINI=$(echo $INFO | jq '.["is-minimized"]')
    echo $MINI
    echo $WINDOW_ID
    echo $BORDER
    echo $MINI

    if [[ $MINI == "true" ]]; then
      echo "a"

      [[ $FOCUS == "false" ]] || :
        echo "b"
        yabai -m window "${WINDOW_ID}" --deminimize "${WINDOW_ID}";
        #yabai -m window "${WINDOW_ID}" --opacity 0.95;
        yabai -m window "$WINDOW_ID" --focus "$WINDOW_ID";

    elif [[ $MINI -eq "false" ]]; then

      if [[ $FOCUS == "false" ]]; then
        yabai -m window "$WINDOW_ID" --focus "$WINDOW_ID";
      else
        yabai -m window "$WINDOW_ID" --opacity 0.001;
        yabai -m window "$WINDOW_ID" --minimize ${WINDOW_ID};
      fi
    fi
  fi
}
kitty_quake $1

and you call it like this

ctrl - return : ~/bin/kitty_quake.sh "whatever"

changes:

I have it open a tmuxinator session defined in the session config.

kbwhodat commented 1 year ago

Code below allows you to spawn your terminal from the screen your mouse is on.


  local app = hs.application.get("kitty")
  local win = app:focusedWindow()
  local appscreen = win:screen() 
  local mousescreen = hs.mouse.getCurrentScreen()

  if app then
      if appscreen == mousescreen then
        if app:isFrontmost() then 
          app:hide()
        else
          app:activate()
      app:mainWindow():moveToUnit'[100,50,0,100]'
        end
      else
        win:moveToScreen(mousescreen)

    if app:isHidden() then 
          app:activate()
      app:mainWindow():moveToUnit'[100,50,0,100]'
        end

      end
  end

  app:mainWidnow().setShadows(false)

end)
GiuseppeMP commented 1 year ago

I created a phoenix script which does this. But only in mac!

brew install --cask phoenix

copy the following file to ~/.phoenix.js

https://github.com/lukesmurray/bootstrap/blob/master/.phoenix.js

Use cmd+` (backtick) to get the dropdown.

These are the relevant lines. You can make your own shortcut

https://github.com/lukesmurray/bootstrap/blob/a4610f273dac7ca9fba3881a4fe74a9e4c20b699/.phoenix.js#L50-L57

It worked like a charm! Ty bro!

Producdevity commented 1 year ago

Did you, or anyone else, find a way to make this move the terminal to the desktop you are on. So you have this "floating window" effect like iTerm2.

This works, but it doesn't function like a "drop-down" terminal, if that makes sense.

svenjacobs commented 1 year ago

This is already a quite long thread with many ideas and solutions but I would like to propose that this functionality becomes a native feature of Kitty. I'm aware that Kitty is a cross-platform application so the feature probably needs to be developed individually for every supported platform. However I believe that only Kitty truly knows which of the opened windows is the "Quake-style" window, so it can provide the best user experience.

For example I wrote my own Phoenix script based on solutions presented here but it also has its drawbacks: For instance it's not possible to find out which of the opened Kitty windows is the pulldown window so the script will place the current active window to the top half of my screen. Also the script can only hide/show all Kitty windows at once, but I only want to toggle the pulldown window.

In my workflow I usually have one pulldown terminal window and sometimes additional (OS) windows which should not interfere with the pulldown functionality.

This may be a limitation of Phoenix so if somebody has a better idea of implementing this for macOS please let me know 😃

kxxdhdn commented 1 year ago

Just an FYI, if you use the GNOME desktop environment, there's a quake-mode extension available that works pretty well (it's even more customizable than iTerm2 on Mac). I discovered it after installing gnome-shell-extensions using apt-get, and despite being listed as 'Manually Installed', it was already there. Alternatively, you can visit the dev page to install it yourself. It is said to be "Developed for usage tilix on Wayland but can manage almost any application." I can confirm that it works perfectly with Kitty on Wayland, too.

prajnak commented 1 year ago

I wanted to create the iTerm hotkey window behaviour exactly with Kitty and Hammerspoon and ended up combining the approach from @exoticus and @mk12 - https://github.com/kovidgoyal/kitty/issues/45#issuecomment-573196169

with this gist from @asmagill for capturing the ctrl double tap in Hammerspoon - https://gist.github.com/asmagill/c38f75fff9d9ef43d1226329fc1436e4

Here is how you can do it. Add the following module (from the gist above) to a file called ~/.hammerspoon/ctrldouble.lua:

local alert    = require("hs.alert")
local timer    = require("hs.timer")
local eventtap = require("hs.eventtap")

local events   = eventtap.event.types

local module   = {}

-- Save this in your Hammerspoon configuration directiorn (~/.hammerspoon/) 
-- You either override timeFrame and action here or after including this file from another, e.g.
--
-- ctrlDoublePress = require("ctrlDoublePress")
-- ctrlDoublePress.timeFrame = 2
-- ctrlDoublePress.action = function()
--    do something special
-- end

-- how quickly must the two single ctrl taps occur?
module.timeFrame = 1

-- what to do when the double tap of ctrl occurs
module.action = function()
    alert("You double tapped ctrl!")
end

-- Synopsis:

-- what we're looking for is 4 events within a set time period and no intervening other key events:
--  flagsChanged with only ctrl = true
--  flagsChanged with all = false
--  flagsChanged with only ctrl = true
--  flagsChanged with all = false

local timeFirstControl, firstDown, secondDown = 0, false, false

-- verify that no keyboard flags are being pressed
local noFlags = function(ev)
    local result = true
    for k,v in pairs(ev:getFlags()) do
        if v then
            result = false
            break
        end
    end
    return result
end

-- verify that *only* the ctrl key flag is being pressed
local onlyCtrl = function(ev)
    local result = ev:getFlags().ctrl
    for k,v in pairs(ev:getFlags()) do
        if k ~= "ctrl" and v then
            result = false
            break
        end
    end
    return result
end

-- the actual workhorse

module.eventWatcher = eventtap.new({events.flagsChanged, events.keyDown}, function(ev)
    -- if it's been too long; previous state doesn't matter
    if (timer.secondsSinceEpoch() - timeFirstControl) > module.timeFrame then
        timeFirstControl, firstDown, secondDown = 0, false, false
    end

    if ev:getType() == events.flagsChanged then
        if noFlags(ev) and firstDown and secondDown then -- ctrl up and we've seen two, so do action
            if module.action then module.action() end
            timeFirstControl, firstDown, secondDown = 0, false, false
        elseif onlyCtrl(ev) and not firstDown then         -- ctrl down and it's a first
            firstDown = true
            timeFirstControl = timer.secondsSinceEpoch()
        elseif onlyCtrl(ev) and firstDown then             -- ctrl down and it's the second
            secondDown = true
        elseif not noFlags(ev) then                        -- otherwise reset and start over
            timeFirstControl, firstDown, secondDown = 0, false, false
        end
    else -- it was a key press, so not a lone ctrl char -- we don't care about it
        timeFirstControl, firstDown, secondDown = 0, false, false
    end
    return false
end):start()

return module

Then, add this code (based off of @exoticus @mk12) to your ~/.hammerspoon/init.lua config file (or your own one if you use a different name) :

ctrlDoublePress = require("ctrldouble")
ctrlDoublePress.action = function()
   local app = hs.application.get("kitty")
   if app then
       if not app:mainWindow() then
           app:selectMenuItem({"kitty", "New OS window"})
       elseif app:isFrontmost() then
           app:hide()
       else
           app:activate()
       end
   else
       hs.application.launchOrFocus("kitty")
       app = hs.application.get("kitty")
   end
   app:mainWindow():moveToUnit'[100,50,0,0]'
end

Works like a charm on MacOS. I've remapped my capslock to ctrl and this exactly recreates the Iterm hotkey window behaviour (much faster and lower resource usage thanks to kitty)

svenjacobs commented 1 year ago

@prajnak Does your solution work with multiple opened OS windows of Kitty? Will it only show / hide the hotkey window while other windows are still shown?

prajnak commented 1 year ago

@svenjacobs it will hide/show the last active kitty window.. But that's ok for now for me.

karmicdude commented 1 year ago

With hammerspoon it is better to select applications with bundleID() not with name() just because if there is at least one window with an active title matching the search string in hs.application.get() method, hammerspoon will select it (docs). For example, if you are on this page or on the kitty documentation page, trying to invoke the shortcut to open a new window will not have the expected result because the tab title contains "kitty".

The BundleID can be obtained with codesign -dr - /path/to/Application.app in identifier field or via the hammerspoon console:

> kitty = hs.application.get("kitty")

> kitty:bundleID()
net.kovidgoyal.kitty

And while in most cases getting the application by name will work, it's a little better to get it by bundleID, respectively just replace value for get() method from name to bundleID, i.e. local app = hs.application.get("net.kovidgoyal.kitty")

dwainm commented 9 months ago

I've adapted the script slightly as it didn't work for me when I'm not focused on kitty and in a different Space. My use case is that I want Kitty opened permanently in one space but still be able to bring up new kitty window instances while I'm working in other spaces.

hs.hotkey.bind({"ctrl"}, "space", function()
    local app = hs.application.get("kitty")
    local currentSpace = hs.spaces.focusedSpace()

    if app then
        -- There are times where main window is found and other times not 
        -- This check ensures that we have a main window ID set in cases where 
        -- it is found or set to -1 in cases where it is not found
        -- this ensures that the new window call can be executed later.
        if not app:mainWindow() then
            mainWindowSpace = -1
        else
            mainWindowSpace = hs.spaces.windowSpaces( app:mainWindow():id())[1]
        end

        if mainWindowSpace ~= currentSpace then
            app:selectMenuItem({"Shell", "New OS Window"})
            hs.timer.doAfter(1, function()
                app:focusedWindow():moveToUnit'[100,50,0,0]'
            end)

        elseif app:isFrontmost() then
            app:hide()
        else
            app:activate()
        end
    else
        hs.application.launchOrFocus("kitty")
        app = hs.application.get("kitty")
    end

end)

It has something to do with how app:mainWindow is defined. The main window kept referring back to one already opened.

truebit commented 7 months ago

I also wrote a Hammerspoon script for this: https://gist.github.com/truebit/d79b8018666d65e95970f208d8f5d149; the script supports:

I-Want-ToBelieve commented 7 months ago

I also wrote a Hammerspoon script for this: https://gist.github.com/truebit/d79b8018666d65e95970f208d8f5d149; the script supports:

* multi window and space show/hide;

* more accurate kitty app detection using bundle id;

* automatically move kitty window to the top of the screen;

* additional kitty config to make script more robustly;

@truebit hi!

 getMainWindow(app):move(hs.geometry({x=0,y=0,w=0.6,h=0.4})) 

The width of the Kitty window is always the width of the monitor And if the stage manager for macOS is enabled, the X coordinate of the Kitty window is not 0 but more to the right

truebit commented 7 months ago

@I-Want-ToBelieve I tried on 13.5.1 (22G90) and 14.1 (23B74), there's no issue. maybe you could use default stage manager settings or tell me the reproducible steps

I-Want-ToBelieve commented 7 months ago

@truebit It's probably because I didn't set up the kitty config posted in your gist.

Now I use yabai and no longer enable stage manager.

I am more familiar with JavaScript than Lua, so I switched to the above solution of phoenix and yabai.

Sorry to bother you.

By the way, I was using mac 13.6 before

q-flexai commented 3 months ago

Easiest way I found on Mac OS without third party apps.

Add new Quick Action in Automator

Open automator -> New Quick Action. Drag the "Run Shell Script" from the left side to the right side and type:

nohup /Applications/kitty.app/Contents/MacOS/kitty -1 1> /dev/null 2> /dev/null & 

Save it with a name like Start Kitty

Add your shortcut from the System Settings Keyboard Shortcuts

Go in System Settings -> Keyboard Shortcuts -> Services tab -> General -> Start Kitty. Assign keystroke.

Voilà.

xave commented 2 weeks ago

Another native way for macOS is to do the following:

  1. Open Apple's "Script Editor" app and select "New Document".

  2. Once in the document, ensure the dropdown is set to "Applescript" (should be by default).

  3. Add the following code

    do shell script "/path/to/kitty"
  4. Save this with file format as "Text" (so you can version control it in git). It will have the *.applescript extension.

  5. Export as an "Application" with everything unchecked and "Sign to run Locally" with a name like "Kitty".

Now you have an executable icon. You can drag it into "Applications" folder if you would like. You can also now Spotlight search "Kitty" and run it as with any other app.


Bonus step: Add the kitty icon to the executable by doing the following:

  1. Right-click on icon and select "Get info".
  2. Drag the kitty icon of your choice to the top left corner image.