Skip to content

Commit

Permalink
Merge branch 'summer-24-touch-up'
Browse files Browse the repository at this point in the history
  • Loading branch information
bnfour committed Jul 20, 2024
2 parents 2e1e3fc + 3bac171 commit d47dc75
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 19 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2022-2023 bnfour
Copyright (c) 2022-2024 bnfour

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ A script for i3 window manager to have one global drop-down terminal window togg
Just drop `quake-terminal.py` somewhere and create some keybinds to launch it in your i3 config.

## Requirements
Requires `i3ipc` package ([PyPI](https://pypi.org/project/i3ipc/), [GitHub](https://github.com/altdesktop/i3ipc-python)).
Requires `i3ipc` package ([PyPI](https://pypi.org/project/i3ipc/), [GitHub](https://github.com/altdesktop/i3ipc-python)).

Fellow Fedora enjoyers:
`sudo dnf install python3-i3ipc`
Expand All @@ -14,7 +14,8 @@ For other distros, consult your package manager repos, or install via `pip`:
`python -m pip install i3ipc`

# Usage
The script will create a single sticky terminal window on specified monitor, toggleable via hotkey. The first call will launch show the window, the second call will hide it. If the window is closed, subsequent call will create it again.
The script creates a single sticky terminal window on specified output, toggleable via calling the script again. The first call will show the window, the second call will hide it, and so on. If the window is closed, the first subsequent call will create and show a new one.

Available settings:
```
$ quake-terminal.py -?
Expand Down Expand Up @@ -61,12 +62,13 @@ This script only really supports `urxvt` (as it's _the_ terminal emulator I use)
Otherwise, please extend the script to work with another terminal emulator. See `terminals` dict in the configuration section.

## Flickering
As stated in help message, to prevent terminal window first appearing in default position and visibly teleporting, add a rule to i3 config to move it to the scratchpad by default.
As stated in the help message, to prevent terminal window first appearing in default position and visibly teleporting to proper position, add a [`for_window` rule](https://i3wm.org/docs/userguide.html#for_window) to your i3 config to move it to the scratchpad by default.

An example for default settings, an `urxvt` window called "The terminal":
```
for_window [class="URxvt" title="The terminal"] move scratchpad
```
Adjust class and title as needed.
Adjust class and title as needed. Class name for your terminal emulator can be found using `xprop`.

# Credits
This script is inspired by https://github.com/NearHuscarl/i3-quake. If this script is not exactly what you're looking for, check it out!
Expand Down
41 changes: 27 additions & 14 deletions quake-terminal.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,19 @@
import os
import sys
import time
from typing import NamedTuple, Final

try:
import i3ipc
except ImportError:
print('i3ipc module not found. Exiting.')
sys.exit(1)

from typing import NamedTuple, Final

#endregion

#region configuration

version: Final = "2.0"
version: Final = '2.1'

class Defaults(object):
"""
Expand Down Expand Up @@ -61,7 +60,10 @@ class Terminal(NamedTuple):
}

def get_args() -> tuple[argparse.Namespace, list[str]]:

"""
Returns parsed arguments for the script itself,
and a list of unrecognized arguments to be passed to the terminal emulator as is.
"""
parser = argparse.ArgumentParser(add_help=False,
description='A script to have one global terminal window toggleable by a hotkey.',
epilog='Any unrecognized arguments are passed as is to the terminal emulator. To prevent flickering, please add an i3 rule to move created terminal windows to the scratchpad, for example: for_window [class="URxvt" title="The terminal"] move scratchpad',
Expand Down Expand Up @@ -109,7 +111,11 @@ def get_args() -> tuple[argparse.Namespace, list[str]]:
#endregion

def main(config: argparse.Namespace, arguments_to_pass: list[str]):

"""
Main entry point of the script.
Toggles the visibility of the terminal emulator window if it's present;
otherwise, creates a new one and shows it.
"""
i3 = i3ipc.Connection()
window_tag = generate_window_tag(config.name)

Expand All @@ -135,7 +141,7 @@ def main(config: argparse.Namespace, arguments_to_pass: list[str]):
else:
term_by_name = None
# wait for the terminal to appear for a second
for i in range(10):
for _ in range(10):
time.sleep(0.1)
term_by_name = i3.get_tree().find_titled(config.name)
if term_by_name:
Expand All @@ -151,24 +157,26 @@ def main(config: argparse.Namespace, arguments_to_pass: list[str]):
term_by_name[0].command(f'mark {window_tag}')
show(term_by_name[0], i3, config)

#region window manipulation code

def toggle(window: i3ipc.Con, i3: i3ipc.Connection, config: argparse.Namespace):
"""
Toggles the terminal state, shows or hides it.
Can be configured to focus the terminal window first before closing on
a subsequent call.
Toggles the terminal visibility state.
Can be configured to focus the visible terminal window first
before closing on a subsequent call when it's focused.
"""
if in_scratchpad(window):
show(window, i3, config)
else:
if not window.focused and config.focus_first:
if config.focus_first and not window.focused:
focus(window)
else:
hide(window)

def show(window: i3ipc.Con, i3: i3ipc.Connection, config: argparse.Namespace):
"""
Calls internal methods required to calculate the terminal window position
and size and show it there
and size and show it there.
"""
output_position, output_size = get_output_properties(config.output, i3)
output_width, output_height = output_size
Expand All @@ -185,10 +193,14 @@ def show(window: i3ipc.Con, i3: i3ipc.Connection, config: argparse.Namespace):
show_internal(window, window_position, window_size)

def show_internal(window: i3ipc.Con, position: tuple[int, int], size: tuple[int, int]):
"""Actually moves and resizes the window to dimensions specified"""
"""
Actually moves and resizes the window to dimensions specified.
Sticky mode is applied only if not already set.
"""
x, y = position
w, h = size
window.command('scratchpad show, sticky enable,'
sticky_command = '' if window.sticky else 'sticky enable,'
window.command(f'scratchpad show, {sticky_command}'
+ f' resize set {w}px {h}px, move position {x}px {y}px')

def focus(window: i3ipc.Con):
Expand All @@ -202,7 +214,7 @@ def hide(window: i3ipc.Con):
def get_output_properties(name: str, i3: i3ipc.Connection) -> tuple[tuple[int, int], tuple[int, int]]:
"""
Gets dimensions of a physical output by given name, or special 'main' value
that specifies the primary output, whatever its name is.
that specifies the primary output, whatever its actual name is.
Returns output's dimesions as ((x position, y position), (width, height))
"""
outputs = i3.get_outputs()
Expand Down Expand Up @@ -255,6 +267,7 @@ def generate_window_tag(name: str) -> str:
"""
return '_bnqi3_' + name.lower().replace(' ', '_')

#endregion

if __name__ == '__main__':
main(*get_args())

0 comments on commit d47dc75

Please sign in to comment.