Compare commits
106 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbccfb9d0d | ||
|
|
9fdd8e4ae4 | ||
|
|
6b6dea5190 | ||
|
|
5af551c96d | ||
|
|
6761fdca1a | ||
|
|
725da39ecb | ||
|
|
9005fc359d | ||
|
|
33e4a44008 | ||
|
|
3bddb9d95b | ||
|
|
fa01e0b76f | ||
|
|
f8f188ecff | ||
|
|
07935d9bd3 | ||
|
|
a827f4190c | ||
|
|
18c108ed03 | ||
|
|
2d70059f47 | ||
|
|
63ff3cfb9f | ||
|
|
ca70f22d46 | ||
|
|
c247a6c0cc | ||
|
|
7421a0b9e3 | ||
|
|
30b5e8bf8f | ||
|
|
96475ce3ee | ||
|
|
24669fb6af | ||
|
|
851885568f | ||
|
|
8efbc479e1 | ||
|
|
2167bfd5b5 | ||
|
|
490d4455af | ||
|
|
c94e06ccd2 | ||
|
|
ea37799fb0 | ||
|
|
d00fbd73fc | ||
|
|
361b23e3f1 | ||
|
|
8feccdf63f | ||
|
|
ed905fcf6d | ||
|
|
50a6abaac1 | ||
|
|
718f7e77a1 | ||
|
|
c4aeb1adba | ||
|
|
d9c69f0a54 | ||
|
|
5afdbbe6f1 | ||
|
|
4273cb35bf | ||
|
|
89be7a031d | ||
|
|
06c5127856 | ||
|
|
fd298256d7 | ||
|
|
e0246044b7 | ||
|
|
2f677990b2 | ||
|
|
da2e4c8503 | ||
|
|
a1eb236616 | ||
|
|
11186735c2 | ||
|
|
a038f62a5b | ||
|
|
f45acc055e | ||
|
|
43a25c49c4 | ||
|
|
e47e2b2560 | ||
|
|
7652065134 | ||
|
|
d0da418e49 | ||
|
|
77e72cfd9a | ||
|
|
7b2ecb0498 | ||
|
|
318c5bca67 | ||
|
|
d52da934ca | ||
|
|
21951e3668 | ||
|
|
64bef38813 | ||
|
|
fe67e3dde7 | ||
|
|
fc5e74e457 | ||
|
|
fdbd8df7b2 | ||
|
|
246972b59f | ||
|
|
6057296499 | ||
|
|
b4b2207b93 | ||
|
|
b881364c50 | ||
|
|
76059882c4 | ||
|
|
be71f5f1f4 | ||
|
|
021dfcad39 | ||
|
|
4e7498b854 | ||
|
|
494035c94b | ||
|
|
9c4230ad9c | ||
|
|
056c2c12bb | ||
|
|
a97c440872 | ||
|
|
653d823175 | ||
|
|
c2a2dbc88c | ||
|
|
77492d31cf | ||
|
|
aaa609109e | ||
|
|
2367fad90b | ||
|
|
aaa521bd24 | ||
|
|
d22686da10 | ||
|
|
48b93ab28f | ||
|
|
703a1b83a8 | ||
|
|
c9441bd3e2 | ||
|
|
9de6d619ee | ||
|
|
9aae31ef2f | ||
|
|
dca04c549b | ||
|
|
69e76d6eeb | ||
|
|
b4a93c67c9 | ||
|
|
f098e6c973 | ||
|
|
f8ebba3954 | ||
|
|
37ee693de2 | ||
|
|
f77ad2b41d | ||
|
|
e99eb2e72b | ||
|
|
86b89072f3 | ||
|
|
022bee0560 | ||
|
|
204ea44946 | ||
|
|
3f2489b306 | ||
|
|
291a2d6295 | ||
|
|
b244d13e8a | ||
|
|
b33324d07c | ||
|
|
ae2a641db5 | ||
|
|
1cac757365 | ||
|
|
992de77040 | ||
|
|
f6b5e54890 | ||
|
|
d20c65ef80 | ||
|
|
4a8562a85f |
29
.travis.yml
29
.travis.yml
@@ -4,7 +4,7 @@ matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- os: linux
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
group: beta
|
||||
language: python
|
||||
sudo: false
|
||||
@@ -12,6 +12,7 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libgl1-mesa-dev
|
||||
- libxi-dev
|
||||
- libxrandr-dev
|
||||
- libxinerama-dev
|
||||
@@ -20,7 +21,7 @@ matrix:
|
||||
- libdbus-1-dev
|
||||
|
||||
- os: linux
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
group: beta
|
||||
sudo: false
|
||||
env:
|
||||
@@ -30,6 +31,7 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libgl1-mesa-dev
|
||||
- libfontconfig1-dev
|
||||
- libharfbuzz-dev
|
||||
- libxi-dev
|
||||
@@ -42,7 +44,7 @@ matrix:
|
||||
- python3-pil
|
||||
|
||||
- os: linux
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
group: beta
|
||||
sudo: false
|
||||
env:
|
||||
@@ -52,6 +54,7 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libgl1-mesa-dev
|
||||
- libfontconfig1-dev
|
||||
- libharfbuzz-dev
|
||||
- libxi-dev
|
||||
@@ -64,7 +67,7 @@ matrix:
|
||||
- python3-pil
|
||||
|
||||
- os: linux
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
group: beta
|
||||
sudo: false
|
||||
env:
|
||||
@@ -74,6 +77,7 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libgl1-mesa-dev
|
||||
- libfontconfig1-dev
|
||||
- libharfbuzz-dev
|
||||
- libxi-dev
|
||||
@@ -89,10 +93,13 @@ matrix:
|
||||
language: generic
|
||||
env: SWBASE=/Users/Shared/buildbot/sw SW=$SWBASE/sw PATH=$SW/bin:$PATH
|
||||
|
||||
# Disabled because harfbuzz is currently broken in brew
|
||||
# - os: osx
|
||||
# language: generic
|
||||
# env: USE_BREW=1 BUILD_PKG=osx-bundle
|
||||
- os: osx
|
||||
language: generic
|
||||
addons:
|
||||
homebrew:
|
||||
update: true
|
||||
brewfile: true
|
||||
env: USE_BREW=1 BUILD_PKG=osx-bundle
|
||||
|
||||
env:
|
||||
global:
|
||||
@@ -106,12 +113,6 @@ install: |
|
||||
if [[ "$RUN_FLAKE" == "1" ]]; then pip install flake8; fi
|
||||
if [[ "$TRAVIS_OS_NAME" == 'osx' ]]; then
|
||||
if [[ "$USE_BREW" == "1" ]]; then
|
||||
brew update;
|
||||
# brew upgrade python;
|
||||
brew install harfbuzz --without-graphite2 --without-icu4c --without-freetype;
|
||||
brew install imagemagick
|
||||
brew install optipng
|
||||
brew install librsvg
|
||||
logo/make.py
|
||||
else
|
||||
mkdir -p $SW;
|
||||
|
||||
5
Brewfile
Normal file
5
Brewfile
Normal file
@@ -0,0 +1,5 @@
|
||||
brew 'imagemagick'
|
||||
brew 'optipng'
|
||||
brew 'librsvg'
|
||||
brew 'harfbuzz'
|
||||
brew 'python'
|
||||
@@ -3,6 +3,86 @@ Changelog
|
||||
|
||||
|kitty| is a feature full, cross-platform, *fast*, GPU based terminal emulator.
|
||||
|
||||
0.13.3 [2019-01-19]
|
||||
------------------------------
|
||||
|
||||
- icat kitten: Add a ``--stdin`` option to control if image data is read from
|
||||
STDIN (:iss:`1308`)
|
||||
|
||||
- hints kitten: Start hints numbering at one instead of zero by default. Added
|
||||
an option ``--hints-offset`` to control it. (:iss:`1289`)
|
||||
|
||||
- Fix a regression in the previous release that broke using ``background`` for
|
||||
:opt:`cursor_text_color` (:iss:`1288`)
|
||||
|
||||
- macOS: Fix dragging kitty window tabs in traditional full screen mode causing
|
||||
crashes (:iss:`1296`)
|
||||
|
||||
- macOS: Ensure that when running from a bundle, the bundle kitty exe is
|
||||
preferred over any kitty in PATH (:iss:`1280`)
|
||||
|
||||
- macOS: Fix a regression that broke mapping of :kbd:`ctrl+tab` (:iss:`1304`)
|
||||
|
||||
- Add a list of user-created kittens to the docs
|
||||
|
||||
- Fix a regression that broke changing mouse wheel scroll direction with
|
||||
negative :opt:`wheel_scroll_multiplier` values in full-screen applications
|
||||
like vim (:iss:`1299`)
|
||||
|
||||
- Fix :opt:`background_opacity` not working with pure white backgrounds
|
||||
(:iss:`1285`)
|
||||
|
||||
- macOS: Fix "New OS Window" dock action not working when kitty is not focused
|
||||
(:iss:`1312`)
|
||||
|
||||
- macOS: Add aliases for close window and new tab actions that conform to common
|
||||
Apple shortcuts for these actions (:iss:`1313`)
|
||||
|
||||
- macOS: Fix some kittens causing 100% CPU usage
|
||||
|
||||
|
||||
0.13.2 [2019-01-04]
|
||||
------------------------------
|
||||
|
||||
- Add a new option :opt:`tab_title_template` to control how tab titles
|
||||
are formatted. In particular the template can be used to display
|
||||
the tab number next to the title (:iss:`1223`)
|
||||
|
||||
- Report the current foreground processes as well as the original child process,
|
||||
when using `kitty @ ls`
|
||||
|
||||
- Use the current working directory of the foreground process for the
|
||||
`*_with_cwd` actions that open a new window with the current working
|
||||
directory.
|
||||
|
||||
- Add a new ``copy_or_interrupt`` action that can be mapped to kbd:`ctrl+c`. It
|
||||
will copy if there is a selection and interrupt otherwise (:iss:`1286`)
|
||||
|
||||
- Fix setting :opt:`background_opacity` causing window margins/padding to be slightly
|
||||
different shade from background (:iss:`1221`)
|
||||
|
||||
- Handle keyboards with a "+" key (:iss:`1224`)
|
||||
|
||||
- Fix Private use Unicode area characters followed by spaces at the end of text
|
||||
not being rendered correctly (:iss:`1210`)
|
||||
|
||||
- macOS: Add an entry to the dock menu to open a new OS window (:iss:`1242`)
|
||||
|
||||
- macOS: Fix scrolling very slowly with wheel mice not working (:iss:`1238`)
|
||||
|
||||
- Fix changing :opt:`cursor_text_color` via remote control not working
|
||||
(:iss:`1229`)
|
||||
|
||||
- Add an action to resize windows that can be mapped to shortcuts in :file:`kitty.conf`
|
||||
(:pull:`1245`)
|
||||
|
||||
- Fix using the ``new_tab !neighbor`` action changing the order of the
|
||||
non-neighboring tabs (:iss:`1256`)
|
||||
|
||||
- macOS: Fix momentum scrolling continuing when changing the active window/tab
|
||||
(:iss:`1267`)
|
||||
|
||||
|
||||
0.13.1 [2018-12-06]
|
||||
------------------------------
|
||||
|
||||
|
||||
36
docs/faq.rst
36
docs/faq.rst
@@ -6,7 +6,7 @@ Frequently Asked Questions
|
||||
.. contents::
|
||||
|
||||
Some special symbols are rendered small/truncated in kitty?
|
||||
-----------------------------------------------------------------
|
||||
-----------------------------------------------------------
|
||||
|
||||
The number of cells a unicode character takes up are controlled by the unicode
|
||||
standard. All characters are rendered in a single cell unless the unicode
|
||||
@@ -48,15 +48,43 @@ terminfo files to the server::
|
||||
kitty +kitten ssh myserver
|
||||
|
||||
If for some reason that does not work (typically because the server is using a
|
||||
a non POSIX compliant shell), you can use the following one-liner instead (it
|
||||
non POSIX compliant shell), you can use the following one-liner instead (it
|
||||
is slower as it needs to ssh into the server twice, but will work with most
|
||||
servers)::
|
||||
|
||||
infocmp xterm-kitty | ssh myserver tic -x -o \~/.terminfo /dev/stdin
|
||||
|
||||
If you are behind a proxy (like Balabit) that prevents this, you must redirect the
|
||||
1st command to a file, copy that to the server and run ``tic`` manually. If you
|
||||
connect to a server, embedded or Android system that doesn't have ``tic``, copy over
|
||||
your local file terminfo to the other system as :file:`~/.terminfo/x/xterm-kitty`.
|
||||
|
||||
Really, the correct solution for this is to convince the OpenSSH maintainers to
|
||||
have ssh do this automatically when connecting to a server, so that all
|
||||
terminals work transparently.
|
||||
have ssh do this automatically, if possible, when connecting to a server, so that
|
||||
all terminals work transparently.
|
||||
|
||||
|
||||
Keys such as arrow keys, backspace, delete, home/end, etc. do not work when using su or sudo?
|
||||
-------------------------------------------------------------------------------------------------
|
||||
|
||||
Make sure the TERM environment variable, is ``xterm-kitty``. And either the
|
||||
TERMINFO environment variable points to a directory containing :file:`x/xterm-kitty`
|
||||
or that file is under :file:`~/.terminfo/x/`.
|
||||
|
||||
Note that ``sudo`` might remove TERMINFO. Then setting it at the shell prompt can
|
||||
be too late, because command line editing may not be reinitialized. In that case
|
||||
you can either ask ``sudo`` to set it or if that is not supported, insert an ``env``
|
||||
command before starting the shell, or, if not possible, after sudo start another
|
||||
Shell providing the right terminfo path::
|
||||
|
||||
sudo … TERMINFO=$HOME/.terminfo bash -i
|
||||
sudo … env TERMINFO=$HOME/.terminfo bash -i
|
||||
TERMINFO=/home/ORIGINALUSER/.terminfo exec bash -i
|
||||
|
||||
If you have double width characters in your prompt, you may also need to
|
||||
explicitly set a UTF-8 locale, like::
|
||||
|
||||
export LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8
|
||||
|
||||
|
||||
How do I change the colors in a running kitty instance?
|
||||
|
||||
@@ -201,7 +201,11 @@ Value of `t` Meaning
|
||||
================== ============
|
||||
``d`` Direct (the data is transmitted within the escape code itself)
|
||||
``f`` A simple file
|
||||
``t`` A temporary file, the terminal emulator will delete the file after reading the pixel data
|
||||
``t`` A temporary file, the terminal emulator will delete the file after reading the pixel data. For security reasons
|
||||
the terminal emulator should only delete the file if it
|
||||
is in a known temporary directory, such as :file:`/tmp`,
|
||||
:file:`/dev/shm`, :file:`TMPDIR env var if present` and any platform
|
||||
specific temporary directories.
|
||||
``s`` A `POSIX shared memory object <http://man7.org/linux/man-pages/man7/shm_overview.7.html>`_.
|
||||
The terminal emulator will delete it after reading the pixel data
|
||||
================== ============
|
||||
|
||||
@@ -122,7 +122,7 @@ Tabs
|
||||
======================== =======================
|
||||
Action Shortcut
|
||||
======================== =======================
|
||||
New tab :sc:`new_tab`
|
||||
New tab :sc:`new_tab` (also :kbd:`⌘+t` on macOS)
|
||||
Close tab :sc:`close_tab`
|
||||
Next tab :sc:`next_tab` (also :kbd:`control+tab` on macOS)
|
||||
Previous tab :sc:`previous_tab` (also :kbd:`control+shift+tab` on macOS)
|
||||
@@ -141,7 +141,7 @@ Action Shortcut
|
||||
======================== =======================
|
||||
New window :sc:`new_window`
|
||||
New OS window :sc:`new_os_window` (also :kbd:`⌘+n` on macOS)
|
||||
Close window :sc:`close_window`
|
||||
Close window :sc:`close_window` (also :kbd:`⌘+w` on macOS)
|
||||
Next window :sc:`next_window`
|
||||
Previous window :sc:`previous_window`
|
||||
Move window forward :sc:`move_window_forward`
|
||||
@@ -223,6 +223,19 @@ example, in the Tall layout you can make the first window wider/narrower, but
|
||||
not taller/shorter. Note that what you are resizing is actually not a window,
|
||||
but a row/column in the layout, all windows in that row/column will be resized.
|
||||
|
||||
You can also define shortcuts in :file:`kitty.conf` to make the active window
|
||||
wider, narrower, taller, or shorter by mapping to the ``resize_window``
|
||||
action, for example::
|
||||
|
||||
map ctrl+left resize_window narrower
|
||||
map ctrl+right resize_window wider
|
||||
map ctrl+up resize_window taller
|
||||
map ctrl+down resize_window shorter 3
|
||||
|
||||
The ``resize_window`` action has a second, optional argument to control
|
||||
the resizing increment (a positive integer that defaults to 1).
|
||||
|
||||
|
||||
Some layouts take options to control their behavior. For example, the ``fat``
|
||||
and ``tall`` layouts accept the ``bias`` option to control how the available
|
||||
space is split up. To specify the option, in :opt:`kitty.conf <enabled_layouts>` use::
|
||||
|
||||
@@ -43,6 +43,8 @@ Now in :file:`kitty.conf` add the lines::
|
||||
Start kitty and press :kbd:`ctrl+k` and you should see the kitten running.
|
||||
The best way to develop your own kittens is to modify one of the built in
|
||||
kittens. Look in the kittens sub-directory of the kitty source code for those.
|
||||
Or see below for a list of :ref:`third-party kittens <external_kittens>`,
|
||||
that other kitty users have created.
|
||||
|
||||
|
||||
Passing arguments to kittens
|
||||
@@ -68,7 +70,14 @@ function, telling kitty what kind of input your kitten would like. For example:
|
||||
|
||||
.. code-block:: py
|
||||
|
||||
def handle_result(...):
|
||||
# in main, STDIN is for the kitten process and will contain
|
||||
# the contents of the screen
|
||||
def main(args):
|
||||
return sys.stdin.read()
|
||||
|
||||
# in handle_result, STDIN is for the kitty process itself, rather
|
||||
# than the kitten process and should not be read from.
|
||||
def handle_result(args, stdin_data, target_window_id, boss):
|
||||
pass
|
||||
|
||||
handle_result.type_of_input = 'text'
|
||||
@@ -120,3 +129,18 @@ layout, by simply adding the line::
|
||||
|
||||
|
||||
to the ``handle_result()`` function, above.
|
||||
|
||||
.. _external_kittens:
|
||||
|
||||
Kittens created by kitty users
|
||||
---------------------------------------------
|
||||
|
||||
`vim-kitty-navigator <https://github.com/knubie/vim-kitty-navigator>`_
|
||||
Allows you to navigate seamlessly between vim and kitty splits using a consistent set of hotkeys.
|
||||
|
||||
`smart-scroll <https://github.com/yurikhan/kitty-smart-scroll>`_
|
||||
Makes the kitty scroll bindings work in full screen applications
|
||||
|
||||
`insert password <https://github.com/kovidgoyal/kitty/issues/1222>`_
|
||||
Insert a password from a CLI password manager, taking care to only do it at
|
||||
a password prompt.
|
||||
|
||||
@@ -26,7 +26,11 @@
|
||||
|
||||
#include "internal.h"
|
||||
#include <sys/param.h> // For MAXPATHLEN
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200
|
||||
#define NSEventMaskKeyUp NSKeyUpMask
|
||||
#define NSEventMaskKeyDown NSKeyDownMask
|
||||
#define NSEventModifierFlagCommand NSCommandKeyMask
|
||||
#endif
|
||||
|
||||
// Change to our application bundle's resources directory, if present
|
||||
//
|
||||
@@ -274,33 +278,83 @@ static GLFWbool initializeTIS(void)
|
||||
return updateUnicodeDataNS();
|
||||
}
|
||||
|
||||
@interface GLFWLayoutListener : NSObject
|
||||
@interface GLFWHelper : NSObject
|
||||
@end
|
||||
|
||||
@implementation GLFWLayoutListener
|
||||
@implementation GLFWHelper
|
||||
|
||||
- (void)selectedKeyboardInputSourceChanged:(NSObject* )object
|
||||
{
|
||||
updateUnicodeDataNS();
|
||||
}
|
||||
|
||||
@end
|
||||
- (void)doNothing:(id)object
|
||||
{
|
||||
}
|
||||
|
||||
@end // GLFWHelper
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW platform API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static inline bool
|
||||
is_ctrl_tab(NSEvent *event) {
|
||||
NSEventModifierFlags modifierFlags = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
|
||||
return event.keyCode == kVK_Tab && (modifierFlags == NSEventModifierFlagControl || modifierFlags == (
|
||||
NSEventModifierFlagControl | NSEventModifierFlagShift));
|
||||
}
|
||||
|
||||
|
||||
int _glfwPlatformInit(void)
|
||||
{
|
||||
_glfw.ns.autoreleasePool = [[NSAutoreleasePool alloc] init];
|
||||
_glfw.ns.helper = [[GLFWHelper alloc] init];
|
||||
|
||||
[NSThread detachNewThreadSelector:@selector(doNothing:)
|
||||
toTarget:_glfw.ns.helper
|
||||
withObject:nil];
|
||||
|
||||
[NSApplication sharedApplication];
|
||||
|
||||
NSEvent* (^keydown_block)(NSEvent*) = ^ NSEvent* (NSEvent* event)
|
||||
{
|
||||
if (is_ctrl_tab(event)) {
|
||||
// Cocoa swallows Ctrl+Tab to cycle between views
|
||||
[[NSApp keyWindow].contentView keyDown:event];
|
||||
}
|
||||
|
||||
return event;
|
||||
};
|
||||
|
||||
NSEvent* (^keyup_block)(NSEvent*) = ^ NSEvent* (NSEvent* event)
|
||||
{
|
||||
if ([event modifierFlags] & NSEventModifierFlagCommand) {
|
||||
// From http://cocoadev.com/index.pl?GameKeyboardHandlingAlmost
|
||||
// This works around an AppKit bug, where key up events while holding
|
||||
// down the command key don't get sent to the key window.
|
||||
[[NSApp keyWindow] sendEvent:event];
|
||||
}
|
||||
if (is_ctrl_tab(event)) {
|
||||
// Cocoa swallows Ctrl+Tab to cycle between views
|
||||
[[NSApp keyWindow].contentView keyUp:event];
|
||||
}
|
||||
|
||||
return event;
|
||||
};
|
||||
|
||||
_glfw.ns.keyUpMonitor =
|
||||
[NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyUp
|
||||
handler:keyup_block];
|
||||
_glfw.ns.keyDownMonitor =
|
||||
[NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyDown
|
||||
handler:keydown_block];
|
||||
if (_glfw.hints.init.ns.chdir)
|
||||
changeToResourcesDirectory();
|
||||
|
||||
_glfw.ns.listener = [[GLFWLayoutListener alloc] init];
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver:_glfw.ns.listener
|
||||
addObserver:_glfw.ns.helper
|
||||
selector:@selector(selectedKeyboardInputSourceChanged:)
|
||||
name:NSTextInputContextKeyboardSelectionDidChangeNotification
|
||||
object:nil];
|
||||
@@ -345,17 +399,21 @@ void _glfwPlatformTerminate(void)
|
||||
_glfw.ns.delegate = nil;
|
||||
}
|
||||
|
||||
if (_glfw.ns.listener)
|
||||
if (_glfw.ns.helper)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
removeObserver:_glfw.ns.listener
|
||||
removeObserver:_glfw.ns.helper
|
||||
name:NSTextInputContextKeyboardSelectionDidChangeNotification
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
removeObserver:_glfw.ns.listener];
|
||||
[_glfw.ns.listener release];
|
||||
_glfw.ns.listener = nil;
|
||||
removeObserver:_glfw.ns.helper];
|
||||
[_glfw.ns.helper release];
|
||||
_glfw.ns.helper = nil;
|
||||
}
|
||||
if (_glfw.ns.keyUpMonitor)
|
||||
[NSEvent removeMonitor:_glfw.ns.keyUpMonitor];
|
||||
if (_glfw.ns.keyDownMonitor)
|
||||
[NSEvent removeMonitor:_glfw.ns.keyDownMonitor];
|
||||
|
||||
free(_glfw.ns.clipboardString);
|
||||
|
||||
|
||||
@@ -467,7 +467,7 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode *mode)
|
||||
CVDisplayLinkRelease(link);
|
||||
}
|
||||
|
||||
void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
|
||||
GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
|
||||
{
|
||||
uint32_t i, size = CGDisplayGammaTableCapacity(monitor->ns.displayID);
|
||||
CGGammaValue* values = calloc(size * 3, sizeof(CGGammaValue));
|
||||
@@ -489,6 +489,7 @@ void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
|
||||
}
|
||||
|
||||
free(values);
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
|
||||
@@ -523,4 +524,3 @@ GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* handle)
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(kCGNullDirectDisplay);
|
||||
return monitor->ns.displayID;
|
||||
}
|
||||
|
||||
|
||||
4
glfw/cocoa_platform.h
vendored
4
glfw/cocoa_platform.h
vendored
@@ -118,7 +118,9 @@ typedef struct _GLFWlibraryNS
|
||||
TISInputSourceRef inputSource;
|
||||
IOHIDManagerRef hidManager;
|
||||
id unicodeData;
|
||||
id listener;
|
||||
id helper;
|
||||
id keyUpMonitor;
|
||||
id keyDownMonitor;
|
||||
|
||||
char keyName[64];
|
||||
char text[256];
|
||||
|
||||
@@ -47,7 +47,6 @@
|
||||
#define NSEventModifierFlagDeviceIndependentFlagsMask NSDeviceIndependentModifierFlagsMask
|
||||
#define NSEventMaskAny NSAnyEventMask
|
||||
#define NSEventTypeApplicationDefined NSApplicationDefined
|
||||
#define NSEventTypeKeyUp NSKeyUp
|
||||
#endif
|
||||
|
||||
#if (MAC_OS_X_VERSION_MAX_ALLOWED < 101400)
|
||||
@@ -312,6 +311,7 @@ static int translateKey(unsigned int key, GLFWbool apply_keymap)
|
||||
K(';', SEMICOLON);
|
||||
K('[', LEFT_BRACKET);
|
||||
K(']', RIGHT_BRACKET);
|
||||
K('+', PLUS);
|
||||
K('`', GRAVE_ACCENT);
|
||||
K('\\', BACKSLASH);
|
||||
#undef K
|
||||
@@ -344,6 +344,8 @@ static NSUInteger translateKeyToModifierFlag(int key)
|
||||
case GLFW_KEY_LEFT_SUPER:
|
||||
case GLFW_KEY_RIGHT_SUPER:
|
||||
return NSEventModifierFlagCommand;
|
||||
case GLFW_KEY_CAPS_LOCK:
|
||||
return NSEventModifierFlagCapsLock;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -625,6 +627,11 @@ static GLFWapplicationshouldhandlereopenfun handle_reopen_callback = NULL;
|
||||
updateCursorImage(window);
|
||||
}
|
||||
|
||||
- (BOOL)acceptsFirstMouse:(NSEvent *)event
|
||||
{
|
||||
return NO; // changed by Kovid, to follow cocoa platform conventions
|
||||
}
|
||||
|
||||
- (void)mouseDown:(NSEvent *)event
|
||||
{
|
||||
_glfwInputMouseClick(window,
|
||||
@@ -915,8 +922,25 @@ is_ascii_control_char(char x) {
|
||||
if (yscale > 0) deltaY *= yscale;
|
||||
}
|
||||
|
||||
if (fabs(deltaX) > 0.0 || fabs(deltaY) > 0.0)
|
||||
_glfwInputScroll(window, deltaX, deltaY, flags);
|
||||
switch([event momentumPhase]) {
|
||||
case NSEventPhaseBegan:
|
||||
flags |= (1 << 1); break;
|
||||
case NSEventPhaseStationary:
|
||||
flags |= (2 << 1); break;
|
||||
case NSEventPhaseChanged:
|
||||
flags |= (3 << 1); break;
|
||||
case NSEventPhaseEnded:
|
||||
flags |= (4 << 1); break;
|
||||
case NSEventPhaseCancelled:
|
||||
flags |= (5 << 1); break;
|
||||
case NSEventPhaseMayBegin:
|
||||
flags |= (6 << 1); break;
|
||||
case NSEventPhaseNone:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_glfwInputScroll(window, deltaX, deltaY, flags);
|
||||
}
|
||||
|
||||
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
|
||||
@@ -1068,67 +1092,6 @@ is_ascii_control_char(char x) {
|
||||
@end
|
||||
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// GLFW application class
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
@interface GLFWApplication : NSApplication
|
||||
{
|
||||
NSArray* nibObjects;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GLFWApplication
|
||||
|
||||
- (void)sendEvent:(NSEvent *)event
|
||||
{
|
||||
NSEventType etype = [event type];
|
||||
NSEventModifierFlags flags;
|
||||
switch(etype) {
|
||||
case NSEventTypeKeyUp:
|
||||
flags = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
|
||||
if (flags & NSEventModifierFlagCommand) {
|
||||
// From http://cocoadev.com/index.pl?GameKeyboardHandlingAlmost
|
||||
// This works around an AppKit bug, where key up events while holding
|
||||
// down the command key don't get sent to the key window.
|
||||
[[self keyWindow] sendEvent:event];
|
||||
return;
|
||||
}
|
||||
if (event.keyCode == kVK_Tab && (flags == NSEventModifierFlagControl || flags == (NSEventModifierFlagControl | NSEventModifierFlagShift))) {
|
||||
// Cocoa swallows Ctrl+Tab to cycle between views
|
||||
[[self keyWindow].contentView keyUp:event];
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case NSEventTypeKeyDown:
|
||||
flags = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
|
||||
if (event.keyCode == kVK_Tab && (flags == NSEventModifierFlagControl || flags == (NSEventModifierFlagControl | NSEventModifierFlagShift))) {
|
||||
// Cocoa swallows Ctrl+Tab to cycle between views
|
||||
[[self keyWindow].contentView keyDown:event];
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
[super sendEvent:event];
|
||||
}
|
||||
|
||||
|
||||
// No-op thread entry point
|
||||
//
|
||||
- (void)doNothing:(id)object
|
||||
{
|
||||
}
|
||||
|
||||
- (void)loadMainMenu
|
||||
{ // removed by Kovid as it generated compiler warnings
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// Set up the menu bar (manually)
|
||||
// This is nasty, nasty stuff -- calls to undocumented semi-private APIs that
|
||||
// could go away at any moment, lots of stuff that really should be
|
||||
@@ -1238,32 +1201,9 @@ static void createMenuBar(void)
|
||||
//
|
||||
static GLFWbool initializeAppKit(void)
|
||||
{
|
||||
if (NSApp)
|
||||
if (_glfw.ns.delegate)
|
||||
return GLFW_TRUE;
|
||||
|
||||
// Implicitly create shared NSApplication instance
|
||||
[GLFWApplication sharedApplication];
|
||||
|
||||
// Make Cocoa enter multi-threaded mode
|
||||
[NSThread detachNewThreadSelector:@selector(doNothing:)
|
||||
toTarget:NSApp
|
||||
withObject:nil];
|
||||
|
||||
if (_glfw.hints.init.ns.menubar)
|
||||
{
|
||||
// In case we are unbundled, make us a proper UI application
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
|
||||
// Menu bar setup must go between sharedApplication above and
|
||||
// finishLaunching below, in order to properly emulate the behavior
|
||||
// of NSApplicationMain
|
||||
|
||||
if ([[NSBundle mainBundle] pathForResource:@"MainMenu" ofType:@"nib"])
|
||||
[NSApp loadMainMenu];
|
||||
else
|
||||
createMenuBar();
|
||||
}
|
||||
|
||||
// There can only be one application delegate, but we allocate it the
|
||||
// first time a window is created to keep all window code in this file
|
||||
_glfw.ns.delegate = [[GLFWApplicationDelegate alloc] init];
|
||||
@@ -1273,15 +1213,29 @@ static GLFWbool initializeAppKit(void)
|
||||
"Cocoa: Failed to create application delegate");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
[NSApp setDelegate:_glfw.ns.delegate];
|
||||
|
||||
if (_glfw.hints.init.ns.menubar)
|
||||
{
|
||||
// In case we are unbundled, make us a proper UI application
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
|
||||
// Menu bar setup must go between sharedApplication above and
|
||||
// finishLaunching below, in order to properly emulate the behavior
|
||||
// of NSApplicationMain
|
||||
|
||||
// disabled by Kovid
|
||||
/* if ([[NSBundle mainBundle] pathForResource:@"MainMenu" ofType:@"nib"]) */
|
||||
/* [NSApp loadMainMenu]; */
|
||||
/* else */
|
||||
createMenuBar();
|
||||
}
|
||||
|
||||
[NSApp run];
|
||||
|
||||
// Press and Hold prevents some keys from emitting repeated characters
|
||||
NSDictionary* defaults =
|
||||
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO],
|
||||
@"ApplePressAndHoldEnabled",
|
||||
nil];
|
||||
NSDictionary* defaults = @{@"ApplePressAndHoldEnabled":@NO};
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
|
||||
|
||||
return GLFW_TRUE;
|
||||
@@ -1793,9 +1747,6 @@ void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
|
||||
|
||||
void _glfwPlatformPollEvents(void)
|
||||
{
|
||||
if (!initializeAppKit())
|
||||
return;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
|
||||
@@ -1936,8 +1887,6 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
||||
NSImage* native;
|
||||
NSBitmapImageRep* rep;
|
||||
|
||||
if (!initializeAppKit())
|
||||
return GLFW_FALSE;
|
||||
native = [[NSImage alloc] initWithSize:NSMakeSize(image->width, image->height)];
|
||||
if (native == nil)
|
||||
return GLFW_FALSE;
|
||||
@@ -1974,8 +1923,6 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
||||
|
||||
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
|
||||
{
|
||||
if (!initializeAppKit())
|
||||
return GLFW_FALSE;
|
||||
|
||||
if (shape == GLFW_ARROW_CURSOR)
|
||||
cursor->ns.object = [NSCursor arrowCursor];
|
||||
@@ -2015,10 +1962,8 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
|
||||
|
||||
void _glfwPlatformSetClipboardString(const char* string)
|
||||
{
|
||||
NSArray* types = [NSArray arrayWithObjects:NSPasteboardTypeString, nil];
|
||||
|
||||
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
|
||||
[pasteboard declareTypes:types owner:nil];
|
||||
[pasteboard declareTypes:@[NSPasteboardTypeString] owner:nil];
|
||||
[pasteboard setString:[NSString stringWithUTF8String:string]
|
||||
forType:NSPasteboardTypeString];
|
||||
}
|
||||
@@ -2218,6 +2163,7 @@ GLFWAPI void glfwGetCocoaKeyEquivalent(int glfw_key, int glfw_mods, unsigned sho
|
||||
K(';', SEMICOLON);
|
||||
K('[', LEFT_BRACKET);
|
||||
K(']', RIGHT_BRACKET);
|
||||
K('+', PLUS);
|
||||
K('`', GRAVE_ACCENT);
|
||||
K('\\', BACKSLASH);
|
||||
|
||||
|
||||
19
glfw/glfw3.h
vendored
19
glfw/glfw3.h
vendored
@@ -407,6 +407,7 @@ extern "C" {
|
||||
#define GLFW_KEY_GRAVE_ACCENT 96 /* ` */
|
||||
#define GLFW_KEY_WORLD_1 161 /* non-US #1 */
|
||||
#define GLFW_KEY_WORLD_2 162 /* non-US #2 */
|
||||
#define GLFW_KEY_PLUS 163
|
||||
|
||||
/* Function keys */
|
||||
#define GLFW_KEY_ESCAPE 256
|
||||
@@ -1379,7 +1380,13 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int);
|
||||
* @param[in] window The window that received the event.
|
||||
* @param[in] xoffset The scroll offset along the x-axis.
|
||||
* @param[in] yoffset The scroll offset along the y-axis.
|
||||
* @param[in] flags A bit-mask providing extra data about the event. flags & 1 will be true if and only if the offset values are "high-precision". Typically pixel values. Otherwise the offset values are number of lines.
|
||||
* @param[in] flags A bit-mask providing extra data about the event.
|
||||
* flags & 1 will be true if and only if the offset values are "high-precision".
|
||||
* Typically pixel values. Otherwise the offset values are number of lines.
|
||||
* (flags >> 1) & 7 will have value 1 for the start of momentum scrolling,
|
||||
* value 2 for stationary momentum scrolling, value 3 for momentum scrolling
|
||||
* in progress, value 4 for momentum scrolling ended, value 5 for momentum
|
||||
* scrolling cancelled and value 6 if scrolling may begin soon.
|
||||
*
|
||||
* @sa @ref scrolling
|
||||
* @sa @ref glfwSetScrollCallback
|
||||
@@ -2121,9 +2128,9 @@ GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor);
|
||||
|
||||
/*! @brief Generates a gamma ramp and sets it for the specified monitor.
|
||||
*
|
||||
* This function generates a 256-element gamma ramp from the specified exponent
|
||||
* and then calls @ref glfwSetGammaRamp with it. The value must be a finite
|
||||
* number greater than zero.
|
||||
* This function generates an appropriately sized gamma ramp from the
|
||||
* specified exponent and then calls @ref glfwSetGammaRamp with it. The value
|
||||
* must be a finite number greater than zero.
|
||||
*
|
||||
* The software controlled gamma ramp is applied _in addition_ to the hardware
|
||||
* gamma correction, which today is usually an approximation of sRGB gamma.
|
||||
@@ -2202,8 +2209,8 @@ GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor);
|
||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
|
||||
* GLFW_PLATFORM_ERROR.
|
||||
*
|
||||
* @remark Gamma ramp sizes other than 256 are not supported by all platforms
|
||||
* or graphics hardware.
|
||||
* @remark The size of the specified gamma ramp should match the size of the
|
||||
* current ramp for that monitor.
|
||||
*
|
||||
* @remark @win32 The gamma ramp size must be 256.
|
||||
*
|
||||
|
||||
1
glfw/input.c
vendored
1
glfw/input.c
vendored
@@ -487,6 +487,7 @@ const char* _glfwGetKeyName(int key)
|
||||
case GLFW_KEY_SLASH: return "SLASH";
|
||||
case GLFW_KEY_WORLD_1: return "WORLD 1";
|
||||
case GLFW_KEY_WORLD_2: return "WORLD 2";
|
||||
case GLFW_KEY_PLUS: return "PLUS";
|
||||
|
||||
// Function keys
|
||||
case GLFW_KEY_ESCAPE: return "ESCAPE";
|
||||
|
||||
2
glfw/internal.h
vendored
2
glfw/internal.h
vendored
@@ -619,7 +619,7 @@ void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
|
||||
float* xscale, float* yscale);
|
||||
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count);
|
||||
void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode);
|
||||
void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp);
|
||||
GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp);
|
||||
void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp);
|
||||
|
||||
void _glfwPlatformSetClipboardString(const char* string);
|
||||
|
||||
27
glfw/monitor.c
vendored
27
glfw/monitor.c
vendored
@@ -427,11 +427,11 @@ GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* handle)
|
||||
|
||||
GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma)
|
||||
{
|
||||
int i;
|
||||
unsigned short values[256];
|
||||
unsigned int i;
|
||||
unsigned short* values;
|
||||
GLFWgammaramp ramp;
|
||||
const GLFWgammaramp* original;
|
||||
assert(handle != NULL);
|
||||
assert(gamma == gamma);
|
||||
assert(gamma >= 0.f);
|
||||
assert(gamma <= FLT_MAX);
|
||||
|
||||
@@ -442,13 +442,18 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma)
|
||||
_glfwInputError(GLFW_INVALID_VALUE, "Invalid gamma value %f", gamma);
|
||||
return;
|
||||
}
|
||||
original = glfwGetGammaRamp(handle);
|
||||
if (!original)
|
||||
return;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
values = calloc(original->size, sizeof(unsigned short));
|
||||
|
||||
for (i = 0; i < original->size; i++)
|
||||
{
|
||||
float value;
|
||||
|
||||
// Calculate intensity
|
||||
value = i / 255.f;
|
||||
value = i / (float) (original->size - 1);
|
||||
// Apply gamma curve
|
||||
value = powf(value, 1.f / gamma) * 65535.f + 0.5f;
|
||||
|
||||
@@ -462,9 +467,10 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma)
|
||||
ramp.red = values;
|
||||
ramp.green = values;
|
||||
ramp.blue = values;
|
||||
ramp.size = 256;
|
||||
ramp.size = original->size;
|
||||
|
||||
glfwSetGammaRamp(handle, &ramp);
|
||||
free(values);
|
||||
}
|
||||
|
||||
GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* handle)
|
||||
@@ -475,7 +481,8 @@ GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* handle)
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
|
||||
_glfwFreeGammaArrays(&monitor->currentRamp);
|
||||
_glfwPlatformGetGammaRamp(monitor, &monitor->currentRamp);
|
||||
if (!_glfwPlatformGetGammaRamp(monitor, &monitor->currentRamp))
|
||||
return NULL;
|
||||
|
||||
return &monitor->currentRamp;
|
||||
}
|
||||
@@ -501,8 +508,10 @@ GLFWAPI void glfwSetGammaRamp(GLFWmonitor* handle, const GLFWgammaramp* ramp)
|
||||
_GLFW_REQUIRE_INIT();
|
||||
|
||||
if (!monitor->originalRamp.size)
|
||||
_glfwPlatformGetGammaRamp(monitor, &monitor->originalRamp);
|
||||
{
|
||||
if (!_glfwPlatformGetGammaRamp(monitor, &monitor->originalRamp))
|
||||
return;
|
||||
}
|
||||
|
||||
_glfwPlatformSetGammaRamp(monitor, ramp);
|
||||
}
|
||||
|
||||
|
||||
4
glfw/null_monitor.c
vendored
4
glfw/null_monitor.c
vendored
@@ -58,11 +58,11 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode)
|
||||
{
|
||||
}
|
||||
|
||||
void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
|
||||
GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
|
||||
{
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
3
glfw/osmesa_context.c
vendored
3
glfw/osmesa_context.c
vendored
@@ -240,7 +240,7 @@ GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window,
|
||||
if (ctxconfig->forward)
|
||||
{
|
||||
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
|
||||
"OSMesa: Foward-compatible contexts not supported");
|
||||
"OSMesa: Forward-compatible contexts not supported");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
@@ -367,4 +367,3 @@ GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* handle)
|
||||
|
||||
return window->context.osmesa.handle;
|
||||
}
|
||||
|
||||
|
||||
5
glfw/win32_monitor.c
vendored
5
glfw/win32_monitor.c
vendored
@@ -455,7 +455,7 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode)
|
||||
&mode->blueBits);
|
||||
}
|
||||
|
||||
void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
|
||||
GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
|
||||
{
|
||||
HDC dc;
|
||||
WORD values[768];
|
||||
@@ -469,6 +469,8 @@ void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
|
||||
memcpy(ramp->red, values + 0, 256 * sizeof(unsigned short));
|
||||
memcpy(ramp->green, values + 256, 256 * sizeof(unsigned short));
|
||||
memcpy(ramp->blue, values + 512, 256 * sizeof(unsigned short));
|
||||
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
|
||||
@@ -510,4 +512,3 @@ GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* handle)
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
return monitor->win32.publicDisplayName;
|
||||
}
|
||||
|
||||
|
||||
9
glfw/wl_monitor.c
vendored
9
glfw/wl_monitor.c
vendored
@@ -180,18 +180,17 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode)
|
||||
*mode = monitor->modes[monitor->wl.currentMode];
|
||||
}
|
||||
|
||||
void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
|
||||
GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
|
||||
{
|
||||
// TODO
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Gamma ramp getting not supported yet");
|
||||
"Wayland: Gamma ramp access is not available");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
|
||||
{
|
||||
// TODO
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Gamma ramp setting not supported yet");
|
||||
"Wayland: Gamma ramp access is not available");
|
||||
}
|
||||
|
||||
|
||||
|
||||
14
glfw/x11_monitor.c
vendored
14
glfw/x11_monitor.c
vendored
@@ -433,7 +433,7 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode)
|
||||
}
|
||||
}
|
||||
|
||||
void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
|
||||
GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
|
||||
{
|
||||
if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken)
|
||||
{
|
||||
@@ -449,6 +449,7 @@ void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
|
||||
memcpy(ramp->blue, gamma->blue, size * sizeof(unsigned short));
|
||||
|
||||
XRRFreeGamma(gamma);
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
else if (_glfw.x11.vidmode.available)
|
||||
{
|
||||
@@ -460,6 +461,13 @@ void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
|
||||
XF86VidModeGetGammaRamp(_glfw.x11.display,
|
||||
_glfw.x11.screen,
|
||||
ramp->size, ramp->red, ramp->green, ramp->blue);
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"X11: Gamma ramp access not supported by server");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,6 +500,10 @@ void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
|
||||
(unsigned short*) ramp->green,
|
||||
(unsigned short*) ramp->blue);
|
||||
}
|
||||
else {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"X11: Gamma ramp access not supported by server");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
3
glfw/x11_platform.h
vendored
3
glfw/x11_platform.h
vendored
@@ -198,9 +198,6 @@ typedef struct _GLFWwindowX11
|
||||
// The last position the cursor was warped to by GLFW
|
||||
int warpCursorPosX, warpCursorPosY;
|
||||
|
||||
// The time of the last KeyPress event
|
||||
Time lastKeyTime;
|
||||
|
||||
} _GLFWwindowX11;
|
||||
|
||||
// X11-specific global data
|
||||
|
||||
4
glfw/x11_window.c
vendored
4
glfw/x11_window.c
vendored
@@ -2244,10 +2244,14 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!window->resizable)
|
||||
updateNormalHints(window, width, height);
|
||||
|
||||
XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
|
||||
xpos, ypos, width, height);
|
||||
}
|
||||
|
||||
XFlush(_glfw.x11.display);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
1
glfw/xkb_glfw.c
vendored
1
glfw/xkb_glfw.c
vendored
@@ -46,6 +46,7 @@
|
||||
S(bracketleft, LEFT_BRACKET); \
|
||||
S(backslash, BACKSLASH); \
|
||||
S(bracketright, RIGHT_BRACKET); \
|
||||
S(plus, PLUS); \
|
||||
S(grave, GRAVE_ACCENT); \
|
||||
S(Escape, ESCAPE); \
|
||||
S(Return, ENTER); \
|
||||
|
||||
@@ -208,7 +208,7 @@ class DiffHandler(Handler):
|
||||
break
|
||||
|
||||
if num is not None:
|
||||
self.scroll_pos = min(num, self.max_scroll_pos)
|
||||
self.scroll_pos = max(0, min(num, self.max_scroll_pos))
|
||||
|
||||
@property
|
||||
def num_lines(self):
|
||||
|
||||
@@ -18,7 +18,7 @@ worker_processes = []
|
||||
|
||||
|
||||
def find_differ():
|
||||
if shutil.which('git'):
|
||||
if shutil.which('git') and subprocess.Popen(['git', '--help'], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL).wait() == 0:
|
||||
return GIT_DIFF
|
||||
if shutil.which('diff'):
|
||||
return DIFF_DIFF
|
||||
|
||||
@@ -315,8 +315,9 @@ def run(args, text):
|
||||
return
|
||||
|
||||
largest_index = all_marks[-1].index
|
||||
offset = max(0, args.hints_offset)
|
||||
for m in all_marks:
|
||||
m.index = largest_index - m.index
|
||||
m.index = largest_index - m.index + offset
|
||||
index_map = {m.index: m for m in all_marks}
|
||||
except Exception:
|
||||
import traceback
|
||||
@@ -378,6 +379,13 @@ default=auto
|
||||
choices=auto,always,never
|
||||
Add trailing space after matched text. Defaults to auto, which adds the space
|
||||
when used together with --multiple.
|
||||
|
||||
|
||||
--hints-offset
|
||||
default=1
|
||||
type=int
|
||||
The offset (from zero) at which to start hint numbering. Note that only numbers
|
||||
greater than zero are respected.
|
||||
'''.format(','.join(sorted(URL_PREFIXES))).format
|
||||
help_text = 'Select text from the screen using the keyboard. Defaults to searching for URLs.'
|
||||
usage = ''
|
||||
|
||||
@@ -81,6 +81,14 @@ type=bool-set
|
||||
Print out the window size as :italic:`widthxheight` (in pixels) and quit. This is a
|
||||
convenience method to query the window size if using kitty icat from a
|
||||
scripting language that cannot make termios calls.
|
||||
|
||||
|
||||
--stdin
|
||||
type=choices
|
||||
choices=detect,yes,no
|
||||
default=detect
|
||||
Read image data from stdin. The default is to do it automatically, when STDIN is not a terminal,
|
||||
but you can turn it off or on explicitly, if needed.
|
||||
'''
|
||||
|
||||
|
||||
@@ -269,9 +277,10 @@ def main(args=sys.argv):
|
||||
if not sys.stdout.isatty():
|
||||
sys.stdout = open(os.ctermid(), 'w')
|
||||
stdin_data = None
|
||||
if not sys.stdin.isatty():
|
||||
if args.stdin == 'yes' or (not sys.stdin.isatty() and args.stdin == 'detect'):
|
||||
stdin_data = sys.stdin.buffer.read()
|
||||
items.insert(0, stdin_data)
|
||||
if stdin_data:
|
||||
items.insert(0, stdin_data)
|
||||
sys.stdin.close()
|
||||
sys.stdin = open(os.ctermid(), 'r')
|
||||
|
||||
@@ -307,7 +316,7 @@ def main(args=sys.argv):
|
||||
raise SystemExit('You must specify at least one file to cat')
|
||||
if args.place:
|
||||
if len(items) > 1 or (isinstance(items[0], str) and os.path.isdir(items[0])):
|
||||
raise SystemExit('The --place option can only be used with a single image')
|
||||
raise SystemExit(f'The --place option can only be used with a single image, not {items}')
|
||||
sys.stdout.buffer.write(b'\0337') # save cursor
|
||||
for item in items:
|
||||
is_tempfile = False
|
||||
|
||||
@@ -153,8 +153,9 @@ class Loop:
|
||||
def __init__(self,
|
||||
sanitize_bracketed_paste='[\x03\x04\x0e\x0f\r\x07\x7f\x8d\x8e\x8f\x90\x9b\x9d\x9e\x9f]'):
|
||||
if is_macos:
|
||||
# On macOS PTY devices are not supported by the KqueueSelector
|
||||
self.asycio_loop = asyncio.SelectorEventLoop(selectors.PollSelector())
|
||||
# On macOS PTY devices are not supported by the KqueueSelector and
|
||||
# the PollSelector is broken, causes 100% CPU usage
|
||||
self.asycio_loop = asyncio.SelectorEventLoop(selectors.SelectSelector())
|
||||
asyncio.set_event_loop(self.asycio_loop)
|
||||
else:
|
||||
self.asycio_loop = asyncio.get_event_loop()
|
||||
|
||||
@@ -4,5 +4,5 @@ in vec3 color;
|
||||
out vec4 final_color;
|
||||
|
||||
void main() {
|
||||
final_color = vec4(color, background_opacity);
|
||||
final_color = vec4(color * background_opacity, background_opacity);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ from functools import partial
|
||||
from gettext import gettext as _
|
||||
from weakref import WeakValueDictionary
|
||||
|
||||
from .child import cached_process_data
|
||||
from .cli import create_opts, parse_args
|
||||
from .conf.utils import to_cmdline
|
||||
from .config import initial_window_size_func, prepare_config_file_for_editing
|
||||
@@ -32,9 +33,9 @@ from .rgb import Color, color_from_int
|
||||
from .session import create_session
|
||||
from .tabs import SpecialWindow, SpecialWindowInstance, TabManager
|
||||
from .utils import (
|
||||
get_editor, get_primary_selection, log_error, open_url, parse_address_spec,
|
||||
remove_socket_file, safe_print, set_primary_selection, single_instance,
|
||||
startup_notification_handler
|
||||
get_editor, get_primary_selection, is_path_in_temp_dir, log_error,
|
||||
open_url, parse_address_spec, remove_socket_file, safe_print,
|
||||
set_primary_selection, single_instance, startup_notification_handler
|
||||
)
|
||||
|
||||
|
||||
@@ -106,6 +107,7 @@ class Boss:
|
||||
set_draw_minimal_borders(opts)
|
||||
self.window_id_map = WeakValueDictionary()
|
||||
self.startup_colors = {k: opts[k] for k in opts if isinstance(opts[k], Color)}
|
||||
self.startup_cursor_text_color = opts.cursor_text_color
|
||||
self.pending_sequences = None
|
||||
self.cached_values = cached_values
|
||||
self.os_window_map = {}
|
||||
@@ -149,14 +151,15 @@ class Boss:
|
||||
return os_window_id
|
||||
|
||||
def list_os_windows(self):
|
||||
active_tab, active_window = self.active_tab, self.active_window
|
||||
active_tab_manager = self.active_tab_manager
|
||||
for os_window_id, tm in self.os_window_map.items():
|
||||
yield {
|
||||
'id': os_window_id,
|
||||
'is_focused': tm is active_tab_manager,
|
||||
'tabs': list(tm.list_tabs(active_tab, active_window)),
|
||||
}
|
||||
with cached_process_data():
|
||||
active_tab, active_window = self.active_tab, self.active_window
|
||||
active_tab_manager = self.active_tab_manager
|
||||
for os_window_id, tm in self.os_window_map.items():
|
||||
yield {
|
||||
'id': os_window_id,
|
||||
'is_focused': tm is active_tab_manager,
|
||||
'tabs': list(tm.list_tabs(active_tab, active_window)),
|
||||
}
|
||||
|
||||
@property
|
||||
def all_tab_managers(self):
|
||||
@@ -253,7 +256,7 @@ class Boss:
|
||||
|
||||
def new_os_window_with_cwd(self, *args):
|
||||
w = self.active_window_for_cwd
|
||||
cwd_from = w.child.pid if w is not None else None
|
||||
cwd_from = w.child.pid_for_cwd if w is not None else None
|
||||
self._new_os_window(args, cwd_from)
|
||||
|
||||
def add_child(self, window):
|
||||
@@ -852,7 +855,7 @@ class Boss:
|
||||
def pipe(self, source, dest, exe, *args):
|
||||
cmd = [exe] + list(args)
|
||||
window = self.active_window
|
||||
cwd_from = window.child.pid if window else None
|
||||
cwd_from = window.child.pid_for_cwd if window else None
|
||||
|
||||
def create_window():
|
||||
return self.special_window_for_cmd(
|
||||
@@ -920,7 +923,7 @@ class Boss:
|
||||
|
||||
def new_tab_with_cwd(self, *args):
|
||||
w = self.active_window_for_cwd
|
||||
cwd_from = w.child.pid if w is not None else None
|
||||
cwd_from = w.child.pid_for_cwd if w is not None else None
|
||||
self._create_tab(args, cwd_from=cwd_from)
|
||||
|
||||
def _new_window(self, args, cwd_from=None):
|
||||
@@ -938,7 +941,7 @@ class Boss:
|
||||
w = self.active_window_for_cwd
|
||||
if w is None:
|
||||
return self.new_window(*args)
|
||||
cwd_from = w.child.pid if w is not None else None
|
||||
cwd_from = w.child.pid_for_cwd if w is not None else None
|
||||
self._new_window(args, cwd_from=cwd_from)
|
||||
|
||||
def move_tab_forward(self):
|
||||
@@ -951,11 +954,20 @@ class Boss:
|
||||
if tm is not None:
|
||||
tm.move_tab(-1)
|
||||
|
||||
def patch_colors(self, spec, configured=False):
|
||||
def patch_colors(self, spec, cursor_text_color, configured=False):
|
||||
if configured:
|
||||
for k, v in spec.items():
|
||||
if hasattr(self.opts, k):
|
||||
setattr(self.opts, k, color_from_int(v))
|
||||
if cursor_text_color is not False:
|
||||
self.opts.cursor_text_color = cursor_text_color
|
||||
for tm in self.all_tab_managers:
|
||||
tm.tab_bar.patch_colors(spec)
|
||||
patch_global_colors(spec, configured)
|
||||
|
||||
def safe_delete_temp_file(self, path):
|
||||
if is_path_in_temp_dir(path):
|
||||
try:
|
||||
os.remove(path)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
@@ -72,7 +72,11 @@ vec4 calculate_foreground() {
|
||||
void main() {
|
||||
#ifdef BACKGROUND
|
||||
#ifdef TRANSPARENT
|
||||
final_color = vec4(background.rgb * bg_alpha, bg_alpha);
|
||||
// bg_alpha is doubled to match rendering in the SIMPLE case
|
||||
// and also rendering of the margin/padding, see https://github.com/kovidgoyal/kitty/pull/1291
|
||||
// to test use background_opacity, window_margin_width and icat to display
|
||||
// an image.
|
||||
final_color = vec4(background.rgb * bg_alpha * bg_alpha, bg_alpha);
|
||||
#else
|
||||
final_color = vec4(background.rgb, 1.0f);
|
||||
#endif
|
||||
|
||||
@@ -5,13 +5,12 @@
|
||||
#define REVERSE_SHIFT {REVERSE_SHIFT}
|
||||
#define STRIKE_SHIFT {STRIKE_SHIFT}
|
||||
#define DIM_SHIFT {DIM_SHIFT}
|
||||
#define CURSOR_TEXT_COLOR {CURSOR_TEXT_COLOR}
|
||||
|
||||
// Inputs {{{
|
||||
layout(std140) uniform CellRenderData {
|
||||
float xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity;
|
||||
float xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity, cursor_text_uses_bg;
|
||||
|
||||
uint default_fg, default_bg, highlight_fg, highlight_bg, cursor_color, url_color, url_style, inverted;
|
||||
uint default_fg, default_bg, highlight_fg, highlight_bg, cursor_color, cursor_text_color, url_color, url_style, inverted;
|
||||
|
||||
uint xnum, ynum, cursor_fg_sprite_idx;
|
||||
float cursor_x, cursor_y, cursor_w;
|
||||
@@ -182,8 +181,9 @@ void main() {
|
||||
|
||||
// Cursor
|
||||
cursor_color_vec = vec4(color_to_vec(cursor_color), 1.0);
|
||||
foreground = choose_color(cell_has_block_cursor, CURSOR_TEXT_COLOR, foreground);
|
||||
decoration_fg = choose_color(cell_has_block_cursor, CURSOR_TEXT_COLOR, decoration_fg);
|
||||
vec3 final_cursor_text_color = mix(color_to_vec(cursor_text_color), bg, cursor_text_uses_bg);
|
||||
foreground = choose_color(cell_has_block_cursor, final_cursor_text_color, foreground);
|
||||
decoration_fg = choose_color(cell_has_block_cursor, final_cursor_text_color, decoration_fg);
|
||||
cursor_pos = to_sprite_pos(pos, cursor_fg_sprite_idx * uint(cell_has_cursor), ZERO, ZERO);
|
||||
#endif
|
||||
// }}}
|
||||
|
||||
@@ -372,7 +372,7 @@ parse_input(ChildMonitor *self) {
|
||||
}
|
||||
|
||||
if (UNLIKELY(kill_signal_received)) {
|
||||
global_state.close_all_windows = true;
|
||||
global_state.terminate = true;
|
||||
} else {
|
||||
count = self->count;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
@@ -737,7 +737,6 @@ process_pending_resizes(double now) {
|
||||
static inline void
|
||||
close_all_windows() {
|
||||
for (size_t w = 0; w < global_state.num_os_windows; w++) mark_os_window_for_close(&global_state.os_windows[w], true);
|
||||
global_state.close_all_windows = false;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
@@ -772,6 +771,9 @@ static unsigned int cocoa_pending_actions = 0;
|
||||
void
|
||||
set_cocoa_pending_action(CocoaPendingAction action) {
|
||||
cocoa_pending_actions |= action;
|
||||
// The main loop may be blocking on the event queue, if e.g. unfocused.
|
||||
// Unjam it so the pending action is processed right now.
|
||||
unjam_event_loop();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -793,7 +795,13 @@ main_loop(ChildMonitor *self, PyObject *a UNUSED) {
|
||||
}
|
||||
#endif
|
||||
parse_input(self);
|
||||
if (global_state.close_all_windows) close_all_windows();
|
||||
if (global_state.terminate) {
|
||||
global_state.terminate = false;
|
||||
close_all_windows();
|
||||
#ifdef __APPLE__
|
||||
request_application_quit();
|
||||
#endif
|
||||
}
|
||||
has_open_windows = process_pending_closes(self);
|
||||
}
|
||||
if (PyErr_Occurred()) return NULL;
|
||||
|
||||
@@ -115,6 +115,9 @@ spawn(PyObject *self UNUSED, PyObject *args) {
|
||||
for (int c = 3; c < 201; c++) close(c);
|
||||
|
||||
environ = env;
|
||||
// for some reason SIGPIPE is set to SIG_IGN, so reset it, needed by bash,
|
||||
// which does not reset signal handlers on its own
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
execvp(exe, argv);
|
||||
// Report the failure and exec a shell instead, so that we are not left
|
||||
// with a forked but not exec'ed process
|
||||
|
||||
@@ -4,17 +4,28 @@
|
||||
|
||||
import fcntl
|
||||
import os
|
||||
from collections import defaultdict
|
||||
from contextlib import contextmanager
|
||||
|
||||
import kitty.fast_data_types as fast_data_types
|
||||
|
||||
from .constants import is_macos, shell_path, terminfo_dir
|
||||
|
||||
if is_macos:
|
||||
from kitty.fast_data_types import cmdline_of_process, cwd_of_process as _cwd, environ_of_process as _environ_of_process
|
||||
from kitty.fast_data_types import (
|
||||
cmdline_of_process, cwd_of_process as _cwd, environ_of_process as _environ_of_process,
|
||||
process_group_map as _process_group_map
|
||||
)
|
||||
|
||||
def cwd_of_process(pid):
|
||||
return os.path.realpath(_cwd(pid))
|
||||
|
||||
def process_group_map():
|
||||
ans = defaultdict(list)
|
||||
for pid, pgid in _process_group_map():
|
||||
ans[pgid].append(pid)
|
||||
return ans
|
||||
|
||||
else:
|
||||
|
||||
def cmdline_of_process(pid):
|
||||
@@ -27,6 +38,46 @@ else:
|
||||
def _environ_of_process(pid):
|
||||
return open('/proc/{}/environ'.format(pid), 'rb').read().decode('utf-8')
|
||||
|
||||
def process_group_map():
|
||||
ans = defaultdict(list)
|
||||
for x in os.listdir('/proc'):
|
||||
try:
|
||||
pid = int(x)
|
||||
except Exception:
|
||||
continue
|
||||
try:
|
||||
raw = open('/proc/' + x + '/stat', 'rb').read().decode('utf-8')
|
||||
except EnvironmentError:
|
||||
continue
|
||||
try:
|
||||
q = int(raw.split(' ', 5)[4])
|
||||
except Exception:
|
||||
continue
|
||||
ans[q].append(pid)
|
||||
return ans
|
||||
|
||||
|
||||
def processes_in_group(grp):
|
||||
gmap = getattr(process_group_map, 'cached_map', None)
|
||||
if gmap is None:
|
||||
try:
|
||||
gmap = process_group_map()
|
||||
except Exception:
|
||||
gmap = {}
|
||||
return gmap.get(grp, [])
|
||||
|
||||
|
||||
@contextmanager
|
||||
def cached_process_data():
|
||||
try:
|
||||
process_group_map.cached_map = process_group_map()
|
||||
except Exception:
|
||||
process_group_map.cached_map = {}
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
process_group_map.cached_map = None
|
||||
|
||||
|
||||
def parse_environ_block(data):
|
||||
"""Parse a C environ block of environment variables into a dictionary."""
|
||||
@@ -142,6 +193,28 @@ class Child:
|
||||
os.close(self.terminal_ready_fd)
|
||||
self.terminal_ready_fd = -1
|
||||
|
||||
@property
|
||||
def foreground_processes(self):
|
||||
try:
|
||||
pgrp = os.tcgetpgrp(self.child_fd)
|
||||
foreground_processes = processes_in_group(pgrp) if pgrp >= 0 else []
|
||||
|
||||
def process_desc(pid):
|
||||
ans = {'pid': pid}
|
||||
try:
|
||||
ans['cmdline'] = cmdline_of_process(pid)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
ans['cwd'] = cwd_of_process(pid) or None
|
||||
except Exception:
|
||||
pass
|
||||
return ans
|
||||
|
||||
return list(map(process_desc, foreground_processes))
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
@property
|
||||
def cmdline(self):
|
||||
try:
|
||||
@@ -162,3 +235,21 @@ class Child:
|
||||
return cwd_of_process(self.pid)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@property
|
||||
def pid_for_cwd(self):
|
||||
try:
|
||||
pgrp = os.tcgetpgrp(self.child_fd)
|
||||
foreground_processes = processes_in_group(pgrp) if pgrp >= 0 else []
|
||||
if len(foreground_processes) == 1:
|
||||
return foreground_processes[0]
|
||||
except Exception:
|
||||
pass
|
||||
return self.pid
|
||||
|
||||
@property
|
||||
def foreground_cwd(self):
|
||||
try:
|
||||
return cwd_of_process(self.pid_for_cwd) or None
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -126,7 +126,7 @@ def set_font_size(boss, window, payload):
|
||||
' and :italic:`\\u21fa` to send unicode characters. If you use the :option:`kitty @ send-text --match` option'
|
||||
' the text will be sent to all matched windows. By default, text is sent to'
|
||||
' only the currently active window.',
|
||||
options_spec=MATCH_WINDOW_OPTION + '''\n
|
||||
options_spec=MATCH_WINDOW_OPTION + '\n\n' + MATCH_TAB_OPTION.replace('--match -m', '--match-tab -t') + '''\n
|
||||
--stdin
|
||||
type=bool-set
|
||||
Read the text to be sent from :italic:`stdin`. Note that in this case the text is sent as is,
|
||||
@@ -142,7 +142,7 @@ are sent as is, not interpreted for escapes.
|
||||
)
|
||||
def cmd_send_text(global_opts, opts, args):
|
||||
limit = 1024
|
||||
ret = {'match': opts.match, 'is_binary': False}
|
||||
ret = {'match': opts.match, 'is_binary': False, 'match_tab': opts.match_tab}
|
||||
|
||||
def pipe():
|
||||
ret['is_binary'] = True
|
||||
@@ -209,6 +209,13 @@ def send_text(boss, window, payload):
|
||||
match = payload['match']
|
||||
if match:
|
||||
windows = tuple(boss.match_windows(match))
|
||||
if payload['match_tab']:
|
||||
windows = []
|
||||
tabs = tuple(boss.match_tabs(payload['match_tab']))
|
||||
if not tabs:
|
||||
raise MatchError(payload['match_tab'], 'tabs')
|
||||
for tab in tabs:
|
||||
windows += tuple(tab)
|
||||
data = payload['text'].encode('utf-8') if payload['is_binary'] else parse_send_text_bytes(payload['text'])
|
||||
for window in windows:
|
||||
if window is not None:
|
||||
@@ -684,7 +691,7 @@ this option, any color arguments are ignored and --configured and --all are impl
|
||||
)
|
||||
def cmd_set_colors(global_opts, opts, args):
|
||||
from .rgb import color_as_int, Color
|
||||
colors = {}
|
||||
colors, cursor_text_color = {}, False
|
||||
if not opts.reset:
|
||||
for spec in args:
|
||||
if '=' in spec:
|
||||
@@ -692,15 +699,17 @@ def cmd_set_colors(global_opts, opts, args):
|
||||
else:
|
||||
with open(os.path.expanduser(spec), encoding='utf-8', errors='replace') as f:
|
||||
colors.update(parse_config(f))
|
||||
cursor_text_color = colors.pop('cursor_text_color', False)
|
||||
colors = {k: color_as_int(v) for k, v in colors.items() if isinstance(v, Color)}
|
||||
return {
|
||||
'title': ' '.join(args), 'match_window': opts.match, 'match_tab': opts.match_tab,
|
||||
'all': opts.all or opts.reset, 'configured': opts.configured or opts.reset, 'colors': colors, 'reset': opts.reset
|
||||
'title': ' '.join(args), 'match_window': opts.match, 'match_tab': opts.match_tab,
|
||||
'all': opts.all or opts.reset, 'configured': opts.configured or opts.reset,
|
||||
'colors': colors, 'reset': opts.reset, 'cursor_text_color': cursor_text_color
|
||||
}
|
||||
|
||||
|
||||
def set_colors(boss, window, payload):
|
||||
from .rgb import color_as_int
|
||||
from .rgb import color_as_int, Color
|
||||
if payload['all']:
|
||||
windows = tuple(boss.all_windows)
|
||||
else:
|
||||
@@ -717,10 +726,14 @@ def set_colors(boss, window, payload):
|
||||
windows += tuple(tab)
|
||||
if payload['reset']:
|
||||
payload['colors'] = {k: color_as_int(v) for k, v in boss.startup_colors.items()}
|
||||
payload['cursor_text_color'] = boss.startup_cursor_text_color
|
||||
profiles = tuple(w.screen.color_profile for w in windows)
|
||||
from .fast_data_types import patch_color_profiles
|
||||
patch_color_profiles(payload['colors'], profiles, payload['configured'])
|
||||
boss.patch_colors(payload['colors'], payload['configured'])
|
||||
cursor_text_color = payload.get('cursor_text_color', False)
|
||||
if isinstance(cursor_text_color, (tuple, list, Color)):
|
||||
cursor_text_color = color_as_int(Color(*cursor_text_color))
|
||||
patch_color_profiles(payload['colors'], cursor_text_color, profiles, payload['configured'])
|
||||
boss.patch_colors(payload['colors'], cursor_text_color, payload['configured'])
|
||||
default_bg_changed = 'background' in payload['colors']
|
||||
for w in windows:
|
||||
if default_bg_changed:
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <AvailabilityMacros.h>
|
||||
// Needed for _NSGetProgname
|
||||
#include <crt_externs.h>
|
||||
#include <objc/runtime.h>
|
||||
|
||||
#if (MAC_OS_X_VERSION_MAX_ALLOWED < 101200)
|
||||
#define NSWindowStyleMaskResizable NSResizableWindowMask
|
||||
@@ -99,6 +100,21 @@ cocoa_set_new_window_trigger(PyObject *self UNUSED, PyObject *args) {
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
|
||||
// Implementation of applicationDockMenu: for the app delegate
|
||||
static NSMenu *dockMenu = nil;
|
||||
static NSMenu *
|
||||
get_dock_menu(id self UNUSED, SEL _cmd UNUSED, NSApplication *sender UNUSED) {
|
||||
if (!dockMenu) {
|
||||
GlobalMenuTarget *global_menu_target = [GlobalMenuTarget shared_instance];
|
||||
dockMenu = [[NSMenu alloc] init];
|
||||
NSMenuItem *newWindowItem = [dockMenu addItemWithTitle:@"New OS window"
|
||||
action:@selector(new_os_window:)
|
||||
keyEquivalent:@""];
|
||||
[newWindowItem setTarget:global_menu_target];
|
||||
}
|
||||
return dockMenu;
|
||||
}
|
||||
|
||||
void
|
||||
cocoa_create_global_menu(void) {
|
||||
NSString* app_name = find_app_name();
|
||||
@@ -183,6 +199,12 @@ cocoa_create_global_menu(void) {
|
||||
}
|
||||
|
||||
[bar release];
|
||||
|
||||
class_addMethod(
|
||||
object_getClass([NSApp delegate]),
|
||||
@selector(applicationDockMenu:),
|
||||
(IMP)get_dock_menu,
|
||||
"@@:@");
|
||||
}
|
||||
|
||||
void
|
||||
@@ -292,6 +314,12 @@ cocoa_set_titlebar_color(void *w, color_type titlebar_color)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cleanup() {
|
||||
if (dockMenu) [dockMenu release];
|
||||
dockMenu = nil;
|
||||
}
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
{"cocoa_get_lang", (PyCFunction)cocoa_get_lang, METH_NOARGS, ""},
|
||||
{"cocoa_set_new_window_trigger", (PyCFunction)cocoa_set_new_window_trigger, METH_VARARGS, ""},
|
||||
@@ -301,5 +329,9 @@ static PyMethodDef module_methods[] = {
|
||||
bool
|
||||
init_cocoa(PyObject *module) {
|
||||
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
|
||||
if (Py_AtExit(cleanup) != 0) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Failed to register the cocoa_window at exit handler");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -100,8 +100,8 @@ update_ansi_color_table(ColorProfile *self, PyObject *val) {
|
||||
|
||||
static PyObject*
|
||||
patch_color_profiles(PyObject *module UNUSED, PyObject *args) {
|
||||
PyObject *spec, *profiles, *v; ColorProfile *self; int change_configured;
|
||||
if (!PyArg_ParseTuple(args, "O!O!p", &PyDict_Type, &spec, &PyTuple_Type, &profiles, &change_configured)) return NULL;
|
||||
PyObject *spec, *profiles, *v; ColorProfile *self; int change_configured; PyObject *cursor_text_color;
|
||||
if (!PyArg_ParseTuple(args, "O!OO!p", &PyDict_Type, &spec, &cursor_text_color, &PyTuple_Type, &profiles, &change_configured)) return NULL;
|
||||
char key[32] = {0};
|
||||
for (size_t i = 0; i < arraysz(FG_BG_256); i++) {
|
||||
snprintf(key, sizeof(key) - 1, "color%zu", i);
|
||||
@@ -131,6 +131,22 @@ patch_color_profiles(PyObject *module UNUSED, PyObject *args) {
|
||||
S(foreground, default_fg); S(background, default_bg); S(cursor, cursor_color);
|
||||
S(selection_foreground, highlight_fg); S(selection_background, highlight_bg);
|
||||
#undef S
|
||||
if (cursor_text_color != Py_False) {
|
||||
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(profiles); i++) {
|
||||
self = (ColorProfile*)PyTuple_GET_ITEM(profiles, i);
|
||||
self->overridden.cursor_text_color = 0x111111;
|
||||
self->overridden.cursor_text_uses_bg = 3;
|
||||
if (cursor_text_color != Py_None) {
|
||||
self->overridden.cursor_text_color = (PyLong_AsUnsignedLong(cursor_text_color) << 8) | 2;
|
||||
self->overridden.cursor_text_uses_bg = 1;
|
||||
}
|
||||
if (change_configured) {
|
||||
self->configured.cursor_text_color = self->overridden.cursor_text_color;
|
||||
self->configured.cursor_text_uses_bg = self->overridden.cursor_text_uses_bg;
|
||||
}
|
||||
self->dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
@@ -149,6 +165,14 @@ colorprofile_to_color(ColorProfile *self, color_type entry, color_type defval) {
|
||||
}
|
||||
}
|
||||
|
||||
float
|
||||
cursor_text_as_bg(ColorProfile *self) {
|
||||
if (self->overridden.cursor_text_uses_bg & 1) {
|
||||
return self->overridden.cursor_text_uses_bg & 2 ? 1.f : 0.f;
|
||||
}
|
||||
return self->configured.cursor_text_uses_bg & 2 ? 1.f : 0.f;
|
||||
}
|
||||
|
||||
|
||||
static PyObject*
|
||||
as_dict(ColorProfile *self, PyObject *args UNUSED) {
|
||||
@@ -174,7 +198,7 @@ as_dict(ColorProfile *self, PyObject *args UNUSED) {
|
||||
if (ret != 0) { Py_CLEAR(ans); return NULL; } \
|
||||
}}
|
||||
D(default_fg, foreground); D(default_bg, background);
|
||||
D(cursor_color, cursor); D(highlight_fg, selection_foreground);
|
||||
D(cursor_color, cursor); D(cursor_text_color, cursor_text); D(highlight_fg, selection_foreground);
|
||||
D(highlight_bg, selection_background);
|
||||
|
||||
#undef D
|
||||
@@ -236,7 +260,11 @@ set_color(ColorProfile *self, PyObject *args) {
|
||||
static PyObject*
|
||||
set_configured_colors(ColorProfile *self, PyObject *args) {
|
||||
#define set_configured_colors_doc "Set the configured colors"
|
||||
if (!PyArg_ParseTuple(args, "II|III", &(self->configured.default_fg), &(self->configured.default_bg), &(self->configured.cursor_color), &(self->configured.highlight_fg), &(self->configured.highlight_bg))) return NULL;
|
||||
if (!PyArg_ParseTuple(
|
||||
args, "II|IIIII",
|
||||
&(self->configured.default_fg), &(self->configured.default_bg),
|
||||
&(self->configured.cursor_color), &(self->configured.cursor_text_color), &(self->configured.cursor_text_uses_bg),
|
||||
&(self->configured.highlight_fg), &(self->configured.highlight_bg))) return NULL;
|
||||
self->dirty = true;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
@@ -286,6 +314,7 @@ default_color_table(PyObject *self UNUSED, PyObject *args UNUSED) {
|
||||
CGETSET(default_fg)
|
||||
CGETSET(default_bg)
|
||||
CGETSET(cursor_color)
|
||||
CGETSET(cursor_text_color)
|
||||
CGETSET(highlight_fg)
|
||||
CGETSET(highlight_bg)
|
||||
|
||||
@@ -293,6 +322,7 @@ static PyGetSetDef getsetters[] = {
|
||||
GETSET(default_fg)
|
||||
GETSET(default_bg)
|
||||
GETSET(cursor_color)
|
||||
GETSET(cursor_text_color)
|
||||
GETSET(highlight_fg)
|
||||
GETSET(highlight_bg)
|
||||
{NULL} /* Sentinel */
|
||||
|
||||
@@ -115,7 +115,7 @@ def float_parse(func, rest):
|
||||
|
||||
@func_with_args('change_font_size')
|
||||
def parse_change_font_size(func, rest):
|
||||
vals = rest.split(' ', 1)
|
||||
vals = rest.strip().split(' ', 1)
|
||||
if len(vals) != 2:
|
||||
log_error('Invalid change_font_size specification: {}, treating it as default'.format(rest))
|
||||
args = [True, None, 0]
|
||||
@@ -125,13 +125,13 @@ def parse_change_font_size(func, rest):
|
||||
if amt[0] in '+-':
|
||||
args[1] = amt[0]
|
||||
amt = amt[1:]
|
||||
args[2] = float(amt)
|
||||
args[2] = float(amt.strip())
|
||||
return func, args
|
||||
|
||||
|
||||
@func_with_args('clear_terminal')
|
||||
def clear_terminal(func, rest):
|
||||
vals = rest.split(' ', 1)
|
||||
vals = rest.strip().split(' ', 1)
|
||||
if len(vals) != 2:
|
||||
log_error('clear_terminal needs two arguments, using defaults')
|
||||
args = ['reset', 'active']
|
||||
@@ -150,6 +150,27 @@ def neighboring_window(func, rest):
|
||||
return func, [rest]
|
||||
|
||||
|
||||
@func_with_args('resize_window')
|
||||
def resize_window(func, rest):
|
||||
vals = rest.strip().split(' ', 1)
|
||||
if len(vals) > 2:
|
||||
log_error('resize_window needs one or two arguments, using defaults')
|
||||
args = ['wider', 1]
|
||||
else:
|
||||
quality = vals[0].lower()
|
||||
if quality not in ('taller', 'shorter', 'wider', 'narrower'):
|
||||
log_error('Invalid quality specification: {}'.format(quality))
|
||||
quality = 'wider'
|
||||
increment = 1
|
||||
if len(vals) == 2:
|
||||
try:
|
||||
increment = int(vals[1])
|
||||
except Exception:
|
||||
log_error('Invalid increment specification: {}'.format(vals[1]))
|
||||
args = [quality, increment]
|
||||
return func, args
|
||||
|
||||
|
||||
@func_with_args('move_window')
|
||||
def move_window(func, rest):
|
||||
rest = rest.lower()
|
||||
@@ -184,7 +205,7 @@ def nth_window(func, rest):
|
||||
|
||||
|
||||
def parse_key_action(action):
|
||||
parts = action.split(' ', 1)
|
||||
parts = action.strip().split(' ', 1)
|
||||
func = parts[0]
|
||||
if len(parts) == 1:
|
||||
return KeyAction(func, ())
|
||||
@@ -195,7 +216,8 @@ def parse_key_action(action):
|
||||
func, args = parser(func, rest)
|
||||
except Exception:
|
||||
log_error('Ignoring invalid key action: {}'.format(action))
|
||||
return KeyAction(func, args)
|
||||
else:
|
||||
return KeyAction(func, args)
|
||||
|
||||
|
||||
all_key_actions = set()
|
||||
|
||||
@@ -169,14 +169,14 @@ Similarly, to switch back to the previous layout::
|
||||
''')],
|
||||
'shortcuts.fonts': [
|
||||
_('Font sizes'), _('''\
|
||||
You can change the font size for all top-level kitty windows at a time
|
||||
You can change the font size for all top-level kitty OS windows at a time
|
||||
or only the current one.
|
||||
'''), _('''\
|
||||
To setup shortcuts for specific font sizes::
|
||||
|
||||
map kitty_mod+f6 change_font_size all 10.0
|
||||
|
||||
To setup shortcuts to change only the current window's font size::
|
||||
To setup shortcuts to change only the current OS window's font size::
|
||||
|
||||
map kitty_mod+f6 change_font_size current 10.0
|
||||
''')],
|
||||
@@ -608,6 +608,13 @@ entries to this list.
|
||||
o('tab_separator', '"{}"'.format(default_tab_separator), option_type=tab_separator, long_text=_('''
|
||||
The separator between tabs in the tab bar when using :code:`separator` as the :opt:`tab_bar_style`.'''))
|
||||
|
||||
o('tab_title_template', '{title}', long_text=_('''
|
||||
A template to render the tab title. The default just renders
|
||||
the title. If you wish to include the tab-index as well,
|
||||
use something like: :code:`{index}: {title}`. Useful
|
||||
if you have shortcuts mapped for :code:`goto_tab N`.
|
||||
'''))
|
||||
|
||||
o('active_tab_foreground', '#000', option_type=to_color, long_text=_('''
|
||||
Tab bar colors and styles'''))
|
||||
o('active_tab_background', '#eee', option_type=to_color)
|
||||
@@ -841,7 +848,9 @@ g('shortcuts.clipboard') # {{{
|
||||
if is_macos:
|
||||
k('copy_to_clipboard', 'cmd+c', 'copy_to_clipboard', _('Copy to clipboard'), add_to_docs=False)
|
||||
k('paste_from_clipboard', 'cmd+v', 'paste_from_clipboard', _('Paste from clipboard'), add_to_docs=False)
|
||||
k('copy_to_clipboard', 'kitty_mod+c', 'copy_to_clipboard', _('Copy to clipboard'))
|
||||
k('copy_to_clipboard', 'kitty_mod+c', 'copy_to_clipboard', _('Copy to clipboard'), long_text=_('''
|
||||
There is also a :code:`copy_or_interrupt` action that can be optionally mapped to :kbd:`Ctrl+c`.
|
||||
It will copy only if there is a selection and send an interrupt otherwise.'''))
|
||||
k('paste_from_clipboard', 'kitty_mod+v', 'paste_from_clipboard', _('Paste from clipboard'))
|
||||
k('paste_from_selection', 'kitty_mod+s', 'paste_from_selection', _('Paste from selection'))
|
||||
k('paste_from_selection', 'shift+insert', 'paste_from_selection', _('Paste from selection'))
|
||||
@@ -906,6 +915,8 @@ if is_macos:
|
||||
k('new_os_window', 'cmd+n', 'new_os_window', _('New OS window'))
|
||||
k('new_os_window', 'kitty_mod+n', 'new_os_window', _('New OS window'))
|
||||
k('close_window', 'kitty_mod+w', 'close_window', _('Close window'))
|
||||
if is_macos:
|
||||
k('close_window', 'cmd+w', 'close_window', _('Close window'), add_to_docs=False)
|
||||
k('next_window', 'kitty_mod+]', 'next_window', _('Next window'))
|
||||
k('previous_window', 'kitty_mod+[', 'previous_window', _('Previous window'))
|
||||
k('move_window_forward', 'kitty_mod+f', 'move_window_forward', _('Move window forward'))
|
||||
@@ -926,12 +937,14 @@ k('tenth_window', 'kitty_mod+0', 'tenth_window', _('Tenth window'))
|
||||
|
||||
g('shortcuts.tab') # {{{
|
||||
if is_macos:
|
||||
k('next_tab', 'ctrl+tab', 'next_tab', _('Next tab'))
|
||||
k('next_tab', 'ctrl+tab', 'next_tab', _('Next tab'), add_to_docs=False)
|
||||
k('next_tab', 'kitty_mod+right', 'next_tab', _('Next tab'))
|
||||
if is_macos:
|
||||
k('previous_tab', 'ctrl+shift+tab', 'previous_tab', _('Previous tab'))
|
||||
k('previous_tab', 'ctrl+shift+tab', 'previous_tab', _('Previous tab'), add_to_docs=False)
|
||||
k('previous_tab', 'kitty_mod+left', 'previous_tab', _('Previous tab'))
|
||||
k('new_tab', 'kitty_mod+t', 'new_tab', _('New tab'))
|
||||
if is_macos:
|
||||
k('new_tab', 'cmd+t', 'new_tab', _('New tab'), add_to_docs=False)
|
||||
k('close_tab', 'kitty_mod+q', 'close_tab', _('Close tab'))
|
||||
k('move_tab_forward', 'kitty_mod+.', 'move_tab_forward', _('Move tab forward'))
|
||||
k('move_tab_backward', 'kitty_mod+,', 'move_tab_backward', _('Move tab backward'))
|
||||
|
||||
@@ -8,7 +8,7 @@ import sys
|
||||
from collections import namedtuple
|
||||
|
||||
appname = 'kitty'
|
||||
version = (0, 13, 1)
|
||||
version = (0, 13, 3)
|
||||
str_version = '.'.join(map(str, version))
|
||||
_plat = sys.platform.lower()
|
||||
is_macos = 'darwin' in _plat
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#define NS_TO_US (1000)
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <libproc.h>
|
||||
#include <mach/mach_time.h>
|
||||
static mach_timebase_info_data_t timebase = {0};
|
||||
|
||||
@@ -54,6 +55,25 @@ user_cache_dir() {
|
||||
return PyUnicode_FromString(buf);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
process_group_map() {
|
||||
int num_of_processes = proc_listallpids(NULL, 0);
|
||||
size_t bufsize = sizeof(pid_t) * (num_of_processes + 1024);
|
||||
pid_t *buf = malloc(bufsize);
|
||||
if (!buf) return PyErr_NoMemory();
|
||||
num_of_processes = proc_listallpids(buf, (int)bufsize);
|
||||
PyObject *ans = PyTuple_New(num_of_processes);
|
||||
if (ans == NULL) { free(buf); return PyErr_NoMemory(); }
|
||||
for (int i = 0; i < num_of_processes; i++) {
|
||||
long pid = buf[i], pgid = getpgid(buf[i]);
|
||||
PyObject *t = Py_BuildValue("ll", pid, pgid);
|
||||
if (t == NULL) { free(buf); Py_DECREF(ans); return NULL; }
|
||||
PyTuple_SET_ITEM(ans, i, t);
|
||||
}
|
||||
free(buf);
|
||||
return ans;
|
||||
}
|
||||
|
||||
#else
|
||||
#include <time.h>
|
||||
static inline double monotonic_() {
|
||||
@@ -177,6 +197,7 @@ static PyMethodDef module_methods[] = {
|
||||
{"redirect_std_streams", (PyCFunction)redirect_std_streams, METH_VARARGS, ""},
|
||||
#ifdef __APPLE__
|
||||
METHODB(user_cache_dir, METH_NOARGS),
|
||||
METHODB(process_group_map, METH_NOARGS),
|
||||
#endif
|
||||
#ifdef WITH_PROFILER
|
||||
{"start_profiler", (PyCFunction)start_profiler, METH_VARARGS, ""},
|
||||
|
||||
@@ -210,7 +210,7 @@ typedef struct {
|
||||
} CursorRenderInfo;
|
||||
|
||||
typedef struct {
|
||||
color_type default_fg, default_bg, cursor_color, highlight_fg, highlight_bg;
|
||||
color_type default_fg, default_bg, cursor_color, cursor_text_color, cursor_text_uses_bg, highlight_fg, highlight_bg;
|
||||
} DynamicColor;
|
||||
|
||||
typedef struct {
|
||||
@@ -285,6 +285,7 @@ bool schedule_write_to_child(unsigned long id, unsigned int num, ...);
|
||||
bool set_iutf8(int, bool);
|
||||
|
||||
color_type colorprofile_to_color(ColorProfile *self, color_type entry, color_type defval);
|
||||
float cursor_text_as_bg(ColorProfile *self);
|
||||
void copy_color_table_to_buffer(ColorProfile *self, color_type *address, int offset, size_t stride);
|
||||
void colorprofile_push_dynamic_colors(ColorProfile*);
|
||||
void colorprofile_pop_dynamic_colors(ColorProfile*);
|
||||
|
||||
@@ -31,10 +31,15 @@ static PyObject*
|
||||
init_x11_startup_notification(PyObject UNUSED *self, PyObject *args) {
|
||||
static bool done = false;
|
||||
static const char* libname = "libstartup-notification-1.so";
|
||||
// some installs are missing the .so symlink, so try the full name
|
||||
static const char* libname2 = "libstartup-notification-1.so.0";
|
||||
static const char* libname3 = "libstartup-notification-1.so.0.0.0";
|
||||
if (!done) {
|
||||
done = true;
|
||||
|
||||
libsn_handle = dlopen(libname, RTLD_LAZY);
|
||||
if (libsn_handle == NULL) libsn_handle = dlopen(libname2, RTLD_LAZY);
|
||||
if (libsn_handle == NULL) libsn_handle = dlopen(libname3, RTLD_LAZY);
|
||||
if (libsn_handle == NULL) {
|
||||
PyErr_Format(PyExc_OSError, "Failed to load %s with error: %s", libname, dlerror());
|
||||
return NULL;
|
||||
|
||||
@@ -24,11 +24,12 @@ pyspacing(int val) {
|
||||
#undef S
|
||||
}
|
||||
|
||||
|
||||
static inline PyObject*
|
||||
pattern_as_dict(FcPattern *pat) {
|
||||
PyObject *ans = PyDict_New();
|
||||
if (ans == NULL) return NULL;
|
||||
#define PS(x) PyUnicode_FromString((char*)x)
|
||||
#define PS(x) PyUnicode_Decode((const char*)x, strlen((const char*)x), "UTF-8", "replace")
|
||||
#define G(type, get, which, conv, name) { \
|
||||
type out; PyObject *p; \
|
||||
if (get(pat, which, 0, &out) == FcResultMatch) { \
|
||||
|
||||
@@ -1012,19 +1012,17 @@ render_line(FONTS_DATA_HANDLE fg_, Line *line) {
|
||||
if (is_private_use(cpu_cell->ch)
|
||||
&& cell_font_idx != BOX_FONT
|
||||
&& cell_font_idx != MISSING_FONT) {
|
||||
int desired_cells;
|
||||
int desired_cells = 1;
|
||||
if (cell_font_idx > 0) {
|
||||
Font *font = (fg->fonts + cell_font_idx);
|
||||
glyph_index glyph_id = glyph_id_for_codepoint(font->face, cpu_cell->ch);
|
||||
|
||||
int width = get_glyph_width(font->face, glyph_id);
|
||||
desired_cells = ceilf((float)width / fg->cell_width);
|
||||
} else {
|
||||
desired_cells = 1;
|
||||
}
|
||||
|
||||
int num_spaces = 0;
|
||||
while ((line->cpu_cells[i+num_spaces+1].ch == ' ' || line->cpu_cells[i+num_spaces+1].ch == 0)
|
||||
while ((line->cpu_cells[i+num_spaces+1].ch == ' ')
|
||||
&& num_spaces < MAX_NUM_EXTRA_GLYPHS_PUA
|
||||
&& num_spaces < desired_cells
|
||||
&& i + num_spaces + 1 < line->xnum) {
|
||||
|
||||
1
kitty/glfw-wrapper.h
generated
1
kitty/glfw-wrapper.h
generated
@@ -166,6 +166,7 @@
|
||||
#define GLFW_KEY_GRAVE_ACCENT 96 /* ` */
|
||||
#define GLFW_KEY_WORLD_1 161 /* non-US #1 */
|
||||
#define GLFW_KEY_WORLD_2 162 /* non-US #2 */
|
||||
#define GLFW_KEY_PLUS 163 /* non-US #2 */
|
||||
|
||||
/* Function keys */
|
||||
#define GLFW_KEY_ESCAPE 256
|
||||
|
||||
69
kitty/glfw.c
69
kitty/glfw.c
@@ -35,8 +35,8 @@ update_os_window_viewport(OSWindow *window, bool notify_boss) {
|
||||
}
|
||||
window->viewport_width = fw; window->viewport_height = fh;
|
||||
double xr = window->viewport_x_ratio, yr = window->viewport_y_ratio;
|
||||
window->viewport_x_ratio = (double)window->viewport_width / (double)w;
|
||||
window->viewport_y_ratio = (double)window->viewport_height / (double)h;
|
||||
window->viewport_x_ratio = w > 0 ? (double)window->viewport_width / (double)w : xr;
|
||||
window->viewport_y_ratio = h > 0 ? (double)window->viewport_height / (double)h : yr;
|
||||
double xdpi = window->logical_dpi_x, ydpi = window->logical_dpi_y;
|
||||
set_os_window_dpi(window);
|
||||
bool dpi_changed = (xr != 0.0 && xr != window->viewport_x_ratio) || (yr != 0.0 && yr != window->viewport_y_ratio) || (xdpi != window->logical_dpi_x) || (ydpi != window->logical_dpi_y);
|
||||
@@ -57,6 +57,19 @@ update_os_window_viewport(OSWindow *window, bool notify_boss) {
|
||||
}
|
||||
}
|
||||
|
||||
// On Cocoa, glfwWaitEvents() can block indefinitely because of the way Cocoa
|
||||
// works. See https://github.com/glfw/glfw/issues/1251. I have noticed this
|
||||
// happening in particular with window resize events, when waiting with no
|
||||
// timeout. See https://github.com/kovidgoyal/kitty/issues/458
|
||||
// So we use an unlovely hack to workaround that case
|
||||
void
|
||||
unjam_event_loop() {
|
||||
#ifdef __APPLE__
|
||||
if (event_loop_blocking_with_no_timeout)
|
||||
wakeup_main_loop();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// callbacks {{{
|
||||
|
||||
@@ -98,16 +111,6 @@ show_mouse_cursor(GLFWwindow *w) {
|
||||
}
|
||||
|
||||
static int min_width = 100, min_height = 100;
|
||||
// On Cocoa, glfwWaitEvents() can block indefinitely because of the way Cocoa
|
||||
// works. See https://github.com/glfw/glfw/issues/1251. I have noticed this
|
||||
// happening in particular with window resize events, when waiting with no
|
||||
// timeout. See https://github.com/kovidgoyal/kitty/issues/458
|
||||
// So we use an unlovely hack to workaround that case
|
||||
#ifdef __APPLE__
|
||||
#define unjam_event_loop() { if (event_loop_blocking_with_no_timeout) wakeup_main_loop(); }
|
||||
#else
|
||||
#define unjam_event_loop()
|
||||
#endif
|
||||
|
||||
static void
|
||||
framebuffer_size_callback(GLFWwindow *w, int width, int height) {
|
||||
@@ -352,8 +355,9 @@ get_window_dpi(GLFWwindow *w, double *x, double *y) {
|
||||
float xscale = 1, yscale = 1;
|
||||
if (w) glfwGetWindowContentScale(w, &xscale, &yscale);
|
||||
else glfwGetMonitorContentScale(glfwGetPrimaryMonitor(), &xscale, &yscale);
|
||||
if (!xscale) xscale = 1.0;
|
||||
if (!yscale) yscale = 1.0;
|
||||
// check for zero or NaN values of xscale/yscale
|
||||
if (!xscale || xscale != xscale) xscale = 1.0;
|
||||
if (!yscale || yscale != yscale) yscale = 1.0;
|
||||
#ifdef __APPLE__
|
||||
double factor = 72.0;
|
||||
#else
|
||||
@@ -650,6 +654,12 @@ bool
|
||||
application_quit_requested() {
|
||||
return !application_quit_canary || glfwWindowShouldClose(application_quit_canary);
|
||||
}
|
||||
|
||||
void
|
||||
request_application_quit() {
|
||||
if (application_quit_canary)
|
||||
glfwSetWindowShouldClose(application_quit_canary, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Global functions {{{
|
||||
@@ -749,6 +759,24 @@ get_clipboard_string(PYNOARG) {
|
||||
return Py_BuildValue("s", "");
|
||||
}
|
||||
|
||||
void
|
||||
ring_audio_bell(OSWindow *w) {
|
||||
static double last_bell_at = -1;
|
||||
double now = monotonic();
|
||||
if (now - last_bell_at <= 0.1) return;
|
||||
last_bell_at = now;
|
||||
if (w->handle) {
|
||||
glfwWindowBell(w->handle);
|
||||
}
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
ring_bell(PYNOARG) {
|
||||
OSWindow *w = current_os_window();
|
||||
ring_audio_bell(w);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
get_content_scale_for_window(PYNOARG) {
|
||||
OSWindow *w = global_state.callback_os_window ? global_state.callback_os_window : global_state.os_windows;
|
||||
@@ -786,17 +814,6 @@ change_os_window_state(PyObject *self UNUSED, PyObject *args) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
void
|
||||
ring_audio_bell(OSWindow *w) {
|
||||
static double last_bell_at = -1;
|
||||
double now = monotonic();
|
||||
if (now - last_bell_at <= 0.1) return;
|
||||
last_bell_at = now;
|
||||
if (w->handle) {
|
||||
glfwWindowBell(w->handle);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
request_window_attention(id_type kitty_window_id, bool audio_bell) {
|
||||
OSWindow *w = os_window_for_kitty_window(kitty_window_id);
|
||||
@@ -1019,6 +1036,7 @@ static PyMethodDef module_methods[] = {
|
||||
METHODB(set_default_window_icon, METH_VARARGS),
|
||||
METHODB(get_clipboard_string, METH_NOARGS),
|
||||
METHODB(get_content_scale_for_window, METH_NOARGS),
|
||||
METHODB(ring_bell, METH_NOARGS),
|
||||
METHODB(set_clipboard_string, METH_VARARGS),
|
||||
METHODB(toggle_fullscreen, METH_NOARGS),
|
||||
METHODB(change_os_window_state, METH_VARARGS),
|
||||
@@ -1116,6 +1134,7 @@ init_glfw(PyObject *m) {
|
||||
ADDC(GLFW_KEY_GRAVE_ACCENT);
|
||||
ADDC(GLFW_KEY_WORLD_1);
|
||||
ADDC(GLFW_KEY_WORLD_2);
|
||||
ADDC(GLFW_KEY_PLUS);
|
||||
|
||||
// --- Function keys -----------------------------------------------------------
|
||||
ADDC(GLFW_KEY_ESCAPE);
|
||||
|
||||
@@ -368,7 +368,10 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_
|
||||
if (fd == -1) ABRT(EBADF, "Failed to open file %s for graphics transmission with error: [%d] %s", fname, errno, strerror(errno));
|
||||
img->data_loaded = mmap_img_file(self, img, fd, g->data_sz, g->data_offset);
|
||||
close(fd);
|
||||
if (tt == 't') unlink(fname);
|
||||
if (tt == 't') {
|
||||
if (global_state.boss) { call_boss(safe_delete_temp_file, "s", fname); }
|
||||
else unlink(fname);
|
||||
}
|
||||
else if (tt == 's') shm_unlink(fname);
|
||||
break;
|
||||
default:
|
||||
|
||||
47
kitty/key_encoding.py
generated
47
kitty/key_encoding.py
generated
@@ -127,7 +127,8 @@ ENCODING = {
|
||||
'WORLD 2': 'x',
|
||||
'X': 'p',
|
||||
'Y': 'q',
|
||||
'Z': 'r'
|
||||
'Z': 'r',
|
||||
'PLUS': 'Bi'
|
||||
}
|
||||
KEY_MAP = {
|
||||
32: 'A',
|
||||
@@ -180,6 +181,7 @@ KEY_MAP = {
|
||||
96: 'v',
|
||||
161: 'w',
|
||||
162: 'x',
|
||||
163: 'Bi',
|
||||
256: 'y',
|
||||
257: 'z',
|
||||
258: '0',
|
||||
@@ -250,10 +252,8 @@ KEY_MAP = {
|
||||
346: 'Bg',
|
||||
347: 'Bh'
|
||||
}
|
||||
|
||||
# END_ENCODING }}}
|
||||
|
||||
|
||||
text_keys = string.ascii_uppercase + string.ascii_lowercase + string.digits + '`~!@#$%^&*()_-+=[{]}\\|<,>./?;:\'" '
|
||||
|
||||
|
||||
@@ -324,28 +324,35 @@ mod_map = {c: i for i, c in enumerate('ABCDEFGHIJKLMNOP')}
|
||||
key_rmap = {}
|
||||
g = globals()
|
||||
config_key_map = {}
|
||||
config_mod_map = {'SHIFT': SHIFT, 'ALT': ALT, 'OPTION': ALT, '⌥': ALT, '⌘': SUPER, 'CMD': SUPER, 'SUPER': SUPER, 'CTRL': CTRL, 'CONTROL': CTRL}
|
||||
config_mod_map = {
|
||||
'SHIFT': SHIFT,
|
||||
'ALT': ALT,
|
||||
'OPTION': ALT,
|
||||
'⌥': ALT,
|
||||
'⌘': SUPER,
|
||||
'CMD': SUPER,
|
||||
'SUPER': SUPER,
|
||||
'CTRL': CTRL,
|
||||
'CONTROL': CTRL
|
||||
}
|
||||
for key_name, enc in ENCODING.items():
|
||||
key_name = key_name.replace(' ', '_')
|
||||
g[key_name] = config_key_map[key_name] = key_name
|
||||
key_rmap[enc] = key_name
|
||||
config_key_map.update({
|
||||
'`': g['GRAVE_ACCENT'],
|
||||
'-': g['MINUS'],
|
||||
'=': g['EQUAL'],
|
||||
|
||||
'[': g['LEFT_BRACKET'],
|
||||
']': g['RIGHT_BRACKET'],
|
||||
'\\': g['BACKSLASH'],
|
||||
|
||||
';': g['SEMICOLON'],
|
||||
"'": g['APOSTROPHE'],
|
||||
|
||||
',': g['COMMA'],
|
||||
'.': g['PERIOD'],
|
||||
'/': g['SLASH'],
|
||||
|
||||
'ESC': g['ESCAPE'],
|
||||
'`': g['GRAVE_ACCENT'],
|
||||
'-': g['MINUS'],
|
||||
'=': g['EQUAL'],
|
||||
'[': g['LEFT_BRACKET'],
|
||||
']': g['RIGHT_BRACKET'],
|
||||
'\\': g['BACKSLASH'],
|
||||
';': g['SEMICOLON'],
|
||||
"'": g['APOSTROPHE'],
|
||||
',': g['COMMA'],
|
||||
'.': g['PERIOD'],
|
||||
'/': g['SLASH'],
|
||||
'ESC': g['ESCAPE'],
|
||||
'+': g['PLUS'],
|
||||
})
|
||||
|
||||
enter_key = KeyEvent(PRESS, 0, g['ENTER'])
|
||||
|
||||
11691
kitty/keys.h
generated
11691
kitty/keys.h
generated
File diff suppressed because it is too large
Load Diff
@@ -6,20 +6,10 @@ import string
|
||||
|
||||
from . import fast_data_types as defines
|
||||
from .key_encoding import KEY_MAP
|
||||
from .terminfo import key_as_bytes
|
||||
from .terminfo import key_as_bytes, modify_key_bytes
|
||||
from .utils import base64_encode
|
||||
|
||||
|
||||
def modify_key_bytes(keybytes, amt):
|
||||
ans = bytearray(keybytes)
|
||||
amt = str(amt).encode('ascii')
|
||||
if ans[-1] == ord('~'):
|
||||
return bytes(ans[:-1] + bytearray(b';' + amt + b'~'))
|
||||
if ans[1] == ord('O'):
|
||||
return bytes(ans[:1] + bytearray(b'[1;' + amt) + ans[-1:])
|
||||
raise ValueError('Unknown key type in key: {!r}'.format(keybytes))
|
||||
|
||||
|
||||
def modify_complex_key(name, amt):
|
||||
if not isinstance(name, bytes):
|
||||
name = key_as_bytes(name)
|
||||
|
||||
@@ -83,13 +83,10 @@ def talk_to_instance(args):
|
||||
|
||||
|
||||
def load_all_shaders(semi_transparent=0):
|
||||
load_shader_programs(semi_transparent, load_all_shaders.cursor_text_color)
|
||||
load_shader_programs(semi_transparent)
|
||||
load_borders_program()
|
||||
|
||||
|
||||
load_all_shaders.cursor_text_color = None
|
||||
|
||||
|
||||
def init_glfw(debug_keyboard=False):
|
||||
glfw_module = 'cocoa' if is_macos else ('wayland' if is_wayland else 'x11')
|
||||
if not glfw_init(glfw_path(glfw_module), debug_keyboard):
|
||||
@@ -122,7 +119,6 @@ def _run_app(opts, args):
|
||||
new_os_window_trigger = get_new_os_window_trigger(opts)
|
||||
if is_macos and opts.macos_custom_beam_cursor:
|
||||
set_custom_ibeam_cursor()
|
||||
load_all_shaders.cursor_text_color = opts.cursor_text_color
|
||||
if not is_wayland and not is_macos: # no window icons on wayland
|
||||
with open(logo_data_file, 'rb') as f:
|
||||
set_default_window_icon(f.read(), 256, 256)
|
||||
@@ -235,7 +231,7 @@ def _main():
|
||||
rpath = os.path.dirname(kitty_exe())
|
||||
items = frozenset(os.environ['PATH'].split(os.pathsep))
|
||||
if rpath and rpath not in items:
|
||||
os.environ['PATH'] += os.pathsep + rpath
|
||||
os.environ['PATH'] = rpath + os.pathsep + os.environ.get('PATH', '')
|
||||
|
||||
args = sys.argv[1:]
|
||||
if is_macos and os.environ.pop('KITTY_LAUNCHED_BY_LAUNCH_SERVICES', None) == '1':
|
||||
|
||||
@@ -557,6 +557,8 @@ mouse_event(int button, int modifiers, int action) {
|
||||
void
|
||||
scroll_event(double UNUSED xoffset, double yoffset, int flags) {
|
||||
bool in_tab_bar;
|
||||
static id_type window_for_momentum_scroll = 0;
|
||||
static bool main_screen_for_momentum_scroll = false;
|
||||
unsigned int window_idx = 0;
|
||||
Window *w = window_for_event(&window_idx, &in_tab_bar);
|
||||
if (!w && !in_tab_bar) {
|
||||
@@ -567,9 +569,35 @@ scroll_event(double UNUSED xoffset, double yoffset, int flags) {
|
||||
if (t) w = t->windows + t->active_window;
|
||||
}
|
||||
if (!w) return;
|
||||
Screen *screen = w->render_data.screen;
|
||||
|
||||
enum MomentumData { NoMomentumData, MomentumPhaseBegan, MomentumPhaseStationary, MomentumPhaseActive, MomentumPhaseEnded, MomentumPhaseCancelled, MomentumPhaseMayBegin };
|
||||
enum MomentumData momentum_data = (flags >> 1) & 7;
|
||||
|
||||
switch(momentum_data) {
|
||||
case NoMomentumData:
|
||||
break;
|
||||
case MomentumPhaseBegan:
|
||||
window_for_momentum_scroll = w->id;
|
||||
main_screen_for_momentum_scroll = screen->linebuf == screen->main_linebuf;
|
||||
break;
|
||||
case MomentumPhaseStationary:
|
||||
case MomentumPhaseActive:
|
||||
if (window_for_momentum_scroll != w->id || main_screen_for_momentum_scroll != (screen->linebuf == screen->main_linebuf)) return;
|
||||
break;
|
||||
case MomentumPhaseEnded:
|
||||
case MomentumPhaseCancelled:
|
||||
window_for_momentum_scroll = 0;
|
||||
break;
|
||||
case MomentumPhaseMayBegin:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (yoffset == 0.0) return;
|
||||
|
||||
int s;
|
||||
bool is_high_resolution = flags & 1;
|
||||
|
||||
if (is_high_resolution) {
|
||||
yoffset *= OPT(touch_scroll_multiplier);
|
||||
if (yoffset * global_state.callback_os_window->pending_scroll_pixels < 0) {
|
||||
@@ -580,16 +608,26 @@ scroll_event(double UNUSED xoffset, double yoffset, int flags) {
|
||||
global_state.callback_os_window->pending_scroll_pixels = pixels;
|
||||
return;
|
||||
}
|
||||
s = abs(((int)round(pixels))) / global_state.callback_os_window->fonts_data->cell_height;
|
||||
if (pixels < 0) s *= -1;
|
||||
s = (int)round(pixels) / (int)global_state.callback_os_window->fonts_data->cell_height;
|
||||
global_state.callback_os_window->pending_scroll_pixels = pixels - s * (int) global_state.callback_os_window->fonts_data->cell_height;
|
||||
} else {
|
||||
s = (int) round(yoffset * OPT(wheel_scroll_multiplier));
|
||||
if (screen->linebuf == screen->main_linebuf || !screen->modes.mouse_tracking_mode) {
|
||||
// Only use wheel_scroll_multiplier if we are scrolling kitty scrollback or in mouse
|
||||
// tracking mode, where the application is responsible for interpreting scroll events
|
||||
yoffset *= OPT(wheel_scroll_multiplier);
|
||||
} else if (OPT(wheel_scroll_multiplier) < 0) {
|
||||
// ensure that changing scroll direction still works, even though
|
||||
// we are not using wheel_scroll_multiplier
|
||||
yoffset *= -1;
|
||||
}
|
||||
s = (int) round(yoffset);
|
||||
// apparently on cocoa some mice generate really small yoffset values
|
||||
// when scrolling slowly https://github.com/kovidgoyal/kitty/issues/1238
|
||||
if (s == 0 && yoffset != 0) s = yoffset > 0 ? 1 : -1;
|
||||
global_state.callback_os_window->pending_scroll_pixels = 0;
|
||||
}
|
||||
if (s == 0) return;
|
||||
bool upwards = s > 0;
|
||||
Screen *screen = w->render_data.screen;
|
||||
if (screen->linebuf == screen->main_linebuf) {
|
||||
screen_history_scroll(screen, abs(s), upwards);
|
||||
} else {
|
||||
@@ -597,13 +635,7 @@ scroll_event(double UNUSED xoffset, double yoffset, int flags) {
|
||||
int sz = encode_mouse_event(w, upwards ? GLFW_MOUSE_BUTTON_4 : GLFW_MOUSE_BUTTON_5, PRESS, 0);
|
||||
if (sz > 0) {
|
||||
mouse_event_buf[sz] = 0;
|
||||
if (is_high_resolution) {
|
||||
for (s = abs(s); s > 0; s--) {
|
||||
write_escape_code_to_child(screen, CSI, mouse_event_buf);
|
||||
}
|
||||
} else {
|
||||
// Since we are sending a mouse button 4/5 event, we ignore 's'
|
||||
// and simply send one event per received scroll event
|
||||
for (s = abs(s); s > 0; s--) {
|
||||
write_escape_code_to_child(screen, CSI, mouse_event_buf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,9 +219,9 @@ static struct CellUniformData cell_uniform_data = {0, .prev_inactive_text_alpha=
|
||||
static inline void
|
||||
cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy, CursorRenderInfo *cursor, bool inverted, OSWindow *os_window) {
|
||||
struct CellRenderData {
|
||||
GLfloat xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity;
|
||||
GLfloat xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity, cursor_text_uses_bg;
|
||||
|
||||
GLuint default_fg, default_bg, highlight_fg, highlight_bg, cursor_color, url_color, url_style, inverted;
|
||||
GLuint default_fg, default_bg, highlight_fg, highlight_bg, cursor_color, cursor_text_color, url_color, url_style, inverted;
|
||||
|
||||
GLuint xnum, ynum, cursor_fg_sprite_idx;
|
||||
GLfloat cursor_x, cursor_y, cursor_w;
|
||||
@@ -260,8 +260,10 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, G
|
||||
|
||||
#define COLOR(name) colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.name, screen->color_profile->configured.name)
|
||||
rd->default_fg = COLOR(default_fg); rd->default_bg = COLOR(default_bg); rd->highlight_fg = COLOR(highlight_fg); rd->highlight_bg = COLOR(highlight_bg);
|
||||
rd->cursor_text_color = COLOR(cursor_text_color);
|
||||
#undef COLOR
|
||||
rd->cursor_color = cursor->color; rd->url_color = OPT(url_color); rd->url_style = OPT(url_style);
|
||||
rd->cursor_text_uses_bg = cursor_text_as_bg(screen->color_profile);
|
||||
|
||||
unmap_vao_buffer(vao_idx, uniform_buffer); rd = NULL;
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ typedef struct {
|
||||
OSWindow *os_windows;
|
||||
size_t num_os_windows, capacity;
|
||||
OSWindow *callback_os_window;
|
||||
bool close_all_windows;
|
||||
bool terminate;
|
||||
bool is_wayland;
|
||||
bool debug_gl, debug_font_fallback;
|
||||
bool has_pending_resizes;
|
||||
@@ -172,6 +172,7 @@ void make_os_window_context_current(OSWindow *w);
|
||||
void update_os_window_references();
|
||||
void mark_os_window_for_close(OSWindow* w, bool yes);
|
||||
void update_os_window_viewport(OSWindow *window, bool);
|
||||
void unjam_event_loop();
|
||||
bool should_os_window_close(OSWindow* w);
|
||||
bool should_os_window_be_rendered(OSWindow* w);
|
||||
void wakeup_main_loop();
|
||||
@@ -209,5 +210,6 @@ typedef enum {
|
||||
} CocoaPendingAction;
|
||||
void set_cocoa_pending_action(CocoaPendingAction action);
|
||||
bool application_quit_requested();
|
||||
void request_application_quit();
|
||||
#endif
|
||||
void wayland_request_frame_render(OSWindow *w);
|
||||
|
||||
@@ -11,31 +11,40 @@ from .fast_data_types import (
|
||||
viewport_for_window
|
||||
)
|
||||
from .layout import Rect
|
||||
from .utils import color_as_int
|
||||
from .utils import color_as_int, log_error
|
||||
from .window import calculate_gl_geometry
|
||||
from .rgb import alpha_blend, color_from_int
|
||||
|
||||
TabBarData = namedtuple('TabBarData', 'title is_active needs_attention')
|
||||
DrawData = namedtuple('DrawData', 'leading_spaces sep trailing_spaces bell_on_tab bell_fg alpha active_bg inactive_bg default_bg')
|
||||
DrawData = namedtuple(
|
||||
'DrawData', 'leading_spaces sep trailing_spaces bell_on_tab'
|
||||
' bell_fg alpha active_bg inactive_bg default_bg title_template')
|
||||
|
||||
|
||||
def as_rgb(x):
|
||||
return (x << 8) | 2
|
||||
|
||||
|
||||
def draw_title(draw_data, screen, tab):
|
||||
def draw_title(draw_data, screen, tab, index):
|
||||
if tab.needs_attention and draw_data.bell_on_tab:
|
||||
fg = screen.cursor.fg
|
||||
screen.cursor.fg = draw_data.bell_fg
|
||||
screen.draw('🔔 ')
|
||||
screen.cursor.fg = fg
|
||||
screen.draw(tab.title)
|
||||
try:
|
||||
title = draw_data.title_template.format(title=tab.title, index=index)
|
||||
except Exception as e:
|
||||
if not hasattr(draw_title, 'template_failure_reported'):
|
||||
draw_title.template_failure_reported = True
|
||||
log_error('Invalid tab title template: "{}" with error: {}'.format(draw_data.title_template, e))
|
||||
title = tab.title
|
||||
screen.draw(title)
|
||||
|
||||
|
||||
def draw_tab_with_separator(draw_data, screen, tab, before, max_title_length):
|
||||
def draw_tab_with_separator(draw_data, screen, tab, before, max_title_length, index):
|
||||
if draw_data.leading_spaces:
|
||||
screen.draw(' ' * draw_data.leading_spaces)
|
||||
draw_title(draw_data, screen, tab)
|
||||
draw_title(draw_data, screen, tab, index)
|
||||
if draw_data.trailing_spaces:
|
||||
screen.draw(' ' * draw_data.trailing_spaces)
|
||||
extra = screen.cursor.x - before - max_title_length
|
||||
@@ -49,17 +58,17 @@ def draw_tab_with_separator(draw_data, screen, tab, before, max_title_length):
|
||||
return end
|
||||
|
||||
|
||||
def draw_tab_with_fade(draw_data, screen, tab, before, max_title_length):
|
||||
def draw_tab_with_fade(draw_data, screen, tab, before, max_title_length, index):
|
||||
tab_bg = draw_data.active_bg if tab.is_active else draw_data.inactive_bg
|
||||
fade_colors = [as_rgb(color_as_int(alpha_blend(tab_bg, draw_data.default_bg, alpha))) for alpha in draw_data.alpha]
|
||||
for bg in fade_colors:
|
||||
screen.cursor.bg = bg
|
||||
screen.draw(' ')
|
||||
draw_title(draw_data, screen, tab)
|
||||
draw_title(draw_data, screen, tab, index)
|
||||
extra = screen.cursor.x - before - max_title_length
|
||||
if extra > 0:
|
||||
screen.cursor.x = before
|
||||
draw_title(draw_data, screen, tab)
|
||||
draw_title(draw_data, screen, tab, index)
|
||||
extra = screen.cursor.x - before - max_title_length
|
||||
if extra > 0:
|
||||
screen.cursor.x -= extra + 1
|
||||
@@ -81,6 +90,7 @@ class TabBar:
|
||||
def __init__(self, os_window_id, opts):
|
||||
self.os_window_id = os_window_id
|
||||
self.opts = opts
|
||||
draw_title.template = opts.tab_title_template
|
||||
self.num_tabs = 1
|
||||
self.margin_width = pt_to_px(self.opts.tab_bar_margin_width, self.os_window_id)
|
||||
self.cell_width, cell_height = cell_size_for_window(self.os_window_id)
|
||||
@@ -112,7 +122,7 @@ class TabBar:
|
||||
self.draw_data = DrawData(
|
||||
self.leading_spaces, self.sep, self.trailing_spaces, self.opts.bell_on_tab, self.bell_fg,
|
||||
self.opts.tab_fade, self.opts.active_tab_background, self.opts.inactive_tab_background,
|
||||
self.opts.background
|
||||
self.opts.background, self.opts.tab_title_template
|
||||
)
|
||||
self.draw_func = draw_tab_with_separator if self.opts.tab_bar_style == 'separator' else draw_tab_with_fade
|
||||
|
||||
@@ -162,12 +172,12 @@ class TabBar:
|
||||
cr = []
|
||||
last_tab = data[-1] if data else None
|
||||
|
||||
for t in data:
|
||||
for i, t in enumerate(data):
|
||||
s.cursor.bg = self.active_bg if t.is_active else 0
|
||||
s.cursor.fg = self.active_fg if t.is_active else 0
|
||||
s.cursor.bold, s.cursor.italic = self.active_font_style if t.is_active else self.inactive_font_style
|
||||
before = s.cursor.x
|
||||
end = self.draw_func(self.draw_data, s, t, before, max_title_length)
|
||||
end = self.draw_func(self.draw_data, s, t, before, max_title_length, i + 1)
|
||||
cr.append((before, end))
|
||||
if s.cursor.x > s.columns - max_title_length and t is not last_tab:
|
||||
s.draw('…')
|
||||
|
||||
@@ -11,7 +11,7 @@ from .child import Child
|
||||
from .constants import appname, get_boss, is_macos, is_wayland
|
||||
from .fast_data_types import (
|
||||
add_tab, glfw_post_empty_event, mark_tab_bar_dirty, next_window_id,
|
||||
pt_to_px, remove_tab, remove_window, set_active_tab, swap_tabs,
|
||||
pt_to_px, remove_tab, remove_window, ring_bell, set_active_tab, swap_tabs,
|
||||
x11_window_id
|
||||
)
|
||||
from .layout import create_layout_object_for, evict_cached_layouts
|
||||
@@ -199,6 +199,16 @@ class Tab: # {{{
|
||||
return
|
||||
return 'Could not resize'
|
||||
|
||||
def resize_window(self, quality, increment):
|
||||
if increment < 1:
|
||||
raise ValueError(increment)
|
||||
is_horizontal = quality in ('wider', 'narrower')
|
||||
increment *= 1 if quality in ('wider', 'taller') else -1
|
||||
if self.resize_window_by(
|
||||
self.windows[self.active_window_idx].id,
|
||||
increment, is_horizontal) is not None:
|
||||
ring_bell()
|
||||
|
||||
def reset_window_sizes(self):
|
||||
if self.current_layout.remove_all_biases():
|
||||
self.relayout()
|
||||
@@ -501,8 +511,10 @@ class TabManager: # {{{
|
||||
if len(self.tabs) > 1:
|
||||
idx = self.active_tab_idx
|
||||
nidx = (idx + len(self.tabs) + delta) % len(self.tabs)
|
||||
self.tabs[idx], self.tabs[nidx] = self.tabs[nidx], self.tabs[idx]
|
||||
swap_tabs(self.os_window_id, idx, nidx)
|
||||
step = 1 if idx < nidx else -1
|
||||
for i in range(idx, nidx, step):
|
||||
self.tabs[i], self.tabs[i + step] = self.tabs[i + step], self.tabs[i]
|
||||
swap_tabs(self.os_window_id, i, i + step)
|
||||
self._set_active_tab(nidx)
|
||||
self.mark_tab_bar_dirty()
|
||||
|
||||
@@ -512,8 +524,9 @@ class TabManager: # {{{
|
||||
self._add_tab(Tab(self, special_window=special_window, cwd_from=cwd_from))
|
||||
self._set_active_tab(idx)
|
||||
if len(self.tabs) > 2 and as_neighbor and idx != nidx:
|
||||
self.tabs[idx], self.tabs[nidx] = self.tabs[nidx], self.tabs[idx]
|
||||
swap_tabs(self.os_window_id, idx, nidx)
|
||||
for i in range(idx, nidx, -1):
|
||||
self.tabs[i], self.tabs[i-1] = self.tabs[i-1], self.tabs[i]
|
||||
swap_tabs(self.os_window_id, i, i-1)
|
||||
self._set_active_tab(nidx)
|
||||
idx = nidx
|
||||
self.mark_tab_bar_dirty()
|
||||
|
||||
@@ -6,6 +6,22 @@ import re
|
||||
from binascii import hexlify, unhexlify
|
||||
|
||||
|
||||
def modify_key_bytes(keybytes, amt):
|
||||
if amt == 0:
|
||||
return keybytes
|
||||
ans = bytearray(keybytes)
|
||||
amt = str(amt).encode('ascii')
|
||||
if ans[-1] == ord('~'):
|
||||
return bytes(ans[:-1] + bytearray(b';' + amt + b'~'))
|
||||
if ans[1] == ord('O'):
|
||||
return bytes(ans[:1] + bytearray(b'[1;' + amt) + ans[-1:])
|
||||
raise ValueError('Unknown key type in key: {!r}'.format(keybytes))
|
||||
|
||||
|
||||
def encode_keystring(keybytes):
|
||||
return keybytes.decode('ascii').replace('\033', r'\E')
|
||||
|
||||
|
||||
names = 'xterm-kitty', 'KovIdTTY'
|
||||
|
||||
termcap_aliases = {
|
||||
@@ -156,54 +172,8 @@ string_capabilities = {
|
||||
# 'invis': r'\E[8m',
|
||||
# Backspace
|
||||
'kbs': r'\177',
|
||||
# Left
|
||||
'kcub1': r'\EOD',
|
||||
# Down
|
||||
'kcud1': r'\EOB',
|
||||
# Right
|
||||
'kcuf1': r'\EOC',
|
||||
# Up
|
||||
'kcuu1': r'\EOA',
|
||||
# Function keys
|
||||
'kf1': r'\EOP',
|
||||
'kf2': r'\EOQ',
|
||||
'kf3': r'\EOR',
|
||||
'kf4': r'\EOS',
|
||||
'kf5': r'\E[15~',
|
||||
'kf6': r'\E[17~',
|
||||
'kf7': r'\E[18~',
|
||||
'kf8': r'\E[19~',
|
||||
'kf9': r'\E[20~',
|
||||
'kf10': r'\E[21~',
|
||||
'kf11': r'\E[23~',
|
||||
'kf12': r'\E[24~',
|
||||
'kf13': r'\E[1;2P',
|
||||
'kf14': r'\E[1;2Q',
|
||||
'kf15': r'\E[1;2R',
|
||||
'kf16': r'\E[1;2S',
|
||||
'kf17': r'\E[15;2~',
|
||||
'kf18': r'\E[17;2~',
|
||||
'kf19': r'\E[18;2~',
|
||||
'kf20': r'\E[19;2~',
|
||||
'kf21': r'\E[20;2~',
|
||||
'kf22': r'\E[21;2~',
|
||||
'kf23': r'\E[23;2~',
|
||||
'kf24': r'\E[24;2~',
|
||||
'kf25': r'\E[1;5P',
|
||||
# Home
|
||||
'khome': r'\EOH',
|
||||
# End
|
||||
'kend': r'\EOF',
|
||||
# Insert character key
|
||||
'kich1': r'\E[2~',
|
||||
# Delete character key
|
||||
'kdch1': r'\E[3~',
|
||||
# Mouse event has occurred
|
||||
'kmous': r'\E[M',
|
||||
# Page down
|
||||
'knp': r'\E[6~',
|
||||
# Page up
|
||||
'kpp': r'\E[5~',
|
||||
# Scroll backwards (reverse index)
|
||||
'kri': r'\E[1;2A',
|
||||
# scroll forwards (index)
|
||||
@@ -270,11 +240,6 @@ string_capabilities = {
|
||||
# Select alternate charset
|
||||
'smacs': r'\E(0',
|
||||
'rmacs': r'\E(B',
|
||||
# Shifted keys
|
||||
'kRIT': r'\E[1;2C',
|
||||
'kLFT': r'\E[1;2D',
|
||||
'kEND': r'\E[1;2F',
|
||||
'kHOM': r'\E[1;2H',
|
||||
# Special keys
|
||||
'khlp': r'',
|
||||
'kund': r'',
|
||||
@@ -298,6 +263,27 @@ string_capabilities = {
|
||||
# 'rs2': r'\E[!p\E[?3;4l\E[4l\E>',
|
||||
}
|
||||
|
||||
string_capabilities.update({
|
||||
'kf{}'.format(offset + n):
|
||||
encode_keystring(modify_key_bytes(b'\033' + value, mod))
|
||||
for offset, mod in {0: 0, 12: 2, 24: 5, 36: 6, 48: 3, 60: 4}.items()
|
||||
for n, value in zip(range(1, 13),
|
||||
b'OP OQ OR OS [15~ [17~ [18~ [19~ [20~ [21~ [23~ [24~'.split())
|
||||
if offset + n < 64
|
||||
})
|
||||
|
||||
string_capabilities.update({
|
||||
name.format(unmod=unmod, key=key):
|
||||
encode_keystring(modify_key_bytes(b'\033' + value, mod))
|
||||
for unmod, key, value in zip(
|
||||
'cuu1 cud1 cuf1 cub1 end home ich1 dch1 pp np'.split(),
|
||||
'UP DN RIT LFT END HOM IC DC PRV NXT'.split(),
|
||||
b'OA OB OC OD OF OH [2~ [3~ [5~ [6~'.split())
|
||||
for name, mod in {
|
||||
'k{unmod}': 0, 'k{key}': 2, 'k{key}3': 3, 'k{key}4': 4,
|
||||
'k{key}5': 5, 'k{key}6': 6, 'k{key}7': 7}.items()
|
||||
})
|
||||
|
||||
termcap_aliases.update({
|
||||
'ac': 'acsc',
|
||||
'bl': 'bel',
|
||||
@@ -346,31 +332,6 @@ termcap_aliases.update({
|
||||
'kd': 'kcud1',
|
||||
'kr': 'kcuf1',
|
||||
'ku': 'kcuu1',
|
||||
'k1': 'kf1',
|
||||
'k2': 'kf2',
|
||||
'k3': 'kf3',
|
||||
'k4': 'kf4',
|
||||
'k5': 'kf5',
|
||||
'k6': 'kf6',
|
||||
'k7': 'kf7',
|
||||
'k8': 'kf8',
|
||||
'k9': 'kf9',
|
||||
'k;': 'kf10',
|
||||
'F1': 'kf11',
|
||||
'F2': 'kf12',
|
||||
'F3': 'kf13',
|
||||
'F4': 'kf14',
|
||||
'F5': 'kf15',
|
||||
'F6': 'kf16',
|
||||
'F7': 'kf17',
|
||||
'F8': 'kf18',
|
||||
'F9': 'kf19',
|
||||
'FA': 'kf20',
|
||||
'FB': 'kf21',
|
||||
'FC': 'kf22',
|
||||
'FD': 'kf23',
|
||||
'FE': 'kf24',
|
||||
'FF': 'kf25',
|
||||
'kh': 'khome',
|
||||
'@7': 'kend',
|
||||
'kI': 'kich1',
|
||||
@@ -410,8 +371,12 @@ termcap_aliases.update({
|
||||
'ks': 'smkx',
|
||||
'ke': 'rmkx',
|
||||
'#2': 'kHOM',
|
||||
'#3': 'kIC',
|
||||
'#4': 'kLFT',
|
||||
'*4': 'kDC',
|
||||
'*7': 'kEND',
|
||||
'%c': 'kNXT',
|
||||
'%e': 'kPRV',
|
||||
'%i': 'kRIT',
|
||||
'%1': 'khlp',
|
||||
'&8': 'kund',
|
||||
@@ -433,10 +398,22 @@ termcap_aliases.update({
|
||||
# 'r2': 'rs2',
|
||||
})
|
||||
|
||||
termcap_aliases.update({
|
||||
tc: 'kf{}'.format(n)
|
||||
for n, tc in enumerate(
|
||||
'k1 k2 k3 k4 k5 k6 k7 k8 k9 k; F1 F2 F3 F4 F5 F6 F7 F8 F9 FA '
|
||||
'FB FC FD FE FF FG FH FI FJ FK FL FM FN FO FP FQ FR FS FT FU '
|
||||
'FV FW FX FY FZ Fa Fb Fc Fd Fe Ff Fg Fh Fi Fj Fk Fl Fm Fn Fo '
|
||||
'Fp Fq Fr'.split(), 1)})
|
||||
|
||||
queryable_capabilities = numeric_capabilities.copy()
|
||||
queryable_capabilities.update(string_capabilities)
|
||||
extra = (bool_capabilities | numeric_capabilities.keys() | string_capabilities.keys()) - set(termcap_aliases.values())
|
||||
no_termcap_for = frozenset('Su Tc setrgbf setrgbb fullkbd'.split())
|
||||
no_termcap_for = frozenset(
|
||||
'Su Tc setrgbf setrgbb fullkbd kUP kDN'.split() + [
|
||||
'k{}{}'.format(key, mod)
|
||||
for key in 'UP DN RIT LFT END HOM IC DC PRV NXT'.split()
|
||||
for mod in range(3, 8)])
|
||||
if extra - no_termcap_for:
|
||||
raise Exception('Termcap aliases not complete, missing: {}'.format(extra - no_termcap_for))
|
||||
del extra
|
||||
|
||||
@@ -432,3 +432,20 @@ def get_editor():
|
||||
ans = shlex.split(ans)
|
||||
get_editor.ans = ans
|
||||
return ans
|
||||
|
||||
|
||||
def is_path_in_temp_dir(path):
|
||||
if not path:
|
||||
return False
|
||||
|
||||
def abspath(x):
|
||||
if x:
|
||||
return os.path.abspath(os.path.realpath(x))
|
||||
|
||||
import tempfile
|
||||
path = abspath(path)
|
||||
candidates = frozenset(map(abspath, ('/tmp', '/dev/shm', os.environ.get('TMPDIR', None), tempfile.gettempdir())))
|
||||
for q in candidates:
|
||||
if q and path.startswith(q):
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -10,7 +10,6 @@ from collections import deque
|
||||
from enum import IntEnum
|
||||
from itertools import chain
|
||||
|
||||
from .child import cwd_of_process
|
||||
from .config import build_ansi_color_table
|
||||
from .constants import (
|
||||
ScreenGeometry, WindowGeometry, appname, get_boss, wakeup
|
||||
@@ -24,7 +23,7 @@ from .fast_data_types import (
|
||||
set_clipboard_string, set_titlebar_color, set_window_render_data,
|
||||
update_window_title, update_window_visibility, viewport_for_window
|
||||
)
|
||||
from .keys import keyboard_mode_name
|
||||
from .keys import keyboard_mode_name, extended_key_event, defines
|
||||
from .rgb import to_color
|
||||
from .terminfo import get_capabilities
|
||||
from .utils import (
|
||||
@@ -56,14 +55,13 @@ def calculate_gl_geometry(window_geometry, viewport_width, viewport_height, cell
|
||||
return ScreenGeometry(xstart, ystart, window_geometry.xnum, window_geometry.ynum, dx, dy)
|
||||
|
||||
|
||||
def load_shader_programs(semi_transparent=0, cursor_text_color=None):
|
||||
def load_shader_programs(semi_transparent=0):
|
||||
compile_program(BLIT_PROGRAM, *load_shaders('blit'))
|
||||
v, f = load_shaders('cell')
|
||||
|
||||
def color_as_vec3(x):
|
||||
return 'vec3({}, {}, {})'.format(x.red / 255, x.green / 255, x.blue / 255)
|
||||
|
||||
cursor_text_color = color_as_vec3(cursor_text_color) if cursor_text_color else 'bg'
|
||||
for which, p in {
|
||||
'SIMPLE': CELL_PROGRAM,
|
||||
'BACKGROUND': CELL_BG_PROGRAM,
|
||||
@@ -76,7 +74,6 @@ def load_shader_programs(semi_transparent=0, cursor_text_color=None):
|
||||
'STRIKE_SHIFT': STRIKETHROUGH,
|
||||
'DIM_SHIFT': DIM,
|
||||
'DECORATION_SHIFT': DECORATION,
|
||||
'CURSOR_TEXT_COLOR': cursor_text_color,
|
||||
}.items():
|
||||
vv = vv.replace('{{{}}}'.format(gln), str(pyn), 1)
|
||||
if semi_transparent:
|
||||
@@ -95,8 +92,13 @@ def load_shader_programs(semi_transparent=0, cursor_text_color=None):
|
||||
|
||||
def setup_colors(screen, opts):
|
||||
screen.color_profile.update_ansi_color_table(build_ansi_color_table(opts))
|
||||
cursor_text_color = opts.cursor_text_color or (12, 12, 12)
|
||||
cursor_text_color_as_bg = 3 if opts.cursor_text_color is None else 1
|
||||
screen.color_profile.set_configured_colors(*map(color_as_int, (
|
||||
opts.foreground, opts.background, opts.cursor, opts.selection_foreground, opts.selection_background)))
|
||||
opts.foreground, opts.background, opts.cursor,
|
||||
cursor_text_color, (0, 0, cursor_text_color_as_bg),
|
||||
opts.selection_foreground, opts.selection_background)
|
||||
))
|
||||
|
||||
|
||||
def text_sanitizer(as_ansi, add_wrap_markers):
|
||||
@@ -165,6 +167,7 @@ class Window:
|
||||
cwd=self.child.current_cwd or self.child.cwd,
|
||||
cmdline=self.child.cmdline,
|
||||
env=self.child.environ,
|
||||
foreground_processes=self.child.foreground_processes
|
||||
)
|
||||
|
||||
@property
|
||||
@@ -477,11 +480,7 @@ class Window:
|
||||
|
||||
@property
|
||||
def cwd_of_child(self):
|
||||
# TODO: Maybe use the cwd of the leader of the foreground process
|
||||
# group in the session of the child process?
|
||||
pid = self.child.pid
|
||||
if pid is not None:
|
||||
return cwd_of_process(pid) or None
|
||||
return self.child.foreground_cwd or self.child.current_cwd
|
||||
|
||||
def pipe_data(self, text, has_wrap_markers=False):
|
||||
text = text or ''
|
||||
@@ -525,6 +524,15 @@ class Window:
|
||||
if text:
|
||||
set_clipboard_string(text)
|
||||
|
||||
def copy_or_interrupt(self):
|
||||
text = self.text_for_selection()
|
||||
if text:
|
||||
set_clipboard_string(text)
|
||||
else:
|
||||
mode = keyboard_mode_name(self.screen)
|
||||
text = extended_key_event(defines.GLFW_KEY_C, defines.GLFW_MOD_CONTROL, defines.GLFW_PRESS) if mode == 'kitty' else b'\x03'
|
||||
self.write_to_child(text)
|
||||
|
||||
def pass_selection_to_program(self, *args):
|
||||
cwd = self.cwd_of_child
|
||||
text = self.text_for_selection()
|
||||
|
||||
@@ -2,13 +2,16 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from kitty.config import build_ansi_color_table, defaults
|
||||
from kitty.fast_data_types import (
|
||||
REVERSE, ColorProfile, Cursor as C, HistoryBuf, LineBuf,
|
||||
parse_input_from_terminal, wcswidth, wcwidth, truncate_point_for_length
|
||||
parse_input_from_terminal, truncate_point_for_length, wcswidth, wcwidth
|
||||
)
|
||||
from kitty.rgb import to_color
|
||||
from kitty.utils import sanitize_title
|
||||
from kitty.utils import is_path_in_temp_dir, sanitize_title
|
||||
|
||||
from . import BaseTest, filled_cursor, filled_history_buf, filled_line_buf
|
||||
|
||||
@@ -384,6 +387,12 @@ class TestDataTypes(BaseTest):
|
||||
tp('a\033', '_', 'x\033', '\\b', text='a b', apc='x')
|
||||
tp('a\033_', 'x', '\033', '\\', 'b', text='a b', apc='x')
|
||||
|
||||
for prefix in ('/tmp', tempfile.gettempdir()):
|
||||
for path in ('a.png', 'x/b.jpg', 'y/../c.jpg'):
|
||||
self.assertTrue(is_path_in_temp_dir(os.path.join(prefix, path)))
|
||||
for path in ('/home/xy/d.png', '/tmp/../home/x.jpg'):
|
||||
self.assertFalse(is_path_in_temp_dir(os.path.join(path)))
|
||||
|
||||
def test_color_profile(self):
|
||||
c = ColorProfile()
|
||||
c.update_ansi_color_table(build_ansi_color_table())
|
||||
|
||||
17
setup.py
17
setup.py
@@ -190,7 +190,7 @@ def init_env(
|
||||
cppflags = shlex.split(cppflags)
|
||||
cflags = os.environ.get(
|
||||
'OVERRIDE_CFLAGS', (
|
||||
'-Wextra -Wno-missing-field-initializers -Wall -std=c99'
|
||||
'-Wextra -Wno-missing-field-initializers -Wall -std=c11'
|
||||
' -pedantic-errors -Werror {} {} -fwrapv {} {} -pipe {} -fvisibility=hidden'
|
||||
).format(
|
||||
optimize,
|
||||
@@ -393,14 +393,23 @@ def compile_c_extension(kenv, module, incremental, compilation_database, all_key
|
||||
todo[original_src] = cmd
|
||||
if todo:
|
||||
parallel_run(todo)
|
||||
dest = os.path.join(base, module + '.so')
|
||||
if not incremental or newer(dest, *objects):
|
||||
dest = os.path.join(base, module + '.temp.so')
|
||||
real_dest = dest[:-len('.temp.so')] + '.so'
|
||||
if not incremental or newer(real_dest, *objects):
|
||||
# Old versions of clang don't like -pthread being passed to the linker
|
||||
# Don't treat linker warnings as errors (linker generates spurious
|
||||
# warnings on some old systems)
|
||||
unsafe = {'-pthread', '-Werror', '-pedantic-errors'}
|
||||
linker_cflags = list(filter(lambda x: x not in unsafe, kenv.cflags))
|
||||
run_tool([kenv.cc] + linker_cflags + kenv.ldflags + objects + kenv.ldpaths + ['-o', dest], desc='Linking {} ...'.format(emphasis(module)))
|
||||
try:
|
||||
run_tool([kenv.cc] + linker_cflags + kenv.ldflags + objects + kenv.ldpaths + ['-o', dest], desc='Linking {} ...'.format(emphasis(module)))
|
||||
except Exception:
|
||||
try:
|
||||
os.remove(dest)
|
||||
except EnvironmentError:
|
||||
pass
|
||||
else:
|
||||
os.rename(dest, real_dest)
|
||||
|
||||
|
||||
def find_c_files():
|
||||
|
||||
Binary file not shown.
@@ -57,10 +57,66 @@ xterm-kitty|KovIdTTY,
|
||||
ind=^J,
|
||||
indn=\E[%p1%dS,
|
||||
initc=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\,
|
||||
kDC=\E[3;2~,
|
||||
kDC3=\E[3;3~,
|
||||
kDC4=\E[3;4~,
|
||||
kDC5=\E[3;5~,
|
||||
kDC6=\E[3;6~,
|
||||
kDC7=\E[3;7~,
|
||||
kDN=\E[1;2B,
|
||||
kDN3=\E[1;3B,
|
||||
kDN4=\E[1;4B,
|
||||
kDN5=\E[1;5B,
|
||||
kDN6=\E[1;6B,
|
||||
kDN7=\E[1;7B,
|
||||
kEND=\E[1;2F,
|
||||
kEND3=\E[1;3F,
|
||||
kEND4=\E[1;4F,
|
||||
kEND5=\E[1;5F,
|
||||
kEND6=\E[1;6F,
|
||||
kEND7=\E[1;7F,
|
||||
kHOM=\E[1;2H,
|
||||
kHOM3=\E[1;3H,
|
||||
kHOM4=\E[1;4H,
|
||||
kHOM5=\E[1;5H,
|
||||
kHOM6=\E[1;6H,
|
||||
kHOM7=\E[1;7H,
|
||||
kIC=\E[2;2~,
|
||||
kIC3=\E[2;3~,
|
||||
kIC4=\E[2;4~,
|
||||
kIC5=\E[2;5~,
|
||||
kIC6=\E[2;6~,
|
||||
kIC7=\E[2;7~,
|
||||
kLFT=\E[1;2D,
|
||||
kLFT3=\E[1;3D,
|
||||
kLFT4=\E[1;4D,
|
||||
kLFT5=\E[1;5D,
|
||||
kLFT6=\E[1;6D,
|
||||
kLFT7=\E[1;7D,
|
||||
kNXT=\E[6;2~,
|
||||
kNXT3=\E[6;3~,
|
||||
kNXT4=\E[6;4~,
|
||||
kNXT5=\E[6;5~,
|
||||
kNXT6=\E[6;6~,
|
||||
kNXT7=\E[6;7~,
|
||||
kPRV=\E[5;2~,
|
||||
kPRV3=\E[5;3~,
|
||||
kPRV4=\E[5;4~,
|
||||
kPRV5=\E[5;5~,
|
||||
kPRV6=\E[5;6~,
|
||||
kPRV7=\E[5;7~,
|
||||
kRIT=\E[1;2C,
|
||||
kRIT3=\E[1;3C,
|
||||
kRIT4=\E[1;4C,
|
||||
kRIT5=\E[1;5C,
|
||||
kRIT6=\E[1;6C,
|
||||
kRIT7=\E[1;7C,
|
||||
kUP=\E[1;2A,
|
||||
kUP3=\E[1;3A,
|
||||
kUP4=\E[1;4A,
|
||||
kUP5=\E[1;5A,
|
||||
kUP6=\E[1;6A,
|
||||
kUP7=\E[1;7A,
|
||||
ka1=,
|
||||
ka3=,
|
||||
kbs=\177,
|
||||
@@ -91,10 +147,48 @@ xterm-kitty|KovIdTTY,
|
||||
kf23=\E[23;2~,
|
||||
kf24=\E[24;2~,
|
||||
kf25=\E[1;5P,
|
||||
kf26=\E[1;5Q,
|
||||
kf27=\E[1;5R,
|
||||
kf28=\E[1;5S,
|
||||
kf29=\E[15;5~,
|
||||
kf3=\EOR,
|
||||
kf30=\E[17;5~,
|
||||
kf31=\E[18;5~,
|
||||
kf32=\E[19;5~,
|
||||
kf33=\E[20;5~,
|
||||
kf34=\E[21;5~,
|
||||
kf35=\E[23;5~,
|
||||
kf36=\E[24;5~,
|
||||
kf37=\E[1;6P,
|
||||
kf38=\E[1;6Q,
|
||||
kf39=\E[1;6R,
|
||||
kf4=\EOS,
|
||||
kf40=\E[1;6S,
|
||||
kf41=\E[15;6~,
|
||||
kf42=\E[17;6~,
|
||||
kf43=\E[18;6~,
|
||||
kf44=\E[19;6~,
|
||||
kf45=\E[20;6~,
|
||||
kf46=\E[21;6~,
|
||||
kf47=\E[23;6~,
|
||||
kf48=\E[24;6~,
|
||||
kf49=\E[1;3P,
|
||||
kf5=\E[15~,
|
||||
kf50=\E[1;3Q,
|
||||
kf51=\E[1;3R,
|
||||
kf52=\E[1;3S,
|
||||
kf53=\E[15;3~,
|
||||
kf54=\E[17;3~,
|
||||
kf55=\E[18;3~,
|
||||
kf56=\E[19;3~,
|
||||
kf57=\E[20;3~,
|
||||
kf58=\E[21;3~,
|
||||
kf59=\E[23;3~,
|
||||
kf6=\E[17~,
|
||||
kf60=\E[24;3~,
|
||||
kf61=\E[1;4P,
|
||||
kf62=\E[1;4Q,
|
||||
kf63=\E[1;4R,
|
||||
kf7=\E[18~,
|
||||
kf8=\E[19~,
|
||||
kf9=\E[20~,
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user