Compare commits
70 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7438e0e997 | ||
|
|
cca672e536 | ||
|
|
0fa12a687d | ||
|
|
d2b900c77f | ||
|
|
21242d9144 | ||
|
|
03fd138b63 | ||
|
|
1eeaef6dbb | ||
|
|
b9a93b68c4 | ||
|
|
e3728942dc | ||
|
|
7f0674ac27 | ||
|
|
60fcf7ab3d | ||
|
|
ee82a6ff18 | ||
|
|
bac5e402b7 | ||
|
|
732108109e | ||
|
|
ab3b86d65b | ||
|
|
d4d63506e1 | ||
|
|
3ad69f8ba3 | ||
|
|
9fd50a6ac0 | ||
|
|
9b5c8a18d4 | ||
|
|
9f3112ca56 | ||
|
|
aa9de47da3 | ||
|
|
5eaeda4402 | ||
|
|
ecb03d7dba | ||
|
|
88612d3691 | ||
|
|
2c0e70a423 | ||
|
|
d6b70f10d3 | ||
|
|
40b01fb702 | ||
|
|
89222d93f4 | ||
|
|
b3db3e5732 | ||
|
|
b45161ecc3 | ||
|
|
d4fc0af7de | ||
|
|
5ef8cce1d7 | ||
|
|
c98a147f94 | ||
|
|
e9975cb7eb | ||
|
|
33f0ac83c1 | ||
|
|
3bd45966c6 | ||
|
|
1b7244f3c8 | ||
|
|
1949f3c3d5 | ||
|
|
6a51ce5dc4 | ||
|
|
c0c8e1ce5e | ||
|
|
6a3d6191bd | ||
|
|
b2aa07d29f | ||
|
|
c84fb68bf8 | ||
|
|
128b9e1cd0 | ||
|
|
bbe8bcb28d | ||
|
|
6dc9c22543 | ||
|
|
59f5781f19 | ||
|
|
d61fb9cc42 | ||
|
|
ace052ee70 | ||
|
|
1451a8ccc0 | ||
|
|
547959c1ef | ||
|
|
b1a5e98630 | ||
|
|
f3a1ee7b9f | ||
|
|
b0a39b127d | ||
|
|
e36f1157cc | ||
|
|
053b9a786c | ||
|
|
2561d937da | ||
|
|
55f457caf4 | ||
|
|
f5cc2fd8ad | ||
|
|
e630b76fac | ||
|
|
0b6948cb4f | ||
|
|
b0a2dea747 | ||
|
|
f8395e36a1 | ||
|
|
6ec58f58ce | ||
|
|
96cbfe946c | ||
|
|
ac686a47c1 | ||
|
|
e05aaf77ce | ||
|
|
6b371eb8a8 | ||
|
|
f6660a0d55 | ||
|
|
3b9f213ea2 |
13
__main__.py
13
__main__.py
@@ -7,8 +7,9 @@ import os
|
|||||||
|
|
||||||
|
|
||||||
def icat(args):
|
def icat(args):
|
||||||
from kittens.icat.main import main
|
from kittens.runner import run_kitten
|
||||||
main(args)
|
sys.argv = args
|
||||||
|
run_kitten('icat')
|
||||||
|
|
||||||
|
|
||||||
def list_fonts(args):
|
def list_fonts(args):
|
||||||
@@ -41,7 +42,13 @@ def complete(args):
|
|||||||
def launch(args):
|
def launch(args):
|
||||||
import runpy
|
import runpy
|
||||||
sys.argv = args[1:]
|
sys.argv = args[1:]
|
||||||
runpy.run_path(args[1], run_name='__main__')
|
exe = args[1]
|
||||||
|
if exe.startswith(':'):
|
||||||
|
import shutil
|
||||||
|
exe = shutil.which(exe[1:])
|
||||||
|
if not exe:
|
||||||
|
raise SystemExit('{} not found in PATH'.format(args[1][1:]))
|
||||||
|
runpy.run_path(exe, run_name='__main__')
|
||||||
|
|
||||||
|
|
||||||
def run_kitten(args):
|
def run_kitten(args):
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
# You can set these variables from the command line.
|
# You can set these variables from the command line.
|
||||||
SPHINXOPTS = -T $(FAIL_WARN)
|
SPHINXOPTS = -T $(FAIL_WARN)
|
||||||
SPHINXBUILD = python3 .. +launch $(shell which sphinx-build)
|
SPHINXBUILD = python3 .. +launch :sphinx-build
|
||||||
SPHINXPROJ = kitty
|
SPHINXPROJ = kitty
|
||||||
SOURCEDIR = .
|
SOURCEDIR = .
|
||||||
BUILDDIR = _build
|
BUILDDIR = _build
|
||||||
|
|||||||
@@ -58,6 +58,13 @@ Customizing the installation
|
|||||||
|ins| \\
|
|ins| \\
|
||||||
installer=/path/to/dmg or tarball
|
installer=/path/to/dmg or tarball
|
||||||
|
|
||||||
|
Uninstalling
|
||||||
|
----------------
|
||||||
|
|
||||||
|
All the installer does is copy the kitty files into the install directory
|
||||||
|
(defaults to :file:`~/.local/kitty.app`). To uninstall, simply delete that
|
||||||
|
directory.
|
||||||
|
|
||||||
|
|
||||||
Building from source
|
Building from source
|
||||||
------------------------
|
------------------------
|
||||||
|
|||||||
@@ -3,6 +3,49 @@ Changelog
|
|||||||
|
|
||||||
|kitty| is a feature full, cross-platform, *fast*, GPU based terminal emulator.
|
|kitty| is a feature full, cross-platform, *fast*, GPU based terminal emulator.
|
||||||
|
|
||||||
|
0.12.1 [2018-09-08]
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
- Add a new :option:`kitty --start-in-fullscreen` command line flag to start
|
||||||
|
kitty in full screen mode (:iss:`856`)
|
||||||
|
|
||||||
|
- macOS: Fix a character that cannot be rendered in any font causing
|
||||||
|
font fallback for all subsequent characters that cannot be rendered in the
|
||||||
|
main font to fail (:iss:`799`)
|
||||||
|
|
||||||
|
- Linux: Do not enable IME input via ibus unless the ``GLFW_IM_MODULE=ibus``
|
||||||
|
environment variable is set. IME causes key processing latency and even
|
||||||
|
missed keystrokes for many people, so it is now off by default.
|
||||||
|
|
||||||
|
- Fix backspacing of wide characters in wide-character unaware programs not working (:iss:`875`)
|
||||||
|
|
||||||
|
- Linux: Fix number pad arrow keys not working when Numlock is off (:iss:`857`)
|
||||||
|
|
||||||
|
- Wayland: Implement support for clipboard copy/paste (:iss:`855`)
|
||||||
|
|
||||||
|
- Allow mapping shortcuts using the raw key code from the OS (:iss:`848`)
|
||||||
|
|
||||||
|
- Allow mapping of individual key-presses without modifiers as shortcuts
|
||||||
|
|
||||||
|
- Fix legacy invocation of icat as `kitty icat` not working (:iss:`850`)
|
||||||
|
|
||||||
|
- Improve rendering of wavy underline at small font sizes (:iss:`853`)
|
||||||
|
|
||||||
|
- Fix a regression in 0.12.0 that broke dynamic resizing of layouts (:iss:`860`)
|
||||||
|
|
||||||
|
- Wayland: Allow using the :option:`kitty --class` command line flag
|
||||||
|
to set the app id (:iss:`862`)
|
||||||
|
|
||||||
|
- Add completion of the kitty command for the fish shell (:pull:`829`)
|
||||||
|
|
||||||
|
- Linux: Fix XCompose rules with no defined symbol not working (:iss:`880`)
|
||||||
|
|
||||||
|
- Linux: Fix crash with some Nvidia drivers when creating tabs in the first
|
||||||
|
top level-window after creating a second top-level window. (:iss:`873`)
|
||||||
|
|
||||||
|
- macOS: Diff kitten: Fix syntax highlighting not working because of
|
||||||
|
a bug in the 0.12.0 macOS package
|
||||||
|
|
||||||
0.12.0 [2018-09-01]
|
0.12.0 [2018-09-01]
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ For some discussion regarding the design choices, see `#33
|
|||||||
|
|
||||||
To see a quick demo, inside a |kitty| terminal run::
|
To see a quick demo, inside a |kitty| terminal run::
|
||||||
|
|
||||||
kitty icat path/to/some/image.png
|
kitty +kitten icat path/to/some/image.png
|
||||||
|
|
||||||
You can also see a screenshot with more sophisticated features such as
|
You can also see a screenshot with more sophisticated features such as
|
||||||
alpha-blending and text over graphics.
|
alpha-blending and text over graphics.
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ Quickstart
|
|||||||
--------------
|
--------------
|
||||||
|
|
||||||
Pre-built binaries of |kitty| are available for both macOS and Linux.
|
Pre-built binaries of |kitty| are available for both macOS and Linux.
|
||||||
See the :doc:`binary install instructions </binary>`.
|
See the :doc:`binary install instructions </binary>`. You can also
|
||||||
|
:doc:`build from source </build>`.
|
||||||
|
|
||||||
If you are on Linux, you can also use your distribution's |kitty| package.
|
If you are on Linux, you can also use your distribution's |kitty| package.
|
||||||
|kitty| packages are available for:
|
|kitty| packages are available for:
|
||||||
@@ -60,7 +61,8 @@ If you are on Linux, you can also use your distribution's |kitty| package.
|
|||||||
`Arch Linux <https://www.archlinux.org/packages/community/x86_64/kitty/>`_,
|
`Arch Linux <https://www.archlinux.org/packages/community/x86_64/kitty/>`_,
|
||||||
`NixOS <https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/misc/kitty/default.nix>`_,
|
`NixOS <https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/misc/kitty/default.nix>`_,
|
||||||
`Gentoo <https://packages.gentoo.org/packages/x11-terms/kitty>`_,
|
`Gentoo <https://packages.gentoo.org/packages/x11-terms/kitty>`_,
|
||||||
`Fedora <https://copr.fedorainfracloud.org/coprs/oleastre/kitty-terminal/>`_.
|
`Fedora <https://copr.fedorainfracloud.org/coprs/gagbo/kitty-latest/>`_,
|
||||||
|
`Void Linux <https://github.com/void-linux/void-packages/blob/master/srcpkgs/kitty/template>`_.
|
||||||
|
|
||||||
See :doc:`Configuring kitty <conf>` for help on configuring |kitty| and
|
See :doc:`Configuring kitty <conf>` for help on configuring |kitty| and
|
||||||
:doc:`Invocation <invocation>` for the command line arguments |kitty| supports.
|
:doc:`Invocation <invocation>` for the command line arguments |kitty| supports.
|
||||||
@@ -123,7 +125,7 @@ Action Shortcut
|
|||||||
New tab :sc:`new_tab`
|
New tab :sc:`new_tab`
|
||||||
Close tab :sc:`close_tab`
|
Close tab :sc:`close_tab`
|
||||||
Next tab :sc:`next_tab` (also :kbd:`control+tab` on macOS)
|
Next tab :sc:`next_tab` (also :kbd:`control+tab` on macOS)
|
||||||
Previous tab :sc:`previous_tab`
|
Previous tab :sc:`previous_tab` (also :kbd:`control+shift+tab` on macOS)
|
||||||
Next layout :sc:`next_layout`
|
Next layout :sc:`next_layout`
|
||||||
Move tab forward :sc:`move_tab_forward`
|
Move tab forward :sc:`move_tab_forward`
|
||||||
Move tab backward :sc:`move_tab_backward`
|
Move tab backward :sc:`move_tab_backward`
|
||||||
@@ -181,6 +183,9 @@ Reset background opacity :sc:`reset_background_opacity`
|
|||||||
Layouts
|
Layouts
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
A layout is an arrangement of multiple *windows*. You can create a new window
|
||||||
|
using the :sc:`new_window` key combination.
|
||||||
|
|
||||||
Currently, there are five layouts available,
|
Currently, there are five layouts available,
|
||||||
|
|
||||||
* **Stack** -- Only a single maximized window is shown at a time
|
* **Stack** -- Only a single maximized window is shown at a time
|
||||||
@@ -384,6 +389,16 @@ The important thing above is to make sure the call to |kitty| to load the zsh
|
|||||||
completions happens after the call to :file:`compinit`.
|
completions happens after the call to :file:`compinit`.
|
||||||
|
|
||||||
|
|
||||||
|
fish
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
Add the following to your :file:`~/.config/fish/config.fish`
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
kitty + complete setup fish | source
|
||||||
|
|
||||||
|
|
||||||
Changelog
|
Changelog
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ It supports all image types supported by `ImageMagick
|
|||||||
<https://www.imagemagick.org>`_. It even works over SSH. For details, see
|
<https://www.imagemagick.org>`_. It even works over SSH. For details, see
|
||||||
the :doc:`kitty graphics protocol </graphics-protocol>`.
|
the :doc:`kitty graphics protocol </graphics-protocol>`.
|
||||||
|
|
||||||
|
You might want to create an alias in your shell's configuration files::
|
||||||
|
|
||||||
|
alias icat="kitty +kitten icat"
|
||||||
|
|
||||||
|
Then you can simply use ``icat image.png`` to view images.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
`ImageMagick <https://www.imagemagick.org>`_ must be installed for ``icat`` to
|
`ImageMagick <https://www.imagemagick.org>`_ must be installed for ``icat`` to
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ glyph in video RAM so that font rendering is not a bottleneck. Interaction
|
|||||||
with child programs takes place in a separate thread from rendering, to improve
|
with child programs takes place in a separate thread from rendering, to improve
|
||||||
smoothness.
|
smoothness.
|
||||||
|
|
||||||
There are two parameters you can tune to adjust the performance. ``repaint_delay``
|
There are two parameters you can tune to adjust the performance. :opt:`repaint_delay`
|
||||||
and ``input_delay``. These control the artificial delays introduced into the
|
and :opt:`input_delay`. These control the artificial delays introduced into the
|
||||||
render loop to reduce CPU usage. See :ref:`conf-kitty-performance` for details.
|
render loop to reduce CPU usage. See :ref:`conf-kitty-performance` for details.
|
||||||
See also the ``sync_to_monitor`` option to further decrease latency at the cost
|
See also the :opt:`sync_to_monitor` option to further decrease latency at the cost
|
||||||
of some `tearing <https://en.wikipedia.org/wiki/Screen_tearing>`_ while scrolling.
|
of some `tearing <https://en.wikipedia.org/wiki/Screen_tearing>`_ while scrolling.
|
||||||
|
|
||||||
You can generate detailed per-function performance data using
|
You can generate detailed per-function performance data using
|
||||||
|
|||||||
51
glfw/backend_utils.c
vendored
51
glfw/backend_utils.c
vendored
@@ -261,3 +261,54 @@ closeFds(int *fds, size_t count) {
|
|||||||
fds++;
|
fds++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Splits and translates a text/uri-list into separate file paths
|
||||||
|
// NOTE: This function destroys the provided string
|
||||||
|
//
|
||||||
|
char** parseUriList(char* text, int* count)
|
||||||
|
{
|
||||||
|
const char* prefix = "file://";
|
||||||
|
char** paths = NULL;
|
||||||
|
char* line;
|
||||||
|
|
||||||
|
*count = 0;
|
||||||
|
|
||||||
|
while ((line = strtok(text, "\r\n")))
|
||||||
|
{
|
||||||
|
text = NULL;
|
||||||
|
|
||||||
|
if (line[0] == '#')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (strncmp(line, prefix, strlen(prefix)) == 0)
|
||||||
|
{
|
||||||
|
line += strlen(prefix);
|
||||||
|
// TODO: Validate hostname
|
||||||
|
while (*line != '/')
|
||||||
|
line++;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*count)++;
|
||||||
|
|
||||||
|
char* path = calloc(strlen(line) + 1, 1);
|
||||||
|
paths = realloc(paths, *count * sizeof(char*));
|
||||||
|
paths[*count - 1] = path;
|
||||||
|
|
||||||
|
while (*line)
|
||||||
|
{
|
||||||
|
if (line[0] == '%' && line[1] && line[2])
|
||||||
|
{
|
||||||
|
const char digits[3] = { line[1], line[2], '\0' };
|
||||||
|
*path = strtol(digits, NULL, 16);
|
||||||
|
line += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
*path = *line;
|
||||||
|
|
||||||
|
path++;
|
||||||
|
line++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|||||||
1
glfw/backend_utils.h
vendored
1
glfw/backend_utils.h
vendored
@@ -71,3 +71,4 @@ int pollForEvents(EventLoopData *eld, double timeout);
|
|||||||
unsigned dispatchTimers(EventLoopData *eld);
|
unsigned dispatchTimers(EventLoopData *eld);
|
||||||
void closeFds(int *fds, size_t count);
|
void closeFds(int *fds, size_t count);
|
||||||
void initPollData(EventLoopData *eld, int wakeup_fd, int display_fd);
|
void initPollData(EventLoopData *eld, int wakeup_fd, int display_fd);
|
||||||
|
char** parseUriList(char* text, int* count);
|
||||||
|
|||||||
12
glfw/glfw.py
12
glfw/glfw.py
@@ -128,16 +128,6 @@ def collect_source_information():
|
|||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
def patch_in_file(path, pfunc):
|
|
||||||
with open(path, 'r+') as f:
|
|
||||||
raw = f.read()
|
|
||||||
nraw = pfunc(raw)
|
|
||||||
if raw == nraw:
|
|
||||||
raise SystemExit('Patching of {} failed'.format(path))
|
|
||||||
f.seek(0), f.truncate()
|
|
||||||
f.write(nraw)
|
|
||||||
|
|
||||||
|
|
||||||
class Arg:
|
class Arg:
|
||||||
|
|
||||||
def __init__(self, decl):
|
def __init__(self, decl):
|
||||||
@@ -283,8 +273,6 @@ def from_glfw(glfw_dir):
|
|||||||
for src in files_to_copy:
|
for src in files_to_copy:
|
||||||
shutil.copy2(src, '.')
|
shutil.copy2(src, '.')
|
||||||
shutil.copy2(glfw_header, '.')
|
shutil.copy2(glfw_header, '.')
|
||||||
patch_in_file('cocoa_window.m', lambda x: re.sub(
|
|
||||||
r'[(]void[)]loadMainMenu.+?}', '(void)loadMainMenu\n{ // removed by Kovid as it generated compiler warnings \n}\n', x, flags=re.DOTALL))
|
|
||||||
json.dump(
|
json.dump(
|
||||||
sinfo,
|
sinfo,
|
||||||
open('source-info.json', 'w'),
|
open('source-info.json', 'w'),
|
||||||
|
|||||||
6
glfw/glfw3.h
vendored
6
glfw/glfw3.h
vendored
@@ -973,6 +973,10 @@ extern "C" {
|
|||||||
* [attribute](@ref GLFW_CLIENT_API_attrib).
|
* [attribute](@ref GLFW_CLIENT_API_attrib).
|
||||||
*/
|
*/
|
||||||
#define GLFW_CONTEXT_CREATION_API 0x0002200B
|
#define GLFW_CONTEXT_CREATION_API 0x0002200B
|
||||||
|
/*! @brief Window content area scaling window
|
||||||
|
* [window hint](@ref GLFW_SCALE_TO_MONITOR).
|
||||||
|
*/
|
||||||
|
#define GLFW_SCALE_TO_MONITOR 0x0002200C
|
||||||
|
|
||||||
#define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001
|
#define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001
|
||||||
#define GLFW_COCOA_FRAME_NAME 0x00023002
|
#define GLFW_COCOA_FRAME_NAME 0x00023002
|
||||||
@@ -980,6 +984,8 @@ extern "C" {
|
|||||||
|
|
||||||
#define GLFW_X11_CLASS_NAME 0x00024001
|
#define GLFW_X11_CLASS_NAME 0x00024001
|
||||||
#define GLFW_X11_INSTANCE_NAME 0x00024002
|
#define GLFW_X11_INSTANCE_NAME 0x00024002
|
||||||
|
|
||||||
|
#define GLFW_WAYLAND_APP_ID 0x00025001
|
||||||
/*! @} */
|
/*! @} */
|
||||||
|
|
||||||
#define GLFW_NO_API 0
|
#define GLFW_NO_API 0
|
||||||
|
|||||||
3
glfw/ibus_glfw.c
vendored
3
glfw/ibus_glfw.c
vendored
@@ -280,8 +280,7 @@ setup_connection(_GLFWIBUSData *ibus) {
|
|||||||
void
|
void
|
||||||
glfw_connect_to_ibus(_GLFWIBUSData *ibus) {
|
glfw_connect_to_ibus(_GLFWIBUSData *ibus) {
|
||||||
if (ibus->inited) return;
|
if (ibus->inited) return;
|
||||||
if (!test_env_var("XMODIFIERS", "@im=ibus") && !test_env_var("GTK_IM_MODULE", "ibus") && !test_env_var("QT_IM_MODULE", "ibus") && !test_env_var("GLFW_IM_MODULE", "ibus")) return;
|
if (!test_env_var("GLFW_IM_MODULE", "ibus")) return;
|
||||||
if (getenv("GLFW_IM_MODULE") && !test_env_var("GLFW_IM_MODULE", "ibus")) return;
|
|
||||||
ibus->inited = GLFW_TRUE;
|
ibus->inited = GLFW_TRUE;
|
||||||
setup_connection(ibus);
|
setup_connection(ibus);
|
||||||
}
|
}
|
||||||
|
|||||||
6
glfw/internal.h
vendored
6
glfw/internal.h
vendored
@@ -31,6 +31,8 @@
|
|||||||
#include "glfw_config.h"
|
#include "glfw_config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define arraysz(x) (sizeof(x)/sizeof(x[0]))
|
||||||
|
|
||||||
#if defined(GLFW_INCLUDE_GLCOREARB) || \
|
#if defined(GLFW_INCLUDE_GLCOREARB) || \
|
||||||
defined(GLFW_INCLUDE_ES1) || \
|
defined(GLFW_INCLUDE_ES1) || \
|
||||||
defined(GLFW_INCLUDE_ES2) || \
|
defined(GLFW_INCLUDE_ES2) || \
|
||||||
@@ -271,6 +273,7 @@ struct _GLFWwndconfig
|
|||||||
GLFWbool maximized;
|
GLFWbool maximized;
|
||||||
GLFWbool centerCursor;
|
GLFWbool centerCursor;
|
||||||
GLFWbool focusOnShow;
|
GLFWbool focusOnShow;
|
||||||
|
GLFWbool scaleToMonitor;
|
||||||
struct {
|
struct {
|
||||||
GLFWbool retina;
|
GLFWbool retina;
|
||||||
char frameName[256];
|
char frameName[256];
|
||||||
@@ -279,6 +282,9 @@ struct _GLFWwndconfig
|
|||||||
char className[256];
|
char className[256];
|
||||||
char instanceName[256];
|
char instanceName[256];
|
||||||
} x11;
|
} x11;
|
||||||
|
struct {
|
||||||
|
char appId[256];
|
||||||
|
} wl;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Context configuration
|
// Context configuration
|
||||||
|
|||||||
34
glfw/wgl_context.c
vendored
34
glfw/wgl_context.c
vendored
@@ -256,12 +256,20 @@ static void makeContextCurrentWGL(_GLFWwindow* window)
|
|||||||
|
|
||||||
static void swapBuffersWGL(_GLFWwindow* window)
|
static void swapBuffersWGL(_GLFWwindow* window)
|
||||||
{
|
{
|
||||||
// HACK: Use DwmFlush when desktop composition is enabled
|
if (!window->monitor)
|
||||||
if (_glfwIsCompositionEnabledWin32() && !window->monitor)
|
|
||||||
{
|
{
|
||||||
int count = abs(window->context.wgl.interval);
|
if (IsWindowsVistaOrGreater())
|
||||||
while (count--)
|
{
|
||||||
DwmFlush();
|
BOOL enabled;
|
||||||
|
|
||||||
|
// HACK: Use DwmFlush when desktop composition is enabled
|
||||||
|
if (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled)
|
||||||
|
{
|
||||||
|
int count = abs(window->context.wgl.interval);
|
||||||
|
while (count--)
|
||||||
|
DwmFlush();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SwapBuffers(window->context.wgl.dc);
|
SwapBuffers(window->context.wgl.dc);
|
||||||
@@ -273,10 +281,18 @@ static void swapIntervalWGL(int interval)
|
|||||||
|
|
||||||
window->context.wgl.interval = interval;
|
window->context.wgl.interval = interval;
|
||||||
|
|
||||||
// HACK: Disable WGL swap interval when desktop composition is enabled to
|
if (!window->monitor)
|
||||||
// avoid interfering with DWM vsync
|
{
|
||||||
if (_glfwIsCompositionEnabledWin32() && !window->monitor)
|
if (IsWindowsVistaOrGreater())
|
||||||
interval = 0;
|
{
|
||||||
|
BOOL enabled;
|
||||||
|
|
||||||
|
// HACK: Disable WGL swap interval when desktop composition is enabled to
|
||||||
|
// avoid interfering with DWM vsync
|
||||||
|
if (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled)
|
||||||
|
interval = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_glfw.wgl.EXT_swap_control)
|
if (_glfw.wgl.EXT_swap_control)
|
||||||
_glfw.wgl.SwapIntervalEXT(interval);
|
_glfw.wgl.SwapIntervalEXT(interval);
|
||||||
|
|||||||
68
glfw/win32_init.c
vendored
68
glfw/win32_init.c
vendored
@@ -62,17 +62,6 @@ BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
|
|||||||
|
|
||||||
#endif // _GLFW_BUILD_DLL
|
#endif // _GLFW_BUILD_DLL
|
||||||
|
|
||||||
// HACK: Define versionhelpers.h functions manually as MinGW lacks the header
|
|
||||||
BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp)
|
|
||||||
{
|
|
||||||
OSVERSIONINFOEXW osvi = { sizeof(osvi), major, minor, 0, 0, {0}, sp };
|
|
||||||
DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR;
|
|
||||||
ULONGLONG cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL);
|
|
||||||
cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL);
|
|
||||||
cond = VerSetConditionMask(cond, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
|
|
||||||
return VerifyVersionInfoW(&osvi, mask, cond);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load necessary libraries (DLLs)
|
// Load necessary libraries (DLLs)
|
||||||
//
|
//
|
||||||
static GLFWbool loadLibraries(void)
|
static GLFWbool loadLibraries(void)
|
||||||
@@ -100,6 +89,14 @@ static GLFWbool loadLibraries(void)
|
|||||||
GetProcAddress(_glfw.win32.user32.instance, "SetProcessDPIAware");
|
GetProcAddress(_glfw.win32.user32.instance, "SetProcessDPIAware");
|
||||||
_glfw.win32.user32.ChangeWindowMessageFilterEx_ = (PFN_ChangeWindowMessageFilterEx)
|
_glfw.win32.user32.ChangeWindowMessageFilterEx_ = (PFN_ChangeWindowMessageFilterEx)
|
||||||
GetProcAddress(_glfw.win32.user32.instance, "ChangeWindowMessageFilterEx");
|
GetProcAddress(_glfw.win32.user32.instance, "ChangeWindowMessageFilterEx");
|
||||||
|
_glfw.win32.user32.EnableNonClientDpiScaling_ = (PFN_EnableNonClientDpiScaling)
|
||||||
|
GetProcAddress(_glfw.win32.user32.instance, "EnableNonClientDpiScaling");
|
||||||
|
_glfw.win32.user32.SetProcessDpiAwarenessContext_ = (PFN_SetProcessDpiAwarenessContext)
|
||||||
|
GetProcAddress(_glfw.win32.user32.instance, "SetProcessDpiAwarenessContext");
|
||||||
|
_glfw.win32.user32.GetDpiForWindow_ = (PFN_GetDpiForWindow)
|
||||||
|
GetProcAddress(_glfw.win32.user32.instance, "GetDpiForWindow");
|
||||||
|
_glfw.win32.user32.AdjustWindowRectExForDpi_ = (PFN_AdjustWindowRectExForDpi)
|
||||||
|
GetProcAddress(_glfw.win32.user32.instance, "AdjustWindowRectExForDpi");
|
||||||
|
|
||||||
_glfw.win32.dinput8.instance = LoadLibraryA("dinput8.dll");
|
_glfw.win32.dinput8.instance = LoadLibraryA("dinput8.dll");
|
||||||
if (_glfw.win32.dinput8.instance)
|
if (_glfw.win32.dinput8.instance)
|
||||||
@@ -155,6 +152,13 @@ static GLFWbool loadLibraries(void)
|
|||||||
GetProcAddress(_glfw.win32.shcore.instance, "GetDpiForMonitor");
|
GetProcAddress(_glfw.win32.shcore.instance, "GetDpiForMonitor");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_glfw.win32.ntdll.instance = LoadLibraryA("ntdll.dll");
|
||||||
|
if (_glfw.win32.ntdll.instance)
|
||||||
|
{
|
||||||
|
_glfw.win32.ntdll.RtlVerifyVersionInfo_ = (PFN_RtlVerifyVersionInfo)
|
||||||
|
GetProcAddress(_glfw.win32.ntdll.instance, "RtlVerifyVersionInfo");
|
||||||
|
}
|
||||||
|
|
||||||
return GLFW_TRUE;
|
return GLFW_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,6 +183,9 @@ static void freeLibraries(void)
|
|||||||
|
|
||||||
if (_glfw.win32.shcore.instance)
|
if (_glfw.win32.shcore.instance)
|
||||||
FreeLibrary(_glfw.win32.shcore.instance);
|
FreeLibrary(_glfw.win32.shcore.instance);
|
||||||
|
|
||||||
|
if (_glfw.win32.ntdll.instance)
|
||||||
|
FreeLibrary(_glfw.win32.ntdll.instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create key code translation tables
|
// Create key code translation tables
|
||||||
@@ -309,6 +316,7 @@ static void createKeyTables(void)
|
|||||||
_glfw.win32.keycodes[0x053] = GLFW_KEY_KP_DECIMAL;
|
_glfw.win32.keycodes[0x053] = GLFW_KEY_KP_DECIMAL;
|
||||||
_glfw.win32.keycodes[0x135] = GLFW_KEY_KP_DIVIDE;
|
_glfw.win32.keycodes[0x135] = GLFW_KEY_KP_DIVIDE;
|
||||||
_glfw.win32.keycodes[0x11C] = GLFW_KEY_KP_ENTER;
|
_glfw.win32.keycodes[0x11C] = GLFW_KEY_KP_ENTER;
|
||||||
|
_glfw.win32.keycodes[0x059] = GLFW_KEY_KP_EQUAL;
|
||||||
_glfw.win32.keycodes[0x037] = GLFW_KEY_KP_MULTIPLY;
|
_glfw.win32.keycodes[0x037] = GLFW_KEY_KP_MULTIPLY;
|
||||||
_glfw.win32.keycodes[0x04A] = GLFW_KEY_KP_SUBTRACT;
|
_glfw.win32.keycodes[0x04A] = GLFW_KEY_KP_SUBTRACT;
|
||||||
|
|
||||||
@@ -339,8 +347,8 @@ static HWND createHelperWindow(void)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// HACK: The first call to ShowWindow is ignored if the parent process
|
// HACK: The command to the first ShowWindow call is ignored if the parent
|
||||||
// passed along a STARTUPINFO, so clear that flag with a no-op call
|
// process passed along a STARTUPINFO, so clear that with a no-op call
|
||||||
ShowWindow(window, SW_HIDE);
|
ShowWindow(window, SW_HIDE);
|
||||||
|
|
||||||
// Register for HID device notifications
|
// Register for HID device notifications
|
||||||
@@ -502,6 +510,36 @@ void _glfwUpdateKeyNamesWin32(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Replacement for IsWindowsVersionOrGreater as MinGW lacks versionhelpers.h
|
||||||
|
//
|
||||||
|
BOOL _glfwIsWindowsVersionOrGreaterWin32(WORD major, WORD minor, WORD sp)
|
||||||
|
{
|
||||||
|
OSVERSIONINFOEXW osvi = { sizeof(osvi), major, minor, 0, 0, {0}, sp };
|
||||||
|
DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR;
|
||||||
|
ULONGLONG cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL);
|
||||||
|
cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL);
|
||||||
|
cond = VerSetConditionMask(cond, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
|
||||||
|
// HACK: Use RtlVerifyVersionInfo instead of VerifyVersionInfoW as the
|
||||||
|
// latter lies unless the user knew to embedd a non-default manifest
|
||||||
|
// announcing support for Windows 10 via supportedOS GUID
|
||||||
|
return RtlVerifyVersionInfo(&osvi, mask, cond) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks whether we are on at least the specified build of Windows 10
|
||||||
|
//
|
||||||
|
BOOL _glfwIsWindows10BuildOrGreaterWin32(WORD build)
|
||||||
|
{
|
||||||
|
OSVERSIONINFOEXW osvi = { sizeof(osvi), 10, 0, build };
|
||||||
|
DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER;
|
||||||
|
ULONGLONG cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL);
|
||||||
|
cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL);
|
||||||
|
cond = VerSetConditionMask(cond, VER_BUILDNUMBER, VER_GREATER_EQUAL);
|
||||||
|
// HACK: Use RtlVerifyVersionInfo instead of VerifyVersionInfoW as the
|
||||||
|
// latter lies unless the user knew to embedd a non-default manifest
|
||||||
|
// announcing support for Windows 10 via supportedOS GUID
|
||||||
|
return RtlVerifyVersionInfo(&osvi, mask, cond) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
////// GLFW platform API //////
|
////// GLFW platform API //////
|
||||||
@@ -523,7 +561,9 @@ int _glfwPlatformInit(void)
|
|||||||
createKeyTables();
|
createKeyTables();
|
||||||
_glfwUpdateKeyNamesWin32();
|
_glfwUpdateKeyNamesWin32();
|
||||||
|
|
||||||
if (IsWindows8Point1OrGreater())
|
if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32())
|
||||||
|
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
||||||
|
else if (IsWindows8Point1OrGreater())
|
||||||
SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
|
SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
|
||||||
else if (IsWindowsVistaOrGreater())
|
else if (IsWindowsVistaOrGreater())
|
||||||
SetProcessDPIAware();
|
SetProcessDPIAware();
|
||||||
|
|||||||
4
glfw/win32_monitor.c
vendored
4
glfw/win32_monitor.c
vendored
@@ -324,9 +324,9 @@ void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* ysc
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (xscale)
|
if (xscale)
|
||||||
*xscale = xdpi / 96.f;
|
*xscale = xdpi / (float) USER_DEFAULT_SCREEN_DPI;
|
||||||
if (yscale)
|
if (yscale)
|
||||||
*yscale = ydpi / 96.f;
|
*yscale = ydpi / (float) USER_DEFAULT_SCREEN_DPI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
70
glfw/win32_platform.h
vendored
70
glfw/win32_platform.h
vendored
@@ -98,6 +98,12 @@
|
|||||||
#ifndef _WIN32_WINNT_WINBLUE
|
#ifndef _WIN32_WINNT_WINBLUE
|
||||||
#define _WIN32_WINNT_WINBLUE 0x0602
|
#define _WIN32_WINNT_WINBLUE 0x0602
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef WM_GETDPISCALEDSIZE
|
||||||
|
#define WM_GETDPISCALEDSIZE 0x02e4
|
||||||
|
#endif
|
||||||
|
#ifndef USER_DEFAULT_SCREEN_DPI
|
||||||
|
#define USER_DEFAULT_SCREEN_DPI 96
|
||||||
|
#endif
|
||||||
|
|
||||||
#if WINVER < 0x0601
|
#if WINVER < 0x0601
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -140,23 +146,32 @@ typedef enum
|
|||||||
} MONITOR_DPI_TYPE;
|
} MONITOR_DPI_TYPE;
|
||||||
#endif /*DPI_ENUMS_DECLARED*/
|
#endif /*DPI_ENUMS_DECLARED*/
|
||||||
|
|
||||||
|
#ifndef _DPI_AWARENESS_CONTEXTS_
|
||||||
|
DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);
|
||||||
|
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT) -4)
|
||||||
|
#endif /*_DPI_AWARENESS_CONTEXTS_*/
|
||||||
|
|
||||||
// HACK: Define versionhelpers.h functions manually as MinGW lacks the header
|
// HACK: Define versionhelpers.h functions manually as MinGW lacks the header
|
||||||
BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp);
|
|
||||||
#define IsWindowsXPOrGreater() \
|
#define IsWindowsXPOrGreater() \
|
||||||
IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), \
|
_glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WINXP), \
|
||||||
LOBYTE(_WIN32_WINNT_WINXP), 0)
|
LOBYTE(_WIN32_WINNT_WINXP), 0)
|
||||||
#define IsWindowsVistaOrGreater() \
|
#define IsWindowsVistaOrGreater() \
|
||||||
IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), \
|
_glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_VISTA), \
|
||||||
LOBYTE(_WIN32_WINNT_VISTA), 0)
|
LOBYTE(_WIN32_WINNT_VISTA), 0)
|
||||||
#define IsWindows7OrGreater() \
|
#define IsWindows7OrGreater() \
|
||||||
IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), \
|
_glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WIN7), \
|
||||||
LOBYTE(_WIN32_WINNT_WIN7), 0)
|
LOBYTE(_WIN32_WINNT_WIN7), 0)
|
||||||
#define IsWindows8OrGreater() \
|
#define IsWindows8OrGreater() \
|
||||||
IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), \
|
_glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WIN8), \
|
||||||
LOBYTE(_WIN32_WINNT_WIN8), 0)
|
LOBYTE(_WIN32_WINNT_WIN8), 0)
|
||||||
#define IsWindows8Point1OrGreater() \
|
#define IsWindows8Point1OrGreater() \
|
||||||
IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINBLUE), \
|
_glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WINBLUE), \
|
||||||
LOBYTE(_WIN32_WINNT_WINBLUE), 0)
|
LOBYTE(_WIN32_WINNT_WINBLUE), 0)
|
||||||
|
|
||||||
|
#define _glfwIsWindows10AnniversaryUpdateOrGreaterWin32() \
|
||||||
|
_glfwIsWindows10BuildOrGreaterWin32(14393)
|
||||||
|
#define _glfwIsWindows10CreatorsUpdateOrGreaterWin32() \
|
||||||
|
_glfwIsWindows10BuildOrGreaterWin32(15063)
|
||||||
|
|
||||||
// HACK: Define macros that some xinput.h variants don't
|
// HACK: Define macros that some xinput.h variants don't
|
||||||
#ifndef XINPUT_CAPS_WIRELESS
|
#ifndef XINPUT_CAPS_WIRELESS
|
||||||
@@ -209,8 +224,16 @@ typedef HRESULT (WINAPI * PFN_DirectInput8Create)(HINSTANCE,DWORD,REFIID,LPVOID*
|
|||||||
// user32.dll function pointer typedefs
|
// user32.dll function pointer typedefs
|
||||||
typedef BOOL (WINAPI * PFN_SetProcessDPIAware)(void);
|
typedef BOOL (WINAPI * PFN_SetProcessDPIAware)(void);
|
||||||
typedef BOOL (WINAPI * PFN_ChangeWindowMessageFilterEx)(HWND,UINT,DWORD,CHANGEFILTERSTRUCT*);
|
typedef BOOL (WINAPI * PFN_ChangeWindowMessageFilterEx)(HWND,UINT,DWORD,CHANGEFILTERSTRUCT*);
|
||||||
|
typedef BOOL (WINAPI * PFN_EnableNonClientDpiScaling)(HWND);
|
||||||
|
typedef BOOL (WINAPI * PFN_SetProcessDpiAwarenessContext)(DPI_AWARENESS_CONTEXT);
|
||||||
|
typedef UINT (WINAPI * PFN_GetDpiForWindow)(HWND);
|
||||||
|
typedef BOOL (WINAPI * PFN_AdjustWindowRectExForDpi)(LPRECT,DWORD,BOOL,DWORD,UINT);
|
||||||
#define SetProcessDPIAware _glfw.win32.user32.SetProcessDPIAware_
|
#define SetProcessDPIAware _glfw.win32.user32.SetProcessDPIAware_
|
||||||
#define ChangeWindowMessageFilterEx _glfw.win32.user32.ChangeWindowMessageFilterEx_
|
#define ChangeWindowMessageFilterEx _glfw.win32.user32.ChangeWindowMessageFilterEx_
|
||||||
|
#define EnableNonClientDpiScaling _glfw.win32.user32.EnableNonClientDpiScaling_
|
||||||
|
#define SetProcessDpiAwarenessContext _glfw.win32.user32.SetProcessDpiAwarenessContext_
|
||||||
|
#define GetDpiForWindow _glfw.win32.user32.GetDpiForWindow_
|
||||||
|
#define AdjustWindowRectExForDpi _glfw.win32.user32.AdjustWindowRectExForDpi_
|
||||||
|
|
||||||
// dwmapi.dll function pointer typedefs
|
// dwmapi.dll function pointer typedefs
|
||||||
typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*);
|
typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*);
|
||||||
@@ -226,6 +249,10 @@ typedef HRESULT (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,
|
|||||||
#define SetProcessDpiAwareness _glfw.win32.shcore.SetProcessDpiAwareness_
|
#define SetProcessDpiAwareness _glfw.win32.shcore.SetProcessDpiAwareness_
|
||||||
#define GetDpiForMonitor _glfw.win32.shcore.GetDpiForMonitor_
|
#define GetDpiForMonitor _glfw.win32.shcore.GetDpiForMonitor_
|
||||||
|
|
||||||
|
// ntdll.dll function pointer typedefs
|
||||||
|
typedef LONG (WINAPI * PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW*,ULONG,ULONGLONG);
|
||||||
|
#define RtlVerifyVersionInfo _glfw.win32.ntdll.RtlVerifyVersionInfo_
|
||||||
|
|
||||||
typedef VkFlags VkWin32SurfaceCreateFlagsKHR;
|
typedef VkFlags VkWin32SurfaceCreateFlagsKHR;
|
||||||
|
|
||||||
typedef struct VkWin32SurfaceCreateInfoKHR
|
typedef struct VkWin32SurfaceCreateInfoKHR
|
||||||
@@ -279,6 +306,7 @@ typedef struct _GLFWwindowWin32
|
|||||||
GLFWbool maximized;
|
GLFWbool maximized;
|
||||||
// Whether to enable framebuffer transparency on DWM
|
// Whether to enable framebuffer transparency on DWM
|
||||||
GLFWbool transparent;
|
GLFWbool transparent;
|
||||||
|
GLFWbool scaleToMonitor;
|
||||||
|
|
||||||
// The last received cursor position, regardless of source
|
// The last received cursor position, regardless of source
|
||||||
int lastCursorPosX, lastCursorPosY;
|
int lastCursorPosX, lastCursorPosY;
|
||||||
@@ -326,6 +354,10 @@ typedef struct _GLFWlibraryWin32
|
|||||||
HINSTANCE instance;
|
HINSTANCE instance;
|
||||||
PFN_SetProcessDPIAware SetProcessDPIAware_;
|
PFN_SetProcessDPIAware SetProcessDPIAware_;
|
||||||
PFN_ChangeWindowMessageFilterEx ChangeWindowMessageFilterEx_;
|
PFN_ChangeWindowMessageFilterEx ChangeWindowMessageFilterEx_;
|
||||||
|
PFN_EnableNonClientDpiScaling EnableNonClientDpiScaling_;
|
||||||
|
PFN_SetProcessDpiAwarenessContext SetProcessDpiAwarenessContext_;
|
||||||
|
PFN_GetDpiForWindow GetDpiForWindow_;
|
||||||
|
PFN_AdjustWindowRectExForDpi AdjustWindowRectExForDpi_;
|
||||||
} user32;
|
} user32;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@@ -341,6 +373,11 @@ typedef struct _GLFWlibraryWin32
|
|||||||
PFN_GetDpiForMonitor GetDpiForMonitor_;
|
PFN_GetDpiForMonitor GetDpiForMonitor_;
|
||||||
} shcore;
|
} shcore;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
HINSTANCE instance;
|
||||||
|
PFN_RtlVerifyVersionInfo RtlVerifyVersionInfo_;
|
||||||
|
} ntdll;
|
||||||
|
|
||||||
} _GLFWlibraryWin32;
|
} _GLFWlibraryWin32;
|
||||||
|
|
||||||
// Win32-specific per-monitor data
|
// Win32-specific per-monitor data
|
||||||
@@ -396,10 +433,11 @@ typedef struct _GLFWmutexWin32
|
|||||||
|
|
||||||
GLFWbool _glfwRegisterWindowClassWin32(void);
|
GLFWbool _glfwRegisterWindowClassWin32(void);
|
||||||
void _glfwUnregisterWindowClassWin32(void);
|
void _glfwUnregisterWindowClassWin32(void);
|
||||||
GLFWbool _glfwIsCompositionEnabledWin32(void);
|
|
||||||
|
|
||||||
WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source);
|
WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source);
|
||||||
char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source);
|
char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source);
|
||||||
|
BOOL _glfwIsWindowsVersionOrGreaterWin32(WORD major, WORD minor, WORD sp);
|
||||||
|
BOOL _glfwIsWindows10BuildOrGreaterWin32(WORD build);
|
||||||
void _glfwInputErrorWin32(int error, const char* description);
|
void _glfwInputErrorWin32(int error, const char* description);
|
||||||
void _glfwUpdateKeyNamesWin32(void);
|
void _glfwUpdateKeyNamesWin32(void);
|
||||||
|
|
||||||
|
|||||||
265
glfw/win32_window.c
vendored
265
glfw/win32_window.c
vendored
@@ -186,14 +186,20 @@ static HICON createIcon(const GLFWimage* image,
|
|||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translate client window size to full window size according to styles
|
// Translate client window size to full window size according to styles and DPI
|
||||||
//
|
//
|
||||||
static void getFullWindowSize(DWORD style, DWORD exStyle,
|
static void getFullWindowSize(DWORD style, DWORD exStyle,
|
||||||
int clientWidth, int clientHeight,
|
int clientWidth, int clientHeight,
|
||||||
int* fullWidth, int* fullHeight)
|
int* fullWidth, int* fullHeight,
|
||||||
|
UINT dpi)
|
||||||
{
|
{
|
||||||
RECT rect = { 0, 0, clientWidth, clientHeight };
|
RECT rect = { 0, 0, clientWidth, clientHeight };
|
||||||
AdjustWindowRectEx(&rect, style, FALSE, exStyle);
|
|
||||||
|
if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
|
||||||
|
AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, dpi);
|
||||||
|
else
|
||||||
|
AdjustWindowRectEx(&rect, style, FALSE, exStyle);
|
||||||
|
|
||||||
*fullWidth = rect.right - rect.left;
|
*fullWidth = rect.right - rect.left;
|
||||||
*fullHeight = rect.bottom - rect.top;
|
*fullHeight = rect.bottom - rect.top;
|
||||||
}
|
}
|
||||||
@@ -206,7 +212,8 @@ static void applyAspectRatio(_GLFWwindow* window, int edge, RECT* area)
|
|||||||
const float ratio = (float) window->numer / (float) window->denom;
|
const float ratio = (float) window->numer / (float) window->denom;
|
||||||
|
|
||||||
getFullWindowSize(getWindowStyle(window), getWindowExStyle(window),
|
getFullWindowSize(getWindowStyle(window), getWindowExStyle(window),
|
||||||
0, 0, &xoff, &yoff);
|
0, 0, &xoff, &yoff,
|
||||||
|
GetDpiForWindow(window->win32.handle));
|
||||||
|
|
||||||
if (edge == WMSZ_LEFT || edge == WMSZ_BOTTOMLEFT ||
|
if (edge == WMSZ_LEFT || edge == WMSZ_BOTTOMLEFT ||
|
||||||
edge == WMSZ_RIGHT || edge == WMSZ_BOTTOMRIGHT)
|
edge == WMSZ_RIGHT || edge == WMSZ_BOTTOMRIGHT)
|
||||||
@@ -337,7 +344,16 @@ static void updateWindowStyles(const _GLFWwindow* window)
|
|||||||
style |= getWindowStyle(window);
|
style |= getWindowStyle(window);
|
||||||
|
|
||||||
GetClientRect(window->win32.handle, &rect);
|
GetClientRect(window->win32.handle, &rect);
|
||||||
AdjustWindowRectEx(&rect, style, FALSE, getWindowExStyle(window));
|
|
||||||
|
if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
|
||||||
|
{
|
||||||
|
AdjustWindowRectExForDpi(&rect, style, FALSE,
|
||||||
|
getWindowExStyle(window),
|
||||||
|
GetDpiForWindow(window->win32.handle));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
AdjustWindowRectEx(&rect, style, FALSE, getWindowExStyle(window));
|
||||||
|
|
||||||
ClientToScreen(window->win32.handle, (POINT*) &rect.left);
|
ClientToScreen(window->win32.handle, (POINT*) &rect.left);
|
||||||
ClientToScreen(window->win32.handle, (POINT*) &rect.right);
|
ClientToScreen(window->win32.handle, (POINT*) &rect.right);
|
||||||
SetWindowLongW(window->win32.handle, GWL_STYLE, style);
|
SetWindowLongW(window->win32.handle, GWL_STYLE, style);
|
||||||
@@ -351,10 +367,12 @@ static void updateWindowStyles(const _GLFWwindow* window)
|
|||||||
//
|
//
|
||||||
static void updateFramebufferTransparency(const _GLFWwindow* window)
|
static void updateFramebufferTransparency(const _GLFWwindow* window)
|
||||||
{
|
{
|
||||||
|
BOOL enabled;
|
||||||
|
|
||||||
if (!IsWindowsVistaOrGreater())
|
if (!IsWindowsVistaOrGreater())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_glfwIsCompositionEnabledWin32())
|
if (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled)
|
||||||
{
|
{
|
||||||
HRGN region = CreateRectRgn(0, 0, -1, -1);
|
HRGN region = CreateRectRgn(0, 0, -1, -1);
|
||||||
DWM_BLURBEHIND bb = {0};
|
DWM_BLURBEHIND bb = {0};
|
||||||
@@ -396,29 +414,6 @@ static void updateFramebufferTransparency(const _GLFWwindow* window)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translates a GLFW standard cursor to a resource ID
|
|
||||||
//
|
|
||||||
static LPWSTR translateCursorShape(int shape)
|
|
||||||
{
|
|
||||||
switch (shape)
|
|
||||||
{
|
|
||||||
case GLFW_ARROW_CURSOR:
|
|
||||||
return IDC_ARROW;
|
|
||||||
case GLFW_IBEAM_CURSOR:
|
|
||||||
return IDC_IBEAM;
|
|
||||||
case GLFW_CROSSHAIR_CURSOR:
|
|
||||||
return IDC_CROSS;
|
|
||||||
case GLFW_HAND_CURSOR:
|
|
||||||
return IDC_HAND;
|
|
||||||
case GLFW_HRESIZE_CURSOR:
|
|
||||||
return IDC_SIZEWE;
|
|
||||||
case GLFW_VRESIZE_CURSOR:
|
|
||||||
return IDC_SIZENS;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieves and translates modifier keys
|
// Retrieves and translates modifier keys
|
||||||
//
|
//
|
||||||
static int getKeyMods(void)
|
static int getKeyMods(void)
|
||||||
@@ -578,9 +573,18 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
|||||||
if (!window)
|
if (!window)
|
||||||
{
|
{
|
||||||
// This is the message handling for the hidden helper window
|
// This is the message handling for the hidden helper window
|
||||||
|
// and for a regular window during its initial creation
|
||||||
|
|
||||||
switch (uMsg)
|
switch (uMsg)
|
||||||
{
|
{
|
||||||
|
case WM_NCCREATE:
|
||||||
|
{
|
||||||
|
if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
|
||||||
|
EnableNonClientDpiScaling(hWnd);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case WM_DISPLAYCHANGE:
|
case WM_DISPLAYCHANGE:
|
||||||
_glfwPollMonitorsWin32();
|
_glfwPollMonitorsWin32();
|
||||||
break;
|
break;
|
||||||
@@ -1003,7 +1007,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
getFullWindowSize(getWindowStyle(window), getWindowExStyle(window),
|
getFullWindowSize(getWindowStyle(window), getWindowExStyle(window),
|
||||||
0, 0, &xoff, &yoff);
|
0, 0, &xoff, &yoff,
|
||||||
|
GetDpiForWindow(window->win32.handle));
|
||||||
|
|
||||||
if (window->minwidth != GLFW_DONT_CARE &&
|
if (window->minwidth != GLFW_DONT_CARE &&
|
||||||
window->minheight != GLFW_DONT_CARE)
|
window->minheight != GLFW_DONT_CARE)
|
||||||
@@ -1056,10 +1061,52 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case WM_GETDPISCALEDSIZE:
|
||||||
|
{
|
||||||
|
if (window->win32.scaleToMonitor)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Adjust the window size to keep the client area size constant
|
||||||
|
if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32())
|
||||||
|
{
|
||||||
|
RECT source = {0}, target = {0};
|
||||||
|
SIZE* size = (SIZE*) lParam;
|
||||||
|
|
||||||
|
AdjustWindowRectExForDpi(&source, getWindowStyle(window),
|
||||||
|
FALSE, getWindowExStyle(window),
|
||||||
|
GetDpiForWindow(window->win32.handle));
|
||||||
|
AdjustWindowRectExForDpi(&target, getWindowStyle(window),
|
||||||
|
FALSE, getWindowExStyle(window),
|
||||||
|
LOWORD(wParam));
|
||||||
|
|
||||||
|
size->cx += (target.right - target.left) -
|
||||||
|
(source.right - source.left);
|
||||||
|
size->cy += (target.bottom - target.top) -
|
||||||
|
(source.bottom - source.top);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case WM_DPICHANGED:
|
case WM_DPICHANGED:
|
||||||
{
|
{
|
||||||
const float xscale = HIWORD(wParam) / 96.f;
|
const float xscale = HIWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI;
|
||||||
const float yscale = LOWORD(wParam) / 96.f;
|
const float yscale = LOWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI;
|
||||||
|
|
||||||
|
// Only apply the suggested size if the OS is new enough to have
|
||||||
|
// sent a WM_GETDPISCALEDSIZE before this
|
||||||
|
if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32())
|
||||||
|
{
|
||||||
|
RECT* suggested = (RECT*) lParam;
|
||||||
|
SetWindowPos(window->win32.handle, HWND_TOP,
|
||||||
|
suggested->left,
|
||||||
|
suggested->top,
|
||||||
|
suggested->right - suggested->left,
|
||||||
|
suggested->bottom - suggested->top,
|
||||||
|
SWP_NOACTIVATE | SWP_NOZORDER);
|
||||||
|
}
|
||||||
|
|
||||||
_glfwInputWindowContentScale(window, xscale, yscale);
|
_glfwInputWindowContentScale(window, xscale, yscale);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1146,7 +1193,8 @@ static int createNativeWindow(_GLFWwindow* window,
|
|||||||
|
|
||||||
getFullWindowSize(style, exStyle,
|
getFullWindowSize(style, exStyle,
|
||||||
wndconfig->width, wndconfig->height,
|
wndconfig->width, wndconfig->height,
|
||||||
&fullWidth, &fullHeight);
|
&fullWidth, &fullHeight,
|
||||||
|
USER_DEFAULT_SCREEN_DPI);
|
||||||
}
|
}
|
||||||
|
|
||||||
wideTitle = _glfwCreateWideStringFromUTF8Win32(wndconfig->title);
|
wideTitle = _glfwCreateWideStringFromUTF8Win32(wndconfig->title);
|
||||||
@@ -1185,6 +1233,40 @@ static int createNativeWindow(_GLFWwindow* window,
|
|||||||
WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL);
|
WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window->win32.scaleToMonitor = wndconfig->scaleToMonitor;
|
||||||
|
|
||||||
|
// Adjust window size to account for DPI scaling of the window frame and
|
||||||
|
// optionally DPI scaling of the client area
|
||||||
|
// This cannot be done until we know what monitor it was placed on
|
||||||
|
if (!window->monitor)
|
||||||
|
{
|
||||||
|
RECT rect = { 0, 0, wndconfig->width, wndconfig->height };
|
||||||
|
|
||||||
|
if (wndconfig->scaleToMonitor)
|
||||||
|
{
|
||||||
|
float xscale, yscale;
|
||||||
|
_glfwPlatformGetWindowContentScale(window, &xscale, &yscale);
|
||||||
|
rect.right = (int) (rect.right * xscale);
|
||||||
|
rect.bottom = (int) (rect.bottom * yscale);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientToScreen(window->win32.handle, (POINT*) &rect.left);
|
||||||
|
ClientToScreen(window->win32.handle, (POINT*) &rect.right);
|
||||||
|
|
||||||
|
if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
|
||||||
|
{
|
||||||
|
AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle,
|
||||||
|
GetDpiForWindow(window->win32.handle));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
AdjustWindowRectEx(&rect, style, FALSE, exStyle);
|
||||||
|
|
||||||
|
SetWindowPos(window->win32.handle, NULL,
|
||||||
|
rect.left, rect.top,
|
||||||
|
rect.right - rect.left, rect.bottom - rect.top,
|
||||||
|
SWP_NOACTIVATE | SWP_NOZORDER);
|
||||||
|
}
|
||||||
|
|
||||||
DragAcceptFiles(window->win32.handle, TRUE);
|
DragAcceptFiles(window->win32.handle, TRUE);
|
||||||
|
|
||||||
if (fbconfig->transparent)
|
if (fbconfig->transparent)
|
||||||
@@ -1244,20 +1326,6 @@ void _glfwUnregisterWindowClassWin32(void)
|
|||||||
UnregisterClassW(_GLFW_WNDCLASSNAME, GetModuleHandleW(NULL));
|
UnregisterClassW(_GLFW_WNDCLASSNAME, GetModuleHandleW(NULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns whether desktop compositing is enabled
|
|
||||||
//
|
|
||||||
GLFWbool _glfwIsCompositionEnabledWin32(void)
|
|
||||||
{
|
|
||||||
if (IsWindowsVistaOrGreater())
|
|
||||||
{
|
|
||||||
BOOL enabled;
|
|
||||||
if (SUCCEEDED(DwmIsCompositionEnabled(&enabled)))
|
|
||||||
return enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
////// GLFW platform API //////
|
////// GLFW platform API //////
|
||||||
@@ -1395,8 +1463,19 @@ void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
|
|||||||
void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)
|
void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)
|
||||||
{
|
{
|
||||||
RECT rect = { xpos, ypos, xpos, ypos };
|
RECT rect = { xpos, ypos, xpos, ypos };
|
||||||
AdjustWindowRectEx(&rect, getWindowStyle(window),
|
|
||||||
FALSE, getWindowExStyle(window));
|
if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
|
||||||
|
{
|
||||||
|
AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
|
||||||
|
FALSE, getWindowExStyle(window),
|
||||||
|
GetDpiForWindow(window->win32.handle));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AdjustWindowRectEx(&rect, getWindowStyle(window),
|
||||||
|
FALSE, getWindowExStyle(window));
|
||||||
|
}
|
||||||
|
|
||||||
SetWindowPos(window->win32.handle, NULL, rect.left, rect.top, 0, 0,
|
SetWindowPos(window->win32.handle, NULL, rect.left, rect.top, 0, 0,
|
||||||
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);
|
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);
|
||||||
}
|
}
|
||||||
@@ -1425,8 +1504,19 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
RECT rect = { 0, 0, width, height };
|
RECT rect = { 0, 0, width, height };
|
||||||
AdjustWindowRectEx(&rect, getWindowStyle(window),
|
|
||||||
FALSE, getWindowExStyle(window));
|
if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
|
||||||
|
{
|
||||||
|
AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
|
||||||
|
FALSE, getWindowExStyle(window),
|
||||||
|
GetDpiForWindow(window->win32.handle));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AdjustWindowRectEx(&rect, getWindowStyle(window),
|
||||||
|
FALSE, getWindowExStyle(window));
|
||||||
|
}
|
||||||
|
|
||||||
SetWindowPos(window->win32.handle, HWND_TOP,
|
SetWindowPos(window->win32.handle, HWND_TOP,
|
||||||
0, 0, rect.right - rect.left, rect.bottom - rect.top,
|
0, 0, rect.right - rect.left, rect.bottom - rect.top,
|
||||||
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER);
|
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER);
|
||||||
@@ -1481,8 +1571,18 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
|
|||||||
|
|
||||||
_glfwPlatformGetWindowSize(window, &width, &height);
|
_glfwPlatformGetWindowSize(window, &width, &height);
|
||||||
SetRect(&rect, 0, 0, width, height);
|
SetRect(&rect, 0, 0, width, height);
|
||||||
AdjustWindowRectEx(&rect, getWindowStyle(window),
|
|
||||||
FALSE, getWindowExStyle(window));
|
if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
|
||||||
|
{
|
||||||
|
AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
|
||||||
|
FALSE, getWindowExStyle(window),
|
||||||
|
GetDpiForWindow(window->win32.handle));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AdjustWindowRectEx(&rect, getWindowStyle(window),
|
||||||
|
FALSE, getWindowExStyle(window));
|
||||||
|
}
|
||||||
|
|
||||||
if (left)
|
if (left)
|
||||||
*left = -rect.left;
|
*left = -rect.left;
|
||||||
@@ -1563,8 +1663,19 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
RECT rect = { xpos, ypos, xpos + width, ypos + height };
|
RECT rect = { xpos, ypos, xpos + width, ypos + height };
|
||||||
AdjustWindowRectEx(&rect, getWindowStyle(window),
|
|
||||||
FALSE, getWindowExStyle(window));
|
if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
|
||||||
|
{
|
||||||
|
AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
|
||||||
|
FALSE, getWindowExStyle(window),
|
||||||
|
GetDpiForWindow(window->win32.handle));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AdjustWindowRectEx(&rect, getWindowStyle(window),
|
||||||
|
FALSE, getWindowExStyle(window));
|
||||||
|
}
|
||||||
|
|
||||||
SetWindowPos(window->win32.handle, HWND_TOP,
|
SetWindowPos(window->win32.handle, HWND_TOP,
|
||||||
rect.left, rect.top,
|
rect.left, rect.top,
|
||||||
rect.right - rect.left, rect.bottom - rect.top,
|
rect.right - rect.left, rect.bottom - rect.top,
|
||||||
@@ -1624,8 +1735,18 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
|
|||||||
else
|
else
|
||||||
after = HWND_NOTOPMOST;
|
after = HWND_NOTOPMOST;
|
||||||
|
|
||||||
AdjustWindowRectEx(&rect, getWindowStyle(window),
|
if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
|
||||||
FALSE, getWindowExStyle(window));
|
{
|
||||||
|
AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
|
||||||
|
FALSE, getWindowExStyle(window),
|
||||||
|
GetDpiForWindow(window->win32.handle));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AdjustWindowRectEx(&rect, getWindowStyle(window),
|
||||||
|
FALSE, getWindowExStyle(window));
|
||||||
|
}
|
||||||
|
|
||||||
SetWindowPos(window->win32.handle, after,
|
SetWindowPos(window->win32.handle, after,
|
||||||
rect.left, rect.top,
|
rect.left, rect.top,
|
||||||
rect.right - rect.left, rect.bottom - rect.top,
|
rect.right - rect.left, rect.bottom - rect.top,
|
||||||
@@ -1660,7 +1781,15 @@ int _glfwPlatformWindowHovered(_GLFWwindow* window)
|
|||||||
|
|
||||||
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
|
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
|
||||||
{
|
{
|
||||||
return window->win32.transparent && _glfwIsCompositionEnabledWin32();
|
BOOL enabled;
|
||||||
|
|
||||||
|
if (!window->win32.transparent)
|
||||||
|
return GLFW_FALSE;
|
||||||
|
|
||||||
|
if (!IsWindowsVistaOrGreater())
|
||||||
|
return GLFW_FALSE;
|
||||||
|
|
||||||
|
return SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
|
void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
|
||||||
@@ -1867,8 +1996,24 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
|||||||
|
|
||||||
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
|
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
|
||||||
{
|
{
|
||||||
cursor->win32.handle =
|
LPCWSTR name = NULL;
|
||||||
CopyCursor(LoadCursorW(NULL, translateCursorShape(shape)));
|
|
||||||
|
if (shape == GLFW_ARROW_CURSOR)
|
||||||
|
name = IDC_ARROW;
|
||||||
|
else if (shape == GLFW_IBEAM_CURSOR)
|
||||||
|
name = IDC_IBEAM;
|
||||||
|
else if (shape == GLFW_CROSSHAIR_CURSOR)
|
||||||
|
name = IDC_CROSS;
|
||||||
|
else if (shape == GLFW_HAND_CURSOR)
|
||||||
|
name = IDC_HAND;
|
||||||
|
else if (shape == GLFW_HRESIZE_CURSOR)
|
||||||
|
name = IDC_SIZEWE;
|
||||||
|
else if (shape == GLFW_VRESIZE_CURSOR)
|
||||||
|
name = IDC_SIZENS;
|
||||||
|
else
|
||||||
|
return GLFW_FALSE;
|
||||||
|
|
||||||
|
cursor->win32.handle = CopyCursor(LoadCursorW(NULL, name));
|
||||||
if (!cursor->win32.handle)
|
if (!cursor->win32.handle)
|
||||||
{
|
{
|
||||||
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
|
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
|
||||||
|
|||||||
7
glfw/window.c
vendored
7
glfw/window.c
vendored
@@ -393,6 +393,9 @@ GLFWAPI void glfwWindowHint(int hint, int value)
|
|||||||
case GLFW_COCOA_GRAPHICS_SWITCHING:
|
case GLFW_COCOA_GRAPHICS_SWITCHING:
|
||||||
_glfw.hints.context.nsgl.offline = value ? GLFW_TRUE : GLFW_FALSE;
|
_glfw.hints.context.nsgl.offline = value ? GLFW_TRUE : GLFW_FALSE;
|
||||||
return;
|
return;
|
||||||
|
case GLFW_SCALE_TO_MONITOR:
|
||||||
|
_glfw.hints.window.scaleToMonitor = value ? GLFW_TRUE : GLFW_FALSE;
|
||||||
|
return;
|
||||||
case GLFW_CENTER_CURSOR:
|
case GLFW_CENTER_CURSOR:
|
||||||
_glfw.hints.window.centerCursor = value ? GLFW_TRUE : GLFW_FALSE;
|
_glfw.hints.window.centerCursor = value ? GLFW_TRUE : GLFW_FALSE;
|
||||||
return;
|
return;
|
||||||
@@ -457,6 +460,10 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value)
|
|||||||
strncpy(_glfw.hints.window.x11.instanceName, value,
|
strncpy(_glfw.hints.window.x11.instanceName, value,
|
||||||
sizeof(_glfw.hints.window.x11.instanceName) - 1);
|
sizeof(_glfw.hints.window.x11.instanceName) - 1);
|
||||||
return;
|
return;
|
||||||
|
case GLFW_WAYLAND_APP_ID:
|
||||||
|
strncpy(_glfw.hints.window.wl.appId, value,
|
||||||
|
sizeof(_glfw.hints.window.wl.appId) - 1);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint string 0x%08X", hint);
|
_glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint string 0x%08X", hint);
|
||||||
|
|||||||
28
glfw/wl_init.c
vendored
28
glfw/wl_init.c
vendored
@@ -560,6 +560,9 @@ static void registryHandleGlobal(void* data,
|
|||||||
_glfw.wl.seatVersion);
|
_glfw.wl.seatVersion);
|
||||||
wl_seat_add_listener(_glfw.wl.seat, &seatListener, NULL);
|
wl_seat_add_listener(_glfw.wl.seat, &seatListener, NULL);
|
||||||
}
|
}
|
||||||
|
if (_glfw.wl.seat && _glfw.wl.dataDeviceManager && !_glfw.wl.dataDevice) {
|
||||||
|
_glfwSetupWaylandDataDevice();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (strcmp(interface, "xdg_wm_base") == 0)
|
else if (strcmp(interface, "xdg_wm_base") == 0)
|
||||||
{
|
{
|
||||||
@@ -593,6 +596,17 @@ static void registryHandleGlobal(void* data,
|
|||||||
&zwp_idle_inhibit_manager_v1_interface,
|
&zwp_idle_inhibit_manager_v1_interface,
|
||||||
1);
|
1);
|
||||||
}
|
}
|
||||||
|
else if (strcmp(interface, "wl_data_device_manager") == 0)
|
||||||
|
{
|
||||||
|
_glfw.wl.dataDeviceManager =
|
||||||
|
wl_registry_bind(registry, name,
|
||||||
|
&wl_data_device_manager_interface,
|
||||||
|
1);
|
||||||
|
if (_glfw.wl.seat && _glfw.wl.dataDeviceManager && !_glfw.wl.dataDevice) {
|
||||||
|
_glfwSetupWaylandDataDevice();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void registryHandleGlobalRemove(void *data,
|
static void registryHandleGlobalRemove(void *data,
|
||||||
@@ -761,6 +775,17 @@ void _glfwPlatformTerminate(void)
|
|||||||
zwp_pointer_constraints_v1_destroy(_glfw.wl.pointerConstraints);
|
zwp_pointer_constraints_v1_destroy(_glfw.wl.pointerConstraints);
|
||||||
if (_glfw.wl.idleInhibitManager)
|
if (_glfw.wl.idleInhibitManager)
|
||||||
zwp_idle_inhibit_manager_v1_destroy(_glfw.wl.idleInhibitManager);
|
zwp_idle_inhibit_manager_v1_destroy(_glfw.wl.idleInhibitManager);
|
||||||
|
if (_glfw.wl.dataSourceForClipboard)
|
||||||
|
wl_data_source_destroy(_glfw.wl.dataSourceForClipboard);
|
||||||
|
for (size_t doi=0; doi < arraysz(_glfw.wl.dataOffers); doi++) {
|
||||||
|
if (_glfw.wl.dataOffers[doi].id) {
|
||||||
|
wl_data_offer_destroy(_glfw.wl.dataOffers[doi].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_glfw.wl.dataDevice)
|
||||||
|
wl_data_device_destroy(_glfw.wl.dataDevice);
|
||||||
|
if (_glfw.wl.dataDeviceManager)
|
||||||
|
wl_data_device_manager_destroy(_glfw.wl.dataDeviceManager);
|
||||||
if (_glfw.wl.registry)
|
if (_glfw.wl.registry)
|
||||||
wl_registry_destroy(_glfw.wl.registry);
|
wl_registry_destroy(_glfw.wl.registry);
|
||||||
if (_glfw.wl.display)
|
if (_glfw.wl.display)
|
||||||
@@ -769,6 +794,9 @@ void _glfwPlatformTerminate(void)
|
|||||||
wl_display_disconnect(_glfw.wl.display);
|
wl_display_disconnect(_glfw.wl.display);
|
||||||
}
|
}
|
||||||
closeFds(_glfw.wl.eventLoopData.wakeupFds, sizeof(_glfw.wl.eventLoopData.wakeupFds)/sizeof(_glfw.wl.eventLoopData.wakeupFds[0]));
|
closeFds(_glfw.wl.eventLoopData.wakeupFds, sizeof(_glfw.wl.eventLoopData.wakeupFds)/sizeof(_glfw.wl.eventLoopData.wakeupFds[0]));
|
||||||
|
free(_glfw.wl.clipboardString); _glfw.wl.clipboardString = NULL;
|
||||||
|
free(_glfw.wl.clipboardSourceString); _glfw.wl.clipboardSourceString = NULL;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* _glfwPlatformGetVersionString(void)
|
const char* _glfwPlatformGetVersionString(void)
|
||||||
|
|||||||
24
glfw/wl_platform.h
vendored
24
glfw/wl_platform.h
vendored
@@ -149,6 +149,7 @@ typedef struct _GLFWwindowWayland
|
|||||||
double cursorPosX, cursorPosY;
|
double cursorPosX, cursorPosY;
|
||||||
|
|
||||||
char* title;
|
char* title;
|
||||||
|
char appId[256];
|
||||||
|
|
||||||
// We need to track the monitors the window spans on to calculate the
|
// We need to track the monitors the window spans on to calculate the
|
||||||
// optimal scaling factor.
|
// optimal scaling factor.
|
||||||
@@ -175,6 +176,19 @@ typedef struct _GLFWwindowWayland
|
|||||||
|
|
||||||
} _GLFWwindowWayland;
|
} _GLFWwindowWayland;
|
||||||
|
|
||||||
|
typedef struct _GLFWWaylandDataOffer
|
||||||
|
{
|
||||||
|
struct wl_data_offer *id;
|
||||||
|
const char *mime;
|
||||||
|
int offer_type;
|
||||||
|
size_t idx;
|
||||||
|
int is_self_offer;
|
||||||
|
int has_uri_list;
|
||||||
|
uint32_t source_actions;
|
||||||
|
uint32_t dnd_action;
|
||||||
|
struct wl_surface *surface;
|
||||||
|
} _GLFWWaylandDataOffer;
|
||||||
|
|
||||||
// Wayland-specific global data
|
// Wayland-specific global data
|
||||||
//
|
//
|
||||||
typedef struct _GLFWlibraryWayland
|
typedef struct _GLFWlibraryWayland
|
||||||
@@ -193,6 +207,9 @@ typedef struct _GLFWlibraryWayland
|
|||||||
struct zwp_relative_pointer_manager_v1* relativePointerManager;
|
struct zwp_relative_pointer_manager_v1* relativePointerManager;
|
||||||
struct zwp_pointer_constraints_v1* pointerConstraints;
|
struct zwp_pointer_constraints_v1* pointerConstraints;
|
||||||
struct zwp_idle_inhibit_manager_v1* idleInhibitManager;
|
struct zwp_idle_inhibit_manager_v1* idleInhibitManager;
|
||||||
|
struct wl_data_device_manager* dataDeviceManager;
|
||||||
|
struct wl_data_device* dataDevice;
|
||||||
|
struct wl_data_source* dataSourceForClipboard;
|
||||||
|
|
||||||
int compositorVersion;
|
int compositorVersion;
|
||||||
int seatVersion;
|
int seatVersion;
|
||||||
@@ -232,7 +249,11 @@ typedef struct _GLFWlibraryWayland
|
|||||||
} egl;
|
} egl;
|
||||||
|
|
||||||
EventLoopData eventLoopData;
|
EventLoopData eventLoopData;
|
||||||
|
char* clipboardString;
|
||||||
|
char* clipboardSourceString;
|
||||||
|
struct wl_data_offer* clipboardSourceOffer;
|
||||||
|
size_t dataOffersCounter;
|
||||||
|
_GLFWWaylandDataOffer dataOffers[8];
|
||||||
} _GLFWlibraryWayland;
|
} _GLFWlibraryWayland;
|
||||||
|
|
||||||
// Wayland-specific per-monitor data
|
// Wayland-specific per-monitor data
|
||||||
@@ -261,3 +282,4 @@ typedef struct _GLFWcursorWayland
|
|||||||
|
|
||||||
|
|
||||||
void _glfwAddOutputWayland(uint32_t name, uint32_t version);
|
void _glfwAddOutputWayland(uint32_t name, uint32_t version);
|
||||||
|
void _glfwSetupWaylandDataDevice();
|
||||||
|
|||||||
339
glfw/wl_window.c
vendored
339
glfw/wl_window.c
vendored
@@ -37,6 +37,18 @@
|
|||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define URI_LIST_MIME "text/uri-list"
|
||||||
|
|
||||||
|
|
||||||
|
static const char*
|
||||||
|
clipboard_mime() {
|
||||||
|
static char buf[128] = {0};
|
||||||
|
if (buf[0] == 0) {
|
||||||
|
snprintf(buf, sizeof(buf), "application/glfw+clipboard-%d", getpid());
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
static void handlePing(void* data,
|
static void handlePing(void* data,
|
||||||
struct wl_shell_surface* shellSurface,
|
struct wl_shell_surface* shellSurface,
|
||||||
uint32_t serial)
|
uint32_t serial)
|
||||||
@@ -681,6 +693,8 @@ static GLFWbool createXdgSurface(_GLFWwindow* window)
|
|||||||
{
|
{
|
||||||
setIdleInhibitor(window, GLFW_FALSE);
|
setIdleInhibitor(window, GLFW_FALSE);
|
||||||
}
|
}
|
||||||
|
if (strlen(window->wl.appId))
|
||||||
|
xdg_toplevel_set_app_id(window->wl.xdg.toplevel, window->wl.appId);
|
||||||
|
|
||||||
wl_surface_commit(window->wl.surface);
|
wl_surface_commit(window->wl.surface);
|
||||||
wl_display_roundtrip(_glfw.wl.display);
|
wl_display_roundtrip(_glfw.wl.display);
|
||||||
@@ -757,6 +771,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
|
|||||||
{
|
{
|
||||||
window->wl.justCreated = GLFW_TRUE;
|
window->wl.justCreated = GLFW_TRUE;
|
||||||
window->wl.transparent = fbconfig->transparent;
|
window->wl.transparent = fbconfig->transparent;
|
||||||
|
strncpy(window->wl.appId, wndconfig->wl.appId, sizeof(window->wl.appId));
|
||||||
|
|
||||||
if (!createSurface(window, wndconfig))
|
if (!createSurface(window, wndconfig))
|
||||||
return GLFW_FALSE;
|
return GLFW_FALSE;
|
||||||
@@ -1435,18 +1450,330 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _glfwSendClipboardText(void *data, struct wl_data_source *data_source, const char *mime_type, int fd)
|
||||||
|
{
|
||||||
|
if (_glfw.wl.clipboardString) {
|
||||||
|
size_t len = strlen(_glfw.wl.clipboardString), pos = 0;
|
||||||
|
double start = glfwGetTime();
|
||||||
|
while (pos < len && glfwGetTime() - start < 2.0) {
|
||||||
|
ssize_t ret = write(fd, _glfw.wl.clipboardString + pos, len - pos);
|
||||||
|
if (ret < 0) {
|
||||||
|
if (errno == EAGAIN || errno == EINTR) continue;
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||||
|
"Wayland: Could not copy writing to destination fd failed with error: %s", strerror(errno));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ret > 0) {
|
||||||
|
start = glfwGetTime();
|
||||||
|
pos += ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* read_data_offer(struct wl_data_offer *data_offer, const char *mime) {
|
||||||
|
int pipefd[2];
|
||||||
|
if (pipe2(pipefd, O_CLOEXEC) != 0) return NULL;
|
||||||
|
wl_data_offer_receive(data_offer, mime, pipefd[1]);
|
||||||
|
close(pipefd[1]);
|
||||||
|
wl_display_flush(_glfw.wl.display);
|
||||||
|
size_t sz = 0, capacity = 0;
|
||||||
|
char *buf = NULL;
|
||||||
|
struct pollfd fds;
|
||||||
|
fds.fd = pipefd[0];
|
||||||
|
fds.events = POLLIN;
|
||||||
|
double start = glfwGetTime();
|
||||||
|
#define bail(...) { \
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR, __VA_ARGS__); \
|
||||||
|
free(buf); buf = NULL; \
|
||||||
|
close(pipefd[0]); \
|
||||||
|
return NULL; \
|
||||||
|
}
|
||||||
|
|
||||||
|
while (glfwGetTime() - start < 2) {
|
||||||
|
int ret = poll(&fds, 1, 2000);
|
||||||
|
if (ret == -1) {
|
||||||
|
if (errno == EINTR) continue;
|
||||||
|
bail("Wayland: Failed to poll clipboard data from pipe with error: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
if (!ret) {
|
||||||
|
bail("Wayland: Failed to read clipboard data from pipe (timed out)");
|
||||||
|
}
|
||||||
|
if (capacity <= sz || capacity - sz <= 64) {
|
||||||
|
capacity += 4096;
|
||||||
|
buf = realloc(buf, capacity);
|
||||||
|
if (!buf) {
|
||||||
|
bail("Wayland: Failed to allocate memory to read clipboard data");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret = read(pipefd[0], buf + sz, capacity - sz - 1);
|
||||||
|
if (ret == -1) {
|
||||||
|
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) continue;
|
||||||
|
bail("Wayland: Failed to read clipboard data from pipe with error: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
if (ret == 0) { close(pipefd[0]); buf[sz] = 0; return buf; }
|
||||||
|
sz += ret;
|
||||||
|
start = glfwGetTime();
|
||||||
|
}
|
||||||
|
bail("Wayland: Failed to read clipboard data from pipe (timed out)");
|
||||||
|
#undef bail
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* _glfwReceiveClipboardText(struct wl_data_offer *data_offer, const char *mime)
|
||||||
|
{
|
||||||
|
if (_glfw.wl.clipboardSourceOffer == data_offer && _glfw.wl.clipboardSourceString)
|
||||||
|
return _glfw.wl.clipboardSourceString;
|
||||||
|
free(_glfw.wl.clipboardSourceString);
|
||||||
|
_glfw.wl.clipboardSourceString = read_data_offer(data_offer, mime);
|
||||||
|
return _glfw.wl.clipboardSourceString;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void data_source_canceled(void *data, struct wl_data_source *wl_data_source) {
|
||||||
|
if (_glfw.wl.dataSourceForClipboard == wl_data_source)
|
||||||
|
_glfw.wl.dataSourceForClipboard = NULL;
|
||||||
|
wl_data_source_destroy(wl_data_source);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void data_source_target(void *data, struct wl_data_source *wl_data_source, const char* mime) {
|
||||||
|
}
|
||||||
|
|
||||||
|
const static struct wl_data_source_listener data_source_listener = {
|
||||||
|
.send = _glfwSendClipboardText,
|
||||||
|
.cancelled = data_source_canceled,
|
||||||
|
.target = data_source_target,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void prune_unclaimed_data_offers() {
|
||||||
|
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||||
|
if (_glfw.wl.dataOffers[i].id && !_glfw.wl.dataOffers[i].offer_type) {
|
||||||
|
wl_data_offer_destroy(_glfw.wl.dataOffers[i].id);
|
||||||
|
memset(_glfw.wl.dataOffers + i, 0, sizeof(_glfw.wl.dataOffers[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mark_selection_offer(void *data, struct wl_data_device *data_device, struct wl_data_offer *data_offer)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||||
|
if (_glfw.wl.dataOffers[i].id == data_offer) {
|
||||||
|
_glfw.wl.dataOffers[i].offer_type = 1;
|
||||||
|
} else if (_glfw.wl.dataOffers[i].offer_type == 1) {
|
||||||
|
_glfw.wl.dataOffers[i].offer_type = 0; // previous selection offer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prune_unclaimed_data_offers();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_offer_mimetype(void *data, struct wl_data_offer* id, const char *mime) {
|
||||||
|
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||||
|
if (_glfw.wl.dataOffers[i].id == id) {
|
||||||
|
if (strcmp(mime, "text/plain;charset=utf-8") == 0)
|
||||||
|
_glfw.wl.dataOffers[i].mime = "text/plain;charset=utf-8";
|
||||||
|
else if (!_glfw.wl.dataOffers[i].mime && strcmp(mime, "text/plain") == 0)
|
||||||
|
_glfw.wl.dataOffers[i].mime = "text/plain";
|
||||||
|
else if (strcmp(mime, clipboard_mime()) == 0)
|
||||||
|
_glfw.wl.dataOffers[i].is_self_offer = 1;
|
||||||
|
else if (strcmp(mime, URI_LIST_MIME) == 0)
|
||||||
|
_glfw.wl.dataOffers[i].has_uri_list = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void data_offer_source_actions(void *data, struct wl_data_offer* id, uint32_t actions) {
|
||||||
|
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||||
|
if (_glfw.wl.dataOffers[i].id == id) {
|
||||||
|
_glfw.wl.dataOffers[i].source_actions = actions;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void data_offer_action(void *data, struct wl_data_offer* id, uint32_t action) {
|
||||||
|
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||||
|
if (_glfw.wl.dataOffers[i].id == id) {
|
||||||
|
_glfw.wl.dataOffers[i].dnd_action = action;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const struct wl_data_offer_listener data_offer_listener = {
|
||||||
|
.offer = handle_offer_mimetype,
|
||||||
|
.source_actions = data_offer_source_actions,
|
||||||
|
.action = data_offer_action,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void handle_data_offer(void *data, struct wl_data_device *wl_data_device, struct wl_data_offer *id) {
|
||||||
|
size_t smallest_idx = SIZE_MAX, pos = 0;
|
||||||
|
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||||
|
if (_glfw.wl.dataOffers[i].idx && _glfw.wl.dataOffers[i].idx < smallest_idx) {
|
||||||
|
smallest_idx = _glfw.wl.dataOffers[i].idx;
|
||||||
|
pos = i;
|
||||||
|
}
|
||||||
|
if (_glfw.wl.dataOffers[i].id == NULL) {
|
||||||
|
_glfw.wl.dataOffers[i].id = id;
|
||||||
|
_glfw.wl.dataOffers[i].idx = ++_glfw.wl.dataOffersCounter;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_glfw.wl.dataOffers[pos].id) wl_data_offer_destroy(_glfw.wl.dataOffers[pos].id);
|
||||||
|
memset(_glfw.wl.dataOffers + pos, 0, sizeof(_glfw.wl.dataOffers[0]));
|
||||||
|
_glfw.wl.dataOffers[pos].id = id;
|
||||||
|
_glfw.wl.dataOffers[pos].idx = ++_glfw.wl.dataOffersCounter;
|
||||||
|
end:
|
||||||
|
wl_data_offer_add_listener(id, &data_offer_listener, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drag_enter(void *data, struct wl_data_device *wl_data_device, uint32_t serial, struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y, struct wl_data_offer *id) {
|
||||||
|
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||||
|
if (_glfw.wl.dataOffers[i].id == id) {
|
||||||
|
_glfw.wl.dataOffers[i].offer_type = 2;
|
||||||
|
_glfw.wl.dataOffers[i].surface = surface;
|
||||||
|
const char *mime = _glfw.wl.dataOffers[i].has_uri_list ? URI_LIST_MIME : NULL;
|
||||||
|
wl_data_offer_accept(id, serial, mime);
|
||||||
|
} else if (_glfw.wl.dataOffers[i].offer_type == 2) {
|
||||||
|
_glfw.wl.dataOffers[i].offer_type = 0; // previous drag offer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prune_unclaimed_data_offers();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drag_leave(void *data, struct wl_data_device *wl_data_device) {
|
||||||
|
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||||
|
if (_glfw.wl.dataOffers[i].offer_type == 2) {
|
||||||
|
wl_data_offer_destroy(_glfw.wl.dataOffers[i].id);
|
||||||
|
memset(_glfw.wl.dataOffers + i, 0, sizeof(_glfw.wl.dataOffers[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void drop(void *data, struct wl_data_device *wl_data_device) {
|
||||||
|
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||||
|
if (_glfw.wl.dataOffers[i].offer_type == 2) {
|
||||||
|
char *uri_list = read_data_offer(_glfw.wl.dataOffers[i].id, URI_LIST_MIME);
|
||||||
|
if (uri_list) {
|
||||||
|
wl_data_offer_finish(_glfw.wl.dataOffers[i].id);
|
||||||
|
int count;
|
||||||
|
char** paths = parseUriList(data, &count);
|
||||||
|
|
||||||
|
_GLFWwindow* window = _glfw.windowListHead;
|
||||||
|
while (window)
|
||||||
|
{
|
||||||
|
if (window->wl.surface == _glfw.wl.dataOffers[i].surface) {
|
||||||
|
_glfwInputDrop(window, count, (const char**) paths);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
window = window->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (int k = 0; k < count; k++)
|
||||||
|
free(paths[k]);
|
||||||
|
free(paths);
|
||||||
|
free(uri_list);
|
||||||
|
}
|
||||||
|
wl_data_offer_destroy(_glfw.wl.dataOffers[i].id);
|
||||||
|
memset(_glfw.wl.dataOffers + i, 0, sizeof(_glfw.wl.dataOffers[0]));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void motion(void *data, struct wl_data_device *wl_data_device, uint32_t time, wl_fixed_t x, wl_fixed_t y) {
|
||||||
|
}
|
||||||
|
|
||||||
|
const static struct wl_data_device_listener data_device_listener = {
|
||||||
|
.data_offer = handle_data_offer,
|
||||||
|
.selection = mark_selection_offer,
|
||||||
|
.enter = drag_enter,
|
||||||
|
.motion = motion,
|
||||||
|
.drop = drop,
|
||||||
|
.leave = drag_leave,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
copy_callback_done(void *data, struct wl_callback *callback, uint32_t serial) {
|
||||||
|
if (!_glfw.wl.dataDevice) return;
|
||||||
|
if (data == (void*)_glfw.wl.dataSourceForClipboard) {
|
||||||
|
wl_data_device_set_selection(_glfw.wl.dataDevice, data, serial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const static struct wl_callback_listener copy_callback_listener = {
|
||||||
|
.done = copy_callback_done
|
||||||
|
};
|
||||||
|
|
||||||
|
void _glfwSetupWaylandDataDevice() {
|
||||||
|
_glfw.wl.dataDevice = wl_data_device_manager_get_data_device(_glfw.wl.dataDeviceManager, _glfw.wl.seat);
|
||||||
|
if (_glfw.wl.dataDevice) wl_data_device_add_listener(_glfw.wl.dataDevice, &data_device_listener, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline GLFWbool _glfwEnsureDataDevice() {
|
||||||
|
if (!_glfw.wl.dataDeviceManager)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||||
|
"Wayland: Cannot use clipboard, data device manager is not ready");
|
||||||
|
return GLFW_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_glfw.wl.dataDevice)
|
||||||
|
{
|
||||||
|
if (!_glfw.wl.seat)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||||
|
"Wayland: Cannot use clipboard, seat is not ready");
|
||||||
|
return GLFW_FALSE;
|
||||||
|
}
|
||||||
|
if (!_glfw.wl.dataDevice)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||||
|
"Wayland: Cannot use clipboard, failed to create data device");
|
||||||
|
return GLFW_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return GLFW_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
void _glfwPlatformSetClipboardString(const char* string)
|
void _glfwPlatformSetClipboardString(const char* string)
|
||||||
{
|
{
|
||||||
// TODO
|
if (!_glfwEnsureDataDevice()) return;
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
free(_glfw.wl.clipboardString);
|
||||||
"Wayland: Clipboard setting not implemented yet");
|
_glfw.wl.clipboardString = _glfw_strdup(string);
|
||||||
|
if (_glfw.wl.dataSourceForClipboard)
|
||||||
|
wl_data_source_destroy(_glfw.wl.dataSourceForClipboard);
|
||||||
|
_glfw.wl.dataSourceForClipboard = wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager);
|
||||||
|
if (!_glfw.wl.dataSourceForClipboard)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||||
|
"Wayland: Cannot copy failed to create data source");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wl_data_source_add_listener(_glfw.wl.dataSourceForClipboard, &data_source_listener, NULL);
|
||||||
|
wl_data_source_offer(_glfw.wl.dataSourceForClipboard, clipboard_mime());
|
||||||
|
wl_data_source_offer(_glfw.wl.dataSourceForClipboard, "text/plain");
|
||||||
|
wl_data_source_offer(_glfw.wl.dataSourceForClipboard, "text/plain;charset=utf-8");
|
||||||
|
wl_data_source_offer(_glfw.wl.dataSourceForClipboard, "TEXT");
|
||||||
|
wl_data_source_offer(_glfw.wl.dataSourceForClipboard, "STRING");
|
||||||
|
wl_data_source_offer(_glfw.wl.dataSourceForClipboard, "UTF8_STRING");
|
||||||
|
struct wl_callback *callback = wl_display_sync(_glfw.wl.display);
|
||||||
|
wl_callback_add_listener(callback, ©_callback_listener, _glfw.wl.dataSourceForClipboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* _glfwPlatformGetClipboardString(void)
|
const char* _glfwPlatformGetClipboardString(void)
|
||||||
{
|
{
|
||||||
// TODO
|
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
if (_glfw.wl.dataOffers[i].id && _glfw.wl.dataOffers[i].mime && _glfw.wl.dataOffers[i].offer_type == 1) {
|
||||||
"Wayland: Clipboard getting not implemented yet");
|
if (_glfw.wl.dataOffers[i].is_self_offer) return _glfw.wl.clipboardString;
|
||||||
|
return _glfwReceiveClipboardText(_glfw.wl.dataOffers[i].id, _glfw.wl.dataOffers[i].mime);
|
||||||
|
}
|
||||||
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
129
glfw/x11_window.c
vendored
129
glfw/x11_window.c
vendored
@@ -161,29 +161,6 @@ static Bool isSelPropNewValueNotify(Display* display, XEvent* event, XPointer po
|
|||||||
event->xproperty.atom == notification->xselection.property;
|
event->xproperty.atom == notification->xselection.property;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translates a GLFW standard cursor to a font cursor shape
|
|
||||||
//
|
|
||||||
static int translateCursorShape(int shape)
|
|
||||||
{
|
|
||||||
switch (shape)
|
|
||||||
{
|
|
||||||
case GLFW_ARROW_CURSOR:
|
|
||||||
return XC_left_ptr;
|
|
||||||
case GLFW_IBEAM_CURSOR:
|
|
||||||
return XC_xterm;
|
|
||||||
case GLFW_CROSSHAIR_CURSOR:
|
|
||||||
return XC_crosshair;
|
|
||||||
case GLFW_HAND_CURSOR:
|
|
||||||
return XC_hand1;
|
|
||||||
case GLFW_HRESIZE_CURSOR:
|
|
||||||
return XC_sb_h_double_arrow;
|
|
||||||
case GLFW_VRESIZE_CURSOR:
|
|
||||||
return XC_sb_v_double_arrow;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Translates an X event modifier state mask
|
// Translates an X event modifier state mask
|
||||||
//
|
//
|
||||||
static int translateState(int state)
|
static int translateState(int state)
|
||||||
@@ -206,23 +183,6 @@ static int translateState(int state)
|
|||||||
return mods;
|
return mods;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the GLFW window corresponding to the specified X11 window
|
|
||||||
//
|
|
||||||
static _GLFWwindow* findWindowByHandle(Window handle)
|
|
||||||
{
|
|
||||||
_GLFWwindow* window;
|
|
||||||
|
|
||||||
if (XFindContext(_glfw.x11.display,
|
|
||||||
handle,
|
|
||||||
_glfw.x11.context,
|
|
||||||
(XPointer*) &window) != 0)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return window;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sends an EWMH or ICCCM event to the window manager
|
// Sends an EWMH or ICCCM event to the window manager
|
||||||
//
|
//
|
||||||
static void sendEventToWM(_GLFWwindow* window, Atom type,
|
static void sendEventToWM(_GLFWwindow* window, Atom type,
|
||||||
@@ -390,56 +350,6 @@ static void updateWindowMode(_GLFWwindow* window)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Splits and translates a text/uri-list into separate file paths
|
|
||||||
// NOTE: This function destroys the provided string
|
|
||||||
//
|
|
||||||
static char** parseUriList(char* text, int* count)
|
|
||||||
{
|
|
||||||
const char* prefix = "file://";
|
|
||||||
char** paths = NULL;
|
|
||||||
char* line;
|
|
||||||
|
|
||||||
*count = 0;
|
|
||||||
|
|
||||||
while ((line = strtok(text, "\r\n")))
|
|
||||||
{
|
|
||||||
text = NULL;
|
|
||||||
|
|
||||||
if (line[0] == '#')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (strncmp(line, prefix, strlen(prefix)) == 0)
|
|
||||||
{
|
|
||||||
line += strlen(prefix);
|
|
||||||
// TODO: Validate hostname
|
|
||||||
while (*line != '/')
|
|
||||||
line++;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*count)++;
|
|
||||||
|
|
||||||
char* path = calloc(strlen(line) + 1, 1);
|
|
||||||
paths = realloc(paths, *count * sizeof(char*));
|
|
||||||
paths[*count - 1] = path;
|
|
||||||
|
|
||||||
while (*line)
|
|
||||||
{
|
|
||||||
if (line[0] == '%' && line[1] && line[2])
|
|
||||||
{
|
|
||||||
const char digits[3] = { line[1], line[2], '\0' };
|
|
||||||
*path = strtol(digits, NULL, 16);
|
|
||||||
line += 2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
*path = *line;
|
|
||||||
|
|
||||||
path++;
|
|
||||||
line++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return paths;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode a Unicode code point to a UTF-8 stream
|
// Encode a Unicode code point to a UTF-8 stream
|
||||||
// Based on cutef8 by Jeff Bezanson (Public Domain)
|
// Based on cutef8 by Jeff Bezanson (Public Domain)
|
||||||
@@ -582,6 +492,15 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
|
|||||||
const _GLFWwndconfig* wndconfig,
|
const _GLFWwndconfig* wndconfig,
|
||||||
Visual* visual, int depth)
|
Visual* visual, int depth)
|
||||||
{
|
{
|
||||||
|
int width = wndconfig->width;
|
||||||
|
int height = wndconfig->height;
|
||||||
|
|
||||||
|
if (wndconfig->scaleToMonitor)
|
||||||
|
{
|
||||||
|
width *= _glfw.x11.contentScaleX;
|
||||||
|
height *= _glfw.x11.contentScaleY;
|
||||||
|
}
|
||||||
|
|
||||||
// Create a colormap based on the visual used by the current context
|
// Create a colormap based on the visual used by the current context
|
||||||
window->x11.colormap = XCreateColormap(_glfw.x11.display,
|
window->x11.colormap = XCreateColormap(_glfw.x11.display,
|
||||||
_glfw.x11.root,
|
_glfw.x11.root,
|
||||||
@@ -607,7 +526,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
|
|||||||
window->x11.handle = XCreateWindow(_glfw.x11.display,
|
window->x11.handle = XCreateWindow(_glfw.x11.display,
|
||||||
_glfw.x11.root,
|
_glfw.x11.root,
|
||||||
0, 0,
|
0, 0,
|
||||||
wndconfig->width, wndconfig->height,
|
width, height,
|
||||||
0, // Border width
|
0, // Border width
|
||||||
depth, // Color depth
|
depth, // Color depth
|
||||||
InputOutput,
|
InputOutput,
|
||||||
@@ -710,7 +629,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
|
|||||||
XFree(hints);
|
XFree(hints);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateNormalHints(window, wndconfig->width, wndconfig->height);
|
updateNormalHints(window, width, height);
|
||||||
|
|
||||||
// Set ICCCM WM_CLASS property
|
// Set ICCCM WM_CLASS property
|
||||||
{
|
{
|
||||||
@@ -1243,8 +1162,10 @@ static void processEvent(XEvent *event)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
window = findWindowByHandle(event->xany.window);
|
if (XFindContext(_glfw.x11.display,
|
||||||
if (window == NULL)
|
event->xany.window,
|
||||||
|
_glfw.x11.context,
|
||||||
|
(XPointer*) &window) != 0)
|
||||||
{
|
{
|
||||||
// This is an event for a window that has already been destroyed
|
// This is an event for a window that has already been destroyed
|
||||||
return;
|
return;
|
||||||
@@ -2680,8 +2601,24 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
|||||||
|
|
||||||
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
|
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
|
||||||
{
|
{
|
||||||
cursor->x11.handle = XCreateFontCursor(_glfw.x11.display,
|
int native = 0;
|
||||||
translateCursorShape(shape));
|
|
||||||
|
if (shape == GLFW_ARROW_CURSOR)
|
||||||
|
native = XC_left_ptr;
|
||||||
|
else if (shape == GLFW_IBEAM_CURSOR)
|
||||||
|
native = XC_xterm;
|
||||||
|
else if (shape == GLFW_CROSSHAIR_CURSOR)
|
||||||
|
native = XC_crosshair;
|
||||||
|
else if (shape == GLFW_HAND_CURSOR)
|
||||||
|
native = XC_hand1;
|
||||||
|
else if (shape == GLFW_HRESIZE_CURSOR)
|
||||||
|
native = XC_sb_h_double_arrow;
|
||||||
|
else if (shape == GLFW_VRESIZE_CURSOR)
|
||||||
|
native = XC_sb_v_double_arrow;
|
||||||
|
else
|
||||||
|
return GLFW_FALSE;
|
||||||
|
|
||||||
|
cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native);
|
||||||
if (!cursor->x11.handle)
|
if (!cursor->x11.handle)
|
||||||
{
|
{
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||||
|
|||||||
25
glfw/xkb_glfw.c
vendored
25
glfw/xkb_glfw.c
vendored
@@ -33,7 +33,7 @@
|
|||||||
#define debug(...) if (_glfw.hints.init.debugKeyboard) printf(__VA_ARGS__);
|
#define debug(...) if (_glfw.hints.init.debugKeyboard) printf(__VA_ARGS__);
|
||||||
|
|
||||||
|
|
||||||
#define map_key(key) { \
|
#define map_key(key) \
|
||||||
switch(key) { \
|
switch(key) { \
|
||||||
S(space, SPACE); \
|
S(space, SPACE); \
|
||||||
S(apostrophe, APOSTROPHE); \
|
S(apostrophe, APOSTROPHE); \
|
||||||
@@ -93,10 +93,6 @@
|
|||||||
D(A, Z, A, Z); \
|
D(A, Z, A, Z); \
|
||||||
R(F1, F25, F1, F25); \
|
R(F1, F25, F1, F25); \
|
||||||
R(KP_0, KP_9, KP_0, KP_9); \
|
R(KP_0, KP_9, KP_0, KP_9); \
|
||||||
default: \
|
|
||||||
break; \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
glfw_key_for_sym(xkb_keysym_t key) {
|
glfw_key_for_sym(xkb_keysym_t key) {
|
||||||
@@ -105,6 +101,13 @@ glfw_key_for_sym(xkb_keysym_t key) {
|
|||||||
#define R(s, e, gs, ...) case XKB_KEY_##s ... XKB_KEY_##e: return GLFW_KEY_##gs + key - XKB_KEY_##s
|
#define R(s, e, gs, ...) case XKB_KEY_##s ... XKB_KEY_##e: return GLFW_KEY_##gs + key - XKB_KEY_##s
|
||||||
#define D(s, e, gs, ...) R(s, e, gs)
|
#define D(s, e, gs, ...) R(s, e, gs)
|
||||||
map_key(key)
|
map_key(key)
|
||||||
|
S(KP_Up, UP);
|
||||||
|
S(KP_Down, DOWN);
|
||||||
|
S(KP_Left, LEFT);
|
||||||
|
S(KP_Right, RIGHT);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
return GLFW_KEY_UNKNOWN;
|
return GLFW_KEY_UNKNOWN;
|
||||||
#undef F
|
#undef F
|
||||||
#undef D
|
#undef D
|
||||||
@@ -119,6 +122,9 @@ glfw_xkb_sym_for_key(int key) {
|
|||||||
#define R(s, e, gs, ge) case GLFW_KEY_##gs ... GLFW_KEY_##ge: return XKB_KEY_##s + key - GLFW_KEY_##gs
|
#define R(s, e, gs, ge) case GLFW_KEY_##gs ... GLFW_KEY_##ge: return XKB_KEY_##s + key - GLFW_KEY_##gs
|
||||||
#define D(...)
|
#define D(...)
|
||||||
map_key(key)
|
map_key(key)
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
return GLFW_KEY_UNKNOWN;
|
return GLFW_KEY_UNKNOWN;
|
||||||
#undef F
|
#undef F
|
||||||
#undef D
|
#undef D
|
||||||
@@ -329,12 +335,14 @@ glfw_xkb_should_repeat(_GLFWXKBData *xkb, xkb_keycode_t scancode) {
|
|||||||
static KeyEvent key_event = {};
|
static KeyEvent key_event = {};
|
||||||
|
|
||||||
static inline xkb_keysym_t
|
static inline xkb_keysym_t
|
||||||
compose_symbol(struct xkb_compose_state *composeState, xkb_keysym_t sym) {
|
compose_symbol(struct xkb_compose_state *composeState, xkb_keysym_t sym, int *compose_completed) {
|
||||||
|
*compose_completed = 0;
|
||||||
if (sym == XKB_KEY_NoSymbol || !composeState) return sym;
|
if (sym == XKB_KEY_NoSymbol || !composeState) return sym;
|
||||||
if (xkb_compose_state_feed(composeState, sym) != XKB_COMPOSE_FEED_ACCEPTED) return sym;
|
if (xkb_compose_state_feed(composeState, sym) != XKB_COMPOSE_FEED_ACCEPTED) return sym;
|
||||||
switch (xkb_compose_state_get_status(composeState)) {
|
switch (xkb_compose_state_get_status(composeState)) {
|
||||||
case XKB_COMPOSE_COMPOSED:
|
case XKB_COMPOSE_COMPOSED:
|
||||||
xkb_compose_state_get_utf8(composeState, key_event.text, sizeof(key_event.text));
|
xkb_compose_state_get_utf8(composeState, key_event.text, sizeof(key_event.text));
|
||||||
|
*compose_completed = 1;
|
||||||
return xkb_compose_state_get_one_sym(composeState);
|
return xkb_compose_state_get_one_sym(composeState);
|
||||||
case XKB_COMPOSE_COMPOSING:
|
case XKB_COMPOSE_COMPOSING:
|
||||||
case XKB_COMPOSE_CANCELLED:
|
case XKB_COMPOSE_CANCELLED:
|
||||||
@@ -465,8 +473,9 @@ glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t
|
|||||||
debug("clean_sym: %s ", glfw_xkb_keysym_name(clean_syms[0]));
|
debug("clean_sym: %s ", glfw_xkb_keysym_name(clean_syms[0]));
|
||||||
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
|
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
|
||||||
const char *text_type = "composed_text";
|
const char *text_type = "composed_text";
|
||||||
glfw_sym = compose_symbol(sg->composeState, syms[0]);
|
int compose_completed;
|
||||||
if (glfw_sym == XKB_KEY_NoSymbol) {
|
glfw_sym = compose_symbol(sg->composeState, syms[0], &compose_completed);
|
||||||
|
if (glfw_sym == XKB_KEY_NoSymbol && !compose_completed) {
|
||||||
debug("compose not complete, ignoring.\n");
|
debug("compose not complete, ignoring.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ from .search import BadRegex, Search
|
|||||||
try:
|
try:
|
||||||
from .highlight import initialize_highlighter, highlight_collection
|
from .highlight import initialize_highlighter, highlight_collection
|
||||||
except ImportError:
|
except ImportError:
|
||||||
initialize_highlighter = None
|
initialize_highlighter = highlight_collection = None
|
||||||
|
|
||||||
|
|
||||||
INITIALIZING, COLLECTED, DIFFED, COMMAND, MESSAGE = range(5)
|
INITIALIZING, COLLECTED, DIFFED, COMMAND, MESSAGE = range(5)
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ Horizontal alignment for the displayed image.
|
|||||||
--place
|
--place
|
||||||
Choose where on the screen to display the image. The image will
|
Choose where on the screen to display the image. The image will
|
||||||
be scaled to fit into the specified rectangle. The syntax for
|
be scaled to fit into the specified rectangle. The syntax for
|
||||||
specifying rectanges is <:italic:`width`>x<:italic:`height`>@<:italic:`left`>x<:italic:`top`>. All measurements
|
specifying rectangles is <:italic:`width`>x<:italic:`height`>@<:italic:`left`>x<:italic:`top`>.
|
||||||
are in cells (i.e. cursor positions) with the origin :italic:`(0, 0)` at
|
All measurements are in cells (i.e. cursor positions) with the
|
||||||
the top-left corner of the screen.
|
origin :italic:`(0, 0)` at the top-left corner of the screen.
|
||||||
|
|
||||||
|
|
||||||
--scale-up
|
--scale-up
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class Resize(Handler):
|
|||||||
cmdline = [resize_window.name, '--self', '--increment={}'.format(increment), '--axis=' + axis]
|
cmdline = [resize_window.name, '--self', '--increment={}'.format(increment), '--axis=' + axis]
|
||||||
opts, items = parse_subcommand_cli(resize_window, cmdline)
|
opts, items = parse_subcommand_cli(resize_window, cmdline)
|
||||||
payload = resize_window(global_opts, opts, items)
|
payload = resize_window(global_opts, opts, items)
|
||||||
send = {'cmd': resize_window.name, 'version': version, 'payload': payload}
|
send = {'cmd': resize_window.name, 'version': version, 'payload': payload, 'no_response': False}
|
||||||
self.write(encode_send(send))
|
self.write(encode_send(send))
|
||||||
|
|
||||||
def on_kitty_cmd_response(self, response):
|
def on_kitty_cmd_response(self, response):
|
||||||
|
|||||||
@@ -103,6 +103,8 @@ class Boss:
|
|||||||
if new_os_window_trigger is not None:
|
if new_os_window_trigger is not None:
|
||||||
self.keymap.pop(new_os_window_trigger, None)
|
self.keymap.pop(new_os_window_trigger, None)
|
||||||
self.add_os_window(startup_session, os_window_id=os_window_id)
|
self.add_os_window(startup_session, os_window_id=os_window_id)
|
||||||
|
if args.start_in_fullscreen:
|
||||||
|
self.toggle_fullscreen()
|
||||||
|
|
||||||
def add_os_window(self, startup_session, os_window_id=None, wclass=None, wname=None, opts_for_size=None, startup_id=None):
|
def add_os_window(self, startup_session, os_window_id=None, wclass=None, wname=None, opts_for_size=None, startup_id=None):
|
||||||
if os_window_id is None:
|
if os_window_id is None:
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ vec4 calculate_foreground() {
|
|||||||
// Since strike and text are the same color, we simply add the alpha values
|
// Since strike and text are the same color, we simply add the alpha values
|
||||||
float combined_alpha = min(text_alpha + strike_alpha, 1.0f);
|
float combined_alpha = min(text_alpha + strike_alpha, 1.0f);
|
||||||
// Underline color might be different, so alpha blend
|
// Underline color might be different, so alpha blend
|
||||||
vec4 ans = alpha_blend(decoration_fg, underline_alpha * effective_text_alpha, fg, combined_alpha * effective_text_alpha);
|
vec4 ans = alpha_blend(fg, combined_alpha * effective_text_alpha, decoration_fg, underline_alpha * effective_text_alpha);
|
||||||
return mix(ans, cursor_color_vec, cursor_alpha);
|
return mix(ans, cursor_color_vec, cursor_alpha);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
static inline char**
|
static inline char**
|
||||||
serialize_string_tuple(PyObject *src) {
|
serialize_string_tuple(PyObject *src) {
|
||||||
Py_ssize_t sz = PyTuple_GET_SIZE(src);
|
Py_ssize_t sz = PyTuple_GET_SIZE(src);
|
||||||
|
|
||||||
char **ans = calloc(sz + 1, sizeof(char*));
|
char **ans = calloc(sz + 1, sizeof(char*));
|
||||||
if (!ans) fatal("Out of memory");
|
if (!ans) fatal("Out of memory");
|
||||||
for (Py_ssize_t i = 0; i < sz; i++) {
|
for (Py_ssize_t i = 0; i < sz; i++) {
|
||||||
@@ -28,6 +29,13 @@ serialize_string_tuple(PyObject *src) {
|
|||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
free_string_tuple(char** data) {
|
||||||
|
size_t i = 0;
|
||||||
|
while(data[i]) free(data[i++]);
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
|
||||||
extern char **environ;
|
extern char **environ;
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
@@ -125,8 +133,8 @@ spawn(PyObject *self UNUSED, PyObject *args) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#undef exit_on_err
|
#undef exit_on_err
|
||||||
free(argv);
|
free_string_tuple(argv);
|
||||||
free(env);
|
free_string_tuple(env);
|
||||||
if (PyErr_Occurred()) return NULL;
|
if (PyErr_Occurred()) return NULL;
|
||||||
return PyLong_FromLong(pid);
|
return PyLong_FromLong(pid);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -464,7 +464,7 @@ def options_spec():
|
|||||||
dest=cls
|
dest=cls
|
||||||
default={appname}
|
default={appname}
|
||||||
condition=not is_macos
|
condition=not is_macos
|
||||||
Set the class part of the :italic:`WM_CLASS` window property
|
Set the class part of the :italic:`WM_CLASS` window property. On Wayland, it sets the app id.
|
||||||
|
|
||||||
|
|
||||||
--name
|
--name
|
||||||
@@ -546,6 +546,11 @@ specify this address. This option will be ignored, unless you set
|
|||||||
option as it is read automatically from the environment.
|
option as it is read automatically from the environment.
|
||||||
|
|
||||||
|
|
||||||
|
--start-in-fullscreen
|
||||||
|
type=bool-set
|
||||||
|
Make the initial kitty window fullscreen on startup
|
||||||
|
|
||||||
|
|
||||||
# Debugging options
|
# Debugging options
|
||||||
|
|
||||||
--version -v
|
--version -v
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ class UnknownLayout(ValueError):
|
|||||||
hide_traceback = True
|
hide_traceback = True
|
||||||
|
|
||||||
|
|
||||||
|
cmap = {}
|
||||||
|
|
||||||
|
|
||||||
def cmd(short_desc, desc=None, options_spec=None, no_response=False, argspec='...', string_return_is_error=False, args_count=None):
|
def cmd(short_desc, desc=None, options_spec=None, no_response=False, argspec='...', string_return_is_error=False, args_count=None):
|
||||||
|
|
||||||
def w(func):
|
def w(func):
|
||||||
@@ -45,6 +48,7 @@ def cmd(short_desc, desc=None, options_spec=None, no_response=False, argspec='..
|
|||||||
func.no_response = no_response
|
func.no_response = no_response
|
||||||
func.string_return_is_error = string_return_is_error
|
func.string_return_is_error = string_return_is_error
|
||||||
func.args_count = 0 if not argspec else args_count
|
func.args_count = 0 if not argspec else args_count
|
||||||
|
cmap[func.name] = func
|
||||||
return func
|
return func
|
||||||
return w
|
return w
|
||||||
|
|
||||||
@@ -363,7 +367,7 @@ reset the layout to its default configuration.
|
|||||||
|
|
||||||
--self
|
--self
|
||||||
type=bool-set
|
type=bool-set
|
||||||
If specified close the window this command is run in, rather than the active window.
|
If specified resize the window this command is run in, rather than the active window.
|
||||||
''',
|
''',
|
||||||
argspec='',
|
argspec='',
|
||||||
string_return_is_error=True
|
string_return_is_error=True
|
||||||
@@ -787,9 +791,6 @@ def kitten(boss, window, payload):
|
|||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
cmap = {v.name: v for v in globals().values() if hasattr(v, 'is_cmd')}
|
|
||||||
|
|
||||||
|
|
||||||
def cli_params_for(func):
|
def cli_params_for(func):
|
||||||
return (func.options_spec or '\n').format, func.argspec, func.desc, '{} @ {}'.format(appname, func.name)
|
return (func.options_spec or '\n').format, func.argspec, func.desc, '{} @ {}'.format(appname, func.name)
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ completion_scripts = {
|
|||||||
'zsh': '''
|
'zsh': '''
|
||||||
_kitty() {
|
_kitty() {
|
||||||
local src
|
local src
|
||||||
# Send all words upto the word the cursor is currently on
|
# Send all words up to the word the cursor is currently on
|
||||||
src=$(printf "%s\n" "${(@)words[1,$CURRENT]}" | kitty +complete zsh)
|
src=$(printf "%s\n" "${(@)words[1,$CURRENT]}" | kitty +complete zsh)
|
||||||
if [[ $? == 0 ]]; then
|
if [[ $? == 0 ]]; then
|
||||||
eval ${src}
|
eval ${src}
|
||||||
@@ -56,6 +56,14 @@ kitty_completions() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
complete -o nospace -F kitty_completions kitty
|
complete -o nospace -F kitty_completions kitty
|
||||||
|
''',
|
||||||
|
'fish': '''
|
||||||
|
function __kitty_completions
|
||||||
|
# Send all words up to the one before the cursor
|
||||||
|
commandline -cop | kitty +complete fish
|
||||||
|
end
|
||||||
|
|
||||||
|
complete -f -c kitty -a "(__kitty_completions)"
|
||||||
''',
|
''',
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,6 +94,11 @@ def bash_input_parser(data):
|
|||||||
return words, new_word
|
return words, new_word
|
||||||
|
|
||||||
|
|
||||||
|
@input_parser
|
||||||
|
def fish_input_parser(data):
|
||||||
|
return data.rstrip().splitlines(), True
|
||||||
|
|
||||||
|
|
||||||
@output_serializer
|
@output_serializer
|
||||||
def zsh_output_serializer(ans):
|
def zsh_output_serializer(ans):
|
||||||
lines = []
|
lines = []
|
||||||
@@ -95,6 +108,10 @@ def zsh_output_serializer(ans):
|
|||||||
cmd += ['-S', '""']
|
cmd += ['-S', '""']
|
||||||
if description in ans.files_groups:
|
if description in ans.files_groups:
|
||||||
cmd.append('-f')
|
cmd.append('-f')
|
||||||
|
common_prefix = os.path.commonprefix(tuple(matches))
|
||||||
|
if common_prefix:
|
||||||
|
cmd.extend(('-p', shlex.quote(common_prefix)))
|
||||||
|
matches = {k[len(common_prefix):]: v for k, v in matches.items()}
|
||||||
cmd.append('--')
|
cmd.append('--')
|
||||||
for word, description in matches.items():
|
for word, description in matches.items():
|
||||||
cmd.append(shlex.quote(word))
|
cmd.append(shlex.quote(word))
|
||||||
@@ -117,6 +134,16 @@ def bash_output_serializer(ans):
|
|||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
@output_serializer
|
||||||
|
def fish_output_serializer(ans):
|
||||||
|
lines = []
|
||||||
|
for matches in ans.match_groups.values():
|
||||||
|
for word in matches:
|
||||||
|
lines.append(shlex.quote(word))
|
||||||
|
# debug('\n'.join(lines))
|
||||||
|
return '\n'.join(lines)
|
||||||
|
|
||||||
|
|
||||||
def completions_for_first_word(ans, prefix, entry_points, namespaced_entry_points):
|
def completions_for_first_word(ans, prefix, entry_points, namespaced_entry_points):
|
||||||
cmds = ['@' + c for c in cmap]
|
cmds = ['@' + c for c in cmap]
|
||||||
ans.match_groups['Entry points'] = {
|
ans.match_groups['Entry points'] = {
|
||||||
@@ -197,21 +224,40 @@ def complete_remote_command(ans, cmd_name, words, new_word):
|
|||||||
complete_alias_map(ans, words, new_word, alias_map)
|
complete_alias_map(ans, words, new_word, alias_map)
|
||||||
|
|
||||||
|
|
||||||
def complete_files_and_dirs(ans, prefix, files_group_name='Files', predicate=lambda filename: True):
|
def path_completion(prefix=''):
|
||||||
|
prefix = prefix.replace(r'\ ', ' ')
|
||||||
dirs, files = [], []
|
dirs, files = [], []
|
||||||
base = '.'
|
base = '.'
|
||||||
if prefix.endswith('/'):
|
if prefix.endswith('/'):
|
||||||
base = prefix
|
base = prefix
|
||||||
elif '/' in prefix:
|
elif '/' in prefix:
|
||||||
base = os.path.dirname(prefix)
|
base = os.path.dirname(prefix)
|
||||||
for x in os.scandir(base):
|
src = os.path.expandvars(os.path.expanduser(base))
|
||||||
q = os.path.relpath(x.path)
|
src_prefix = os.path.abspath(os.path.expandvars(os.path.expanduser(prefix))) if prefix else ''
|
||||||
if x.is_dir():
|
try:
|
||||||
if q.startswith(prefix):
|
items = os.scandir(src)
|
||||||
dirs.append(q.rstrip(os.sep) + os.sep)
|
except FileNotFoundError:
|
||||||
|
items = ()
|
||||||
|
for x in items:
|
||||||
|
abspath = os.path.abspath(x.path)
|
||||||
|
if prefix and not abspath.startswith(src_prefix):
|
||||||
|
continue
|
||||||
|
if prefix:
|
||||||
|
q = prefix + abspath[len(src_prefix):].lstrip(os.sep)
|
||||||
|
q = os.path.expandvars(os.path.expanduser(q))
|
||||||
else:
|
else:
|
||||||
if q.startswith(prefix) and predicate(q):
|
q = os.path.relpath(abspath)
|
||||||
files.append(q)
|
if x.is_dir():
|
||||||
|
dirs.append(q.rstrip(os.sep) + os.sep)
|
||||||
|
else:
|
||||||
|
files.append(q)
|
||||||
|
return dirs, files
|
||||||
|
|
||||||
|
|
||||||
|
def complete_files_and_dirs(ans, prefix, files_group_name='Files', predicate=None):
|
||||||
|
dirs, files = path_completion(prefix or '')
|
||||||
|
files = filter(predicate, files)
|
||||||
|
|
||||||
if dirs:
|
if dirs:
|
||||||
ans.match_groups['Directories'] = dict.fromkeys(dirs)
|
ans.match_groups['Directories'] = dict.fromkeys(dirs)
|
||||||
ans.files_groups.add('Directories'), ans.no_space_groups.add('Directories')
|
ans.files_groups.add('Directories'), ans.no_space_groups.add('Directories')
|
||||||
@@ -232,6 +278,17 @@ def complete_icat_args(ans, opt, prefix):
|
|||||||
complete_files_and_dirs(ans, prefix, 'Images', icat_file_predicate)
|
complete_files_and_dirs(ans, prefix, 'Images', icat_file_predicate)
|
||||||
|
|
||||||
|
|
||||||
|
def config_file_predicate(filename):
|
||||||
|
return filename.endswith('.conf')
|
||||||
|
|
||||||
|
|
||||||
|
def complete_diff_args(ans, opt, prefix):
|
||||||
|
if opt is None:
|
||||||
|
complete_files_and_dirs(ans, prefix, 'Files')
|
||||||
|
elif opt['dest'] == 'config':
|
||||||
|
complete_files_and_dirs(ans, prefix, 'Config Files', config_file_predicate)
|
||||||
|
|
||||||
|
|
||||||
def complete_kitten(ans, kitten, words, new_word):
|
def complete_kitten(ans, kitten, words, new_word):
|
||||||
try:
|
try:
|
||||||
cd = get_kitten_cli_docs(kitten)
|
cd = get_kitten_cli_docs(kitten)
|
||||||
@@ -247,7 +304,8 @@ def complete_kitten(ans, kitten, words, new_word):
|
|||||||
for alias in opt['aliases']:
|
for alias in opt['aliases']:
|
||||||
option_map[alias] = opt
|
option_map[alias] = opt
|
||||||
complete_alias_map(ans, words, new_word, option_map, {
|
complete_alias_map(ans, words, new_word, option_map, {
|
||||||
'icat': complete_icat_args
|
'icat': complete_icat_args,
|
||||||
|
'diff': complete_diff_args,
|
||||||
}.get(kitten))
|
}.get(kitten))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -35,14 +35,22 @@ named_keys = {
|
|||||||
|
|
||||||
def parse_shortcut(sc):
|
def parse_shortcut(sc):
|
||||||
parts = sc.split('+')
|
parts = sc.split('+')
|
||||||
mods = parse_mods(parts[:-1], sc)
|
mods = 0
|
||||||
if mods is None:
|
if len(parts) > 1:
|
||||||
return None, None, None
|
mods = parse_mods(parts[:-1], sc)
|
||||||
|
if mods is None:
|
||||||
|
return None, None, None
|
||||||
key = parts[-1].upper()
|
key = parts[-1].upper()
|
||||||
key = getattr(defines, 'GLFW_KEY_' + named_keys.get(key, key), None)
|
key = getattr(defines, 'GLFW_KEY_' + named_keys.get(key, key), None)
|
||||||
is_native = False
|
is_native = False
|
||||||
if key is None:
|
if key is None:
|
||||||
key = defines.key_for_native_key_name(parts[-1])
|
if parts[-1].startswith('0x'):
|
||||||
|
try:
|
||||||
|
key = int(parts[-1], 16)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
key = defines.key_for_native_key_name(parts[-1])
|
||||||
is_native = key is not None
|
is_native = key is not None
|
||||||
return mods, is_native, key
|
return mods, is_native, key
|
||||||
|
|
||||||
|
|||||||
@@ -95,6 +95,21 @@ for a list of key names. The name to use is the part after the :code:`XKB_KEY_`
|
|||||||
prefix. Note that you should only use an XKB key name for keys that are not present
|
prefix. Note that you should only use an XKB key name for keys that are not present
|
||||||
in the list of GLFW keys.
|
in the list of GLFW keys.
|
||||||
|
|
||||||
|
Finally, you can use raw system key codes to map keys. To see the system key code
|
||||||
|
for a key, start kitty with the :option:`kitty --debug-keyboard` option. Then kitty will
|
||||||
|
output some debug text for every key event. In that text look for ``native_code``
|
||||||
|
the value of that becomes the key name in the shortcut. For example:
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
on_key_input: glfw key: 65 native_code: 0x61 action: PRESS mods: 0x0 text: 'a'
|
||||||
|
|
||||||
|
Here, the key name for the :kbd:`A` key is :kbd:`0x61` and you can use it with::
|
||||||
|
|
||||||
|
map ctrl+0x61 something
|
||||||
|
|
||||||
|
to map :kbd:`ctrl+a` to something.
|
||||||
|
|
||||||
You can use the special action :code:`no_op` to unmap a keyboard shortcut that is
|
You can use the special action :code:`no_op` to unmap a keyboard shortcut that is
|
||||||
assigned in the default configuration.
|
assigned in the default configuration.
|
||||||
|
|
||||||
@@ -290,9 +305,19 @@ o('cursor_stop_blinking_after', 15.0, option_type=positive_float)
|
|||||||
|
|
||||||
g('scrollback') # {{{
|
g('scrollback') # {{{
|
||||||
|
|
||||||
o('scrollback_lines', 2000, option_type=positive_int, long_text=_('''
|
|
||||||
|
def scrollback_lines(x):
|
||||||
|
x = int(x)
|
||||||
|
if x < 0:
|
||||||
|
x = 2 ** 32 - 1
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
o('scrollback_lines', 2000, option_type=scrollback_lines, long_text=_('''
|
||||||
Number of lines of history to keep in memory for scrolling back. Memory is allocated
|
Number of lines of history to keep in memory for scrolling back. Memory is allocated
|
||||||
on demand.'''))
|
on demand. Negative numbers are (effectively) infinite scrollback. Note that using
|
||||||
|
very large scrollback is not recommended a it can slow down resizing of the terminal
|
||||||
|
and also use large amounts of RAM.'''))
|
||||||
|
|
||||||
o('scrollback_pager', 'less --chop-long-lines --RAW-CONTROL-CHARS +INPUT_LINE_NUMBER', option_type=to_cmdline, long_text=_('''
|
o('scrollback_pager', 'less --chop-long-lines --RAW-CONTROL-CHARS +INPUT_LINE_NUMBER', option_type=to_cmdline, long_text=_('''
|
||||||
Program with which to view scrollback in a new window. The scrollback buffer is
|
Program with which to view scrollback in a new window. The scrollback buffer is
|
||||||
@@ -850,6 +875,8 @@ g('shortcuts.tab') # {{{
|
|||||||
if is_macos:
|
if is_macos:
|
||||||
k('next_tab', 'ctrl+tab', 'next_tab', _('Next tab'))
|
k('next_tab', 'ctrl+tab', 'next_tab', _('Next tab'))
|
||||||
k('next_tab', 'kitty_mod+right', 'next_tab', _('Next tab'))
|
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', 'kitty_mod+left', 'previous_tab', _('Previous tab'))
|
k('previous_tab', 'kitty_mod+left', 'previous_tab', _('Previous tab'))
|
||||||
k('new_tab', 'kitty_mod+t', 'new_tab', _('New tab'))
|
k('new_tab', 'kitty_mod+t', 'new_tab', _('New tab'))
|
||||||
k('close_tab', 'kitty_mod+q', 'close_tab', _('Close tab'))
|
k('close_tab', 'kitty_mod+q', 'close_tab', _('Close tab'))
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from collections import namedtuple
|
|||||||
|
|
||||||
|
|
||||||
appname = 'kitty'
|
appname = 'kitty'
|
||||||
version = (0, 12, 0)
|
version = (0, 12, 1)
|
||||||
str_version = '.'.join(map(str, version))
|
str_version = '.'.join(map(str, version))
|
||||||
_plat = sys.platform.lower()
|
_plat = sys.platform.lower()
|
||||||
is_macos = 'darwin' in _plat
|
is_macos = 'darwin' in _plat
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
// *Bell*: Beeps.
|
// *Bell*: Beeps.
|
||||||
#define BEL 0x07
|
#define BEL 0x07
|
||||||
|
|
||||||
// *Backspace*: Backspace one column, but not past the begining of the
|
// *Backspace*: Backspace one column, but not past the beginning of the
|
||||||
// line.
|
// line.
|
||||||
#define BS 0x08
|
#define BS 0x08
|
||||||
|
|
||||||
|
|||||||
@@ -171,6 +171,14 @@ find_substitute_face(CFStringRef str, CTFontRef old_font) {
|
|||||||
else start++;
|
else start++;
|
||||||
if (new_font == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to find fallback CTFont"); return NULL; }
|
if (new_font == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to find fallback CTFont"); return NULL; }
|
||||||
if (new_font == old_font) { CFRelease(new_font); continue; }
|
if (new_font == old_font) { CFRelease(new_font); continue; }
|
||||||
|
CFStringRef name = CTFontCopyPostScriptName(new_font);
|
||||||
|
CFComparisonResult cr = CFStringCompare(name, CFSTR("LastResort"), 0);
|
||||||
|
CFRelease(name);
|
||||||
|
if (cr == kCFCompareEqualTo) {
|
||||||
|
CFRelease(new_font);
|
||||||
|
PyErr_SetString(PyExc_ValueError, "Failed to find fallback CTFont other than the LastResort font");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
return new_font;
|
return new_font;
|
||||||
}
|
}
|
||||||
PyErr_SetString(PyExc_ValueError, "CoreText returned the same font as a fallback font");
|
PyErr_SetString(PyExc_ValueError, "CoreText returned the same font as a fallback font");
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ open_tty(PyObject *self UNUSED, PyObject *args) {
|
|||||||
if (!read_with_timeout) flags |= O_NONBLOCK;
|
if (!read_with_timeout) flags |= O_NONBLOCK;
|
||||||
static char ctty[L_ctermid+1];
|
static char ctty[L_ctermid+1];
|
||||||
int fd = open(ctermid(ctty), flags);
|
int fd = open(ctermid(ctty), flags);
|
||||||
|
if (fd == -1) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
|
||||||
struct termios *termios_p = calloc(1, sizeof(struct termios));
|
struct termios *termios_p = calloc(1, sizeof(struct termios));
|
||||||
if (!termios_p) return PyErr_NoMemory();
|
if (!termios_p) return PyErr_NoMemory();
|
||||||
if (tcgetattr(fd, termios_p) != 0) { free(termios_p); PyErr_SetFromErrno(PyExc_OSError); return NULL; }
|
if (tcgetattr(fd, termios_p) != 0) { free(termios_p); PyErr_SetFromErrno(PyExc_OSError); return NULL; }
|
||||||
|
|||||||
@@ -1077,10 +1077,10 @@ send_prerendered_sprites(FontGroup *fg) {
|
|||||||
do_increment(fg, &error);
|
do_increment(fg, &error);
|
||||||
if (error != 0) { sprite_map_set_error(error); PyErr_Print(); fatal("failed"); }
|
if (error != 0) { sprite_map_set_error(error); PyErr_Print(); fatal("failed"); }
|
||||||
PyObject *args = PyObject_CallFunction(prerender_function, "IIIIIdd", fg->cell_width, fg->cell_height, fg->baseline, fg->underline_position, fg->underline_thickness, fg->logical_dpi_x, fg->logical_dpi_y);
|
PyObject *args = PyObject_CallFunction(prerender_function, "IIIIIdd", fg->cell_width, fg->cell_height, fg->baseline, fg->underline_position, fg->underline_thickness, fg->logical_dpi_x, fg->logical_dpi_y);
|
||||||
if (args == NULL) { PyErr_Print(); fatal("Failed to prerender cells"); }
|
if (args == NULL) { PyErr_Print(); fatal("Failed to pre-render cells"); }
|
||||||
for (ssize_t i = 0; i < PyTuple_GET_SIZE(args) - 1; i++) {
|
for (ssize_t i = 0; i < PyTuple_GET_SIZE(args) - 1; i++) {
|
||||||
x = fg->sprite_tracker.x; y = fg->sprite_tracker.y; z = fg->sprite_tracker.z;
|
x = fg->sprite_tracker.x; y = fg->sprite_tracker.y; z = fg->sprite_tracker.z;
|
||||||
if (y > 0) { fatal("too many prerendered sprites for your GPU"); }
|
if (y > 0) { fatal("too many pre-rendered sprites for your GPU or the font size is too large"); }
|
||||||
do_increment(fg, &error);
|
do_increment(fg, &error);
|
||||||
if (error != 0) { sprite_map_set_error(error); PyErr_Print(); fatal("failed"); }
|
if (error != 0) { sprite_map_set_error(error); PyErr_Print(); fatal("failed"); }
|
||||||
uint8_t *alpha_mask = PyLong_AsVoidPtr(PyTuple_GET_ITEM(args, i));
|
uint8_t *alpha_mask = PyLong_AsVoidPtr(PyTuple_GET_ITEM(args, i));
|
||||||
@@ -1280,6 +1280,7 @@ get_fallback_font(PyObject UNUSED *self, PyObject *args) {
|
|||||||
if (italic) gpu_cell.attrs |= 1 << ITALIC_SHIFT;
|
if (italic) gpu_cell.attrs |= 1 << ITALIC_SHIFT;
|
||||||
FontGroup *fg = font_groups;
|
FontGroup *fg = font_groups;
|
||||||
ssize_t ans = fallback_font(fg, &cpu_cell, &gpu_cell);
|
ssize_t ans = fallback_font(fg, &cpu_cell, &gpu_cell);
|
||||||
|
if (ans == MISSING_FONT) { PyErr_SetString(PyExc_ValueError, "No fallback font found"); return NULL; }
|
||||||
if (ans < 0) { PyErr_SetString(PyExc_ValueError, "Too many fallback fonts"); return NULL; }
|
if (ans < 0) { PyErr_SetString(PyExc_ValueError, "Too many fallback fonts"); return NULL; }
|
||||||
return fg->fonts[ans].face;
|
return fg->fonts[ans].face;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
import ctypes
|
import ctypes
|
||||||
import sys
|
import sys
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from math import ceil, floor, pi, sin, sqrt
|
from math import ceil, pi, cos, sqrt
|
||||||
|
|
||||||
from kitty.config import defaults
|
from kitty.config import defaults
|
||||||
from kitty.constants import is_macos
|
from kitty.constants import is_macos
|
||||||
@@ -116,7 +116,7 @@ def add_dline(buf, cell_width, position, thickness, cell_height):
|
|||||||
|
|
||||||
def add_curl(buf, cell_width, position, thickness, cell_height):
|
def add_curl(buf, cell_width, position, thickness, cell_height):
|
||||||
xfactor = 2.0 * pi / cell_width
|
xfactor = 2.0 * pi / cell_width
|
||||||
yfactor = thickness
|
yfactor = max(thickness, 2)
|
||||||
|
|
||||||
def clamp_y(y):
|
def clamp_y(y):
|
||||||
return max(0, min(int(y), cell_height - 1))
|
return max(0, min(int(y), cell_height - 1))
|
||||||
@@ -125,19 +125,18 @@ def add_curl(buf, cell_width, position, thickness, cell_height):
|
|||||||
return max(0, min(int(x), cell_width - 1))
|
return max(0, min(int(x), cell_width - 1))
|
||||||
|
|
||||||
def add_intensity(x, y, distance):
|
def add_intensity(x, y, distance):
|
||||||
buf[cell_width * y + x] = min(
|
idx = cell_width * y + x
|
||||||
255, buf[cell_width * y + x] + int(255 * (1 - distance))
|
buf[idx] = min(
|
||||||
|
255, buf[idx] + int(255 * (1 - distance))
|
||||||
)
|
)
|
||||||
|
|
||||||
for x_exact in range(cell_width):
|
for x_exact in range(cell_width):
|
||||||
y_exact = yfactor * sin(x_exact * xfactor) + position
|
y_exact = yfactor * cos(x_exact * xfactor) + position
|
||||||
y_below = clamp_y(floor(y_exact))
|
y = clamp_y(ceil(y_exact))
|
||||||
y_above = clamp_y(ceil(y_exact))
|
|
||||||
x_before, x_after = map(clamp_x, (x_exact - 1, x_exact + 1))
|
x_before, x_after = map(clamp_x, (x_exact - 1, x_exact + 1))
|
||||||
for x in {x_before, x_exact, x_after}:
|
for x in {x_before, x_exact, x_after}:
|
||||||
for y in {y_below, y_above}:
|
dist = sqrt((x - x_exact)**2 + (y - y_exact)**2) / 2
|
||||||
dist = sqrt((x - x_exact)**2 + (y - y_exact)**2) / 2
|
add_intensity(x, y, dist)
|
||||||
add_intensity(x, y, dist)
|
|
||||||
|
|
||||||
|
|
||||||
def render_special(
|
def render_special(
|
||||||
|
|||||||
6
kitty/glfw-wrapper.h
generated
6
kitty/glfw-wrapper.h
generated
@@ -734,6 +734,10 @@ typedef int (* GLFWapplicationshouldhandlereopenfun)(int);
|
|||||||
* [attribute](@ref GLFW_CLIENT_API_attrib).
|
* [attribute](@ref GLFW_CLIENT_API_attrib).
|
||||||
*/
|
*/
|
||||||
#define GLFW_CONTEXT_CREATION_API 0x0002200B
|
#define GLFW_CONTEXT_CREATION_API 0x0002200B
|
||||||
|
/*! @brief Window content area scaling window
|
||||||
|
* [window hint](@ref GLFW_SCALE_TO_MONITOR).
|
||||||
|
*/
|
||||||
|
#define GLFW_SCALE_TO_MONITOR 0x0002200C
|
||||||
|
|
||||||
#define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001
|
#define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001
|
||||||
#define GLFW_COCOA_FRAME_NAME 0x00023002
|
#define GLFW_COCOA_FRAME_NAME 0x00023002
|
||||||
@@ -741,6 +745,8 @@ typedef int (* GLFWapplicationshouldhandlereopenfun)(int);
|
|||||||
|
|
||||||
#define GLFW_X11_CLASS_NAME 0x00024001
|
#define GLFW_X11_CLASS_NAME 0x00024001
|
||||||
#define GLFW_X11_INSTANCE_NAME 0x00024002
|
#define GLFW_X11_INSTANCE_NAME 0x00024002
|
||||||
|
|
||||||
|
#define GLFW_WAYLAND_APP_ID 0x00025001
|
||||||
/*! @} */
|
/*! @} */
|
||||||
|
|
||||||
#define GLFW_NO_API 0
|
#define GLFW_NO_API 0
|
||||||
|
|||||||
@@ -278,12 +278,11 @@ set_default_window_icon(PyObject UNUSED *self, PyObject *args) {
|
|||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GLFWwindow *current_os_window_ctx = NULL;
|
|
||||||
|
|
||||||
void
|
void
|
||||||
make_os_window_context_current(OSWindow *w) {
|
make_os_window_context_current(OSWindow *w) {
|
||||||
if (w->handle != current_os_window_ctx) {
|
GLFWwindow *current_context = glfwGetCurrentContext();
|
||||||
current_os_window_ctx = w->handle;
|
if (w->handle != current_context) {
|
||||||
glfwMakeContextCurrent(w->handle);
|
glfwMakeContextCurrent(w->handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -409,6 +408,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
|||||||
#ifndef __APPLE__
|
#ifndef __APPLE__
|
||||||
glfwWindowHintString(GLFW_X11_INSTANCE_NAME, wm_class_name);
|
glfwWindowHintString(GLFW_X11_INSTANCE_NAME, wm_class_name);
|
||||||
glfwWindowHintString(GLFW_X11_CLASS_NAME, wm_class_class);
|
glfwWindowHintString(GLFW_X11_CLASS_NAME, wm_class_class);
|
||||||
|
glfwWindowHintString(GLFW_WAYLAND_APP_ID, wm_class_class);
|
||||||
if (OPT(x11_hide_window_decorations)) {
|
if (OPT(x11_hide_window_decorations)) {
|
||||||
glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
|
glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
|
||||||
}
|
}
|
||||||
@@ -488,7 +488,6 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
|||||||
}
|
}
|
||||||
w->logical_dpi_x = dpi_x; w->logical_dpi_y = dpi_y;
|
w->logical_dpi_x = dpi_x; w->logical_dpi_y = dpi_y;
|
||||||
w->fonts_data = fonts_data;
|
w->fonts_data = fonts_data;
|
||||||
current_os_window_ctx = glfw_window;
|
|
||||||
w->shown_once = true;
|
w->shown_once = true;
|
||||||
push_focus_history(w);
|
push_focus_history(w);
|
||||||
glfwSwapInterval(OPT(sync_to_monitor) ? 1 : 0);
|
glfwSwapInterval(OPT(sync_to_monitor) ? 1 : 0);
|
||||||
@@ -535,7 +534,6 @@ destroy_os_window(OSWindow *w) {
|
|||||||
show_mouse_cursor(w->handle);
|
show_mouse_cursor(w->handle);
|
||||||
glfwSetCursor(w->handle, NULL);
|
glfwSetCursor(w->handle, NULL);
|
||||||
glfwDestroyWindow(w->handle);
|
glfwDestroyWindow(w->handle);
|
||||||
if (current_os_window_ctx == w->handle) current_os_window_ctx = NULL;
|
|
||||||
}
|
}
|
||||||
w->handle = NULL;
|
w->handle = NULL;
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
|||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "II", &ynum, &xnum)) return NULL;
|
if (!PyArg_ParseTuple(args, "II", &ynum, &xnum)) return NULL;
|
||||||
|
|
||||||
if (xnum * ynum == 0) {
|
if (xnum == 0 || ynum == 0) {
|
||||||
PyErr_SetString(PyExc_ValueError, "Cannot create an empty history buffer");
|
PyErr_SetString(PyExc_ValueError, "Cannot create an empty history buffer");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -74,9 +74,6 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
|||||||
add_segment(self);
|
add_segment(self);
|
||||||
self->line = alloc_line();
|
self->line = alloc_line();
|
||||||
self->line->xnum = xnum;
|
self->line->xnum = xnum;
|
||||||
for(index_type y = 0; y < self->ynum; y++) {
|
|
||||||
clear_chars_in_line(cpu_lineptr(self, y), gpu_lineptr(self, y), self->xnum, BLANK_CHAR);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (PyObject*)self;
|
return (PyObject*)self;
|
||||||
@@ -164,7 +161,7 @@ line(HistoryBuf *self, PyObject *val) {
|
|||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
__str__(HistoryBuf *self) {
|
__str__(HistoryBuf *self) {
|
||||||
PyObject *lines = PyTuple_New(self->ynum);
|
PyObject *lines = PyTuple_New(self->count);
|
||||||
if (lines == NULL) return PyErr_NoMemory();
|
if (lines == NULL) return PyErr_NoMemory();
|
||||||
for (index_type i = 0; i < self->count; i++) {
|
for (index_type i = 0; i < self->count; i++) {
|
||||||
init_line(self, index_of(self, i), self->line);
|
init_line(self, index_of(self, i), self->line);
|
||||||
@@ -225,7 +222,7 @@ static PyObject*
|
|||||||
dirty_lines(HistoryBuf *self, PyObject *a UNUSED) {
|
dirty_lines(HistoryBuf *self, PyObject *a UNUSED) {
|
||||||
#define dirty_lines_doc "dirty_lines() -> Line numbers of all lines that have dirty text."
|
#define dirty_lines_doc "dirty_lines() -> Line numbers of all lines that have dirty text."
|
||||||
PyObject *ans = PyList_New(0);
|
PyObject *ans = PyList_New(0);
|
||||||
for (index_type i = 0; i < self->ynum; i++) {
|
for (index_type i = 0; i < self->count; i++) {
|
||||||
if (*attrptr(self, i) & TEXT_DIRTY_MASK) {
|
if (*attrptr(self, i) & TEXT_DIRTY_MASK) {
|
||||||
PyList_Append(ans, PyLong_FromUnsignedLong(i));
|
PyList_Append(ans, PyLong_FromUnsignedLong(i));
|
||||||
}
|
}
|
||||||
|
|||||||
18
kitty/keys.c
18
kitty/keys.c
@@ -103,13 +103,9 @@ check_if_special(int key, int mods, int scancode) {
|
|||||||
qkey = SPECIAL_INDEX(qkey);
|
qkey = SPECIAL_INDEX(qkey);
|
||||||
special = needs_special_handling[qkey];
|
special = needs_special_handling[qkey];
|
||||||
}
|
}
|
||||||
#ifdef __APPLE__
|
for (size_t i = 0; !special && i < native_special_keys_count; i++) {
|
||||||
(void)scancode;
|
if (scancode == native_special_keys[i].scancode && mods == native_special_keys[i].mods) special = true;
|
||||||
#else
|
}
|
||||||
for (size_t i = 0; !special && i < native_special_keys_count; i++) {
|
|
||||||
if (scancode == native_special_keys[i].scancode && mods == native_special_keys[i].mods) special = true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return special;
|
return special;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +123,7 @@ update_ime_position(OSWindow *os_window, Window* w, Screen *screen) {
|
|||||||
void
|
void
|
||||||
on_key_input(int key, int scancode, int action, int mods, const char* text, int state) {
|
on_key_input(int key, int scancode, int action, int mods, const char* text, int state) {
|
||||||
Window *w = active_window();
|
Window *w = active_window();
|
||||||
debug("on_key_input: glfw key: %d native_code: %d action: %s mods: 0x%x text: '%s' state: %d ",
|
debug("on_key_input: glfw key: %d native_code: 0x%x action: %s mods: 0x%x text: '%s' state: %d ",
|
||||||
key, scancode,
|
key, scancode,
|
||||||
(action == GLFW_RELEASE ? "RELEASE" : (action == GLFW_PRESS ? "PRESS" : "REPEAT")),
|
(action == GLFW_RELEASE ? "RELEASE" : (action == GLFW_PRESS ? "PRESS" : "REPEAT")),
|
||||||
mods, text, state);
|
mods, text, state);
|
||||||
@@ -222,15 +218,13 @@ PYWRAP1(key_for_native_key_name) {
|
|||||||
const char *name;
|
const char *name;
|
||||||
int case_sensitive = 0;
|
int case_sensitive = 0;
|
||||||
PA("s|p", &name, case_sensitive);
|
PA("s|p", &name, case_sensitive);
|
||||||
#ifdef __APPLE__
|
#ifndef __APPLE__
|
||||||
Py_RETURN_NONE;
|
|
||||||
#else
|
|
||||||
if (glfwGetXKBScancode) { // if this function is called before GLFW is initialized glfwGetXKBScancode will be NULL
|
if (glfwGetXKBScancode) { // if this function is called before GLFW is initialized glfwGetXKBScancode will be NULL
|
||||||
int scancode = glfwGetXKBScancode(name, case_sensitive);
|
int scancode = glfwGetXKBScancode(name, case_sensitive);
|
||||||
if (scancode) return Py_BuildValue("i", scancode);
|
if (scancode) return Py_BuildValue("i", scancode);
|
||||||
}
|
}
|
||||||
Py_RETURN_NONE;
|
|
||||||
#endif
|
#endif
|
||||||
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyMethodDef module_methods[] = {
|
static PyMethodDef module_methods[] = {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ from . import fast_data_types as defines
|
|||||||
from .key_encoding import KEY_MAP
|
from .key_encoding import KEY_MAP
|
||||||
from .terminfo import key_as_bytes
|
from .terminfo import key_as_bytes
|
||||||
from .utils import base64_encode
|
from .utils import base64_encode
|
||||||
from .constants import is_macos
|
|
||||||
|
|
||||||
|
|
||||||
def modify_key_bytes(keybytes, amt):
|
def modify_key_bytes(keybytes, amt):
|
||||||
@@ -276,7 +275,7 @@ def interpret_key_event(key, scancode, mods, window, action):
|
|||||||
def get_shortcut(keymap, mods, key, scancode):
|
def get_shortcut(keymap, mods, key, scancode):
|
||||||
mods &= 0b1111
|
mods &= 0b1111
|
||||||
ans = keymap.get((mods, False, key))
|
ans = keymap.get((mods, False, key))
|
||||||
if ans is None and not is_macos:
|
if ans is None:
|
||||||
ans = keymap.get((mods, True, scancode))
|
ans = keymap.get((mods, True, scancode))
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|||||||
@@ -191,12 +191,12 @@ def setup_profiling(args):
|
|||||||
print('To view the graphical call data, use: kcachegrind', cg)
|
print('To view the graphical call data, use: kcachegrind', cg)
|
||||||
|
|
||||||
|
|
||||||
def macos_cmdline():
|
def macos_cmdline(argv_args):
|
||||||
try:
|
try:
|
||||||
with open(os.path.join(config_dir, 'macos-launch-services-cmdline')) as f:
|
with open(os.path.join(config_dir, 'macos-launch-services-cmdline')) as f:
|
||||||
raw = f.read()
|
raw = f.read()
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
return []
|
return argv_args
|
||||||
import shlex
|
import shlex
|
||||||
raw = raw.strip()
|
raw = raw.strip()
|
||||||
ans = shlex.split(raw)
|
ans = shlex.split(raw)
|
||||||
@@ -242,7 +242,7 @@ def _main():
|
|||||||
args = sys.argv[1:]
|
args = sys.argv[1:]
|
||||||
if is_macos and os.environ.pop('KITTY_LAUNCHED_BY_LAUNCH_SERVICES', None) == '1':
|
if is_macos and os.environ.pop('KITTY_LAUNCHED_BY_LAUNCH_SERVICES', None) == '1':
|
||||||
os.chdir(os.path.expanduser('~'))
|
os.chdir(os.path.expanduser('~'))
|
||||||
args = macos_cmdline()
|
args = macos_cmdline(args)
|
||||||
try:
|
try:
|
||||||
cwd_ok = os.path.isdir(os.getcwd())
|
cwd_ok = os.path.isdir(os.getcwd())
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
@@ -708,7 +708,15 @@ screen_is_cursor_visible(Screen *self) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
screen_backspace(Screen *self) {
|
screen_backspace(Screen *self) {
|
||||||
screen_cursor_back(self, 1, -1);
|
unsigned int amount = 1;
|
||||||
|
if (self->cursor->x < self->columns && self->cursor->x > 1) {
|
||||||
|
// check if previous character is a wide character
|
||||||
|
linebuf_init_line(self->linebuf, self->cursor->y);
|
||||||
|
unsigned int xpos = self->cursor->x - 2;
|
||||||
|
GPUCell *gpu_cell = self->linebuf->line->gpu_cells + xpos;
|
||||||
|
if ((gpu_cell->attrs & WIDTH_MASK) == 2) amount = 2;
|
||||||
|
}
|
||||||
|
screen_cursor_back(self, amount, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ add_os_window() {
|
|||||||
static inline id_type
|
static inline id_type
|
||||||
add_tab(id_type os_window_id) {
|
add_tab(id_type os_window_id) {
|
||||||
WITH_OS_WINDOW(os_window_id)
|
WITH_OS_WINDOW(os_window_id)
|
||||||
|
make_os_window_context_current(os_window);
|
||||||
ensure_space_for(os_window, tabs, Tab, os_window->num_tabs + 1, capacity, 1, true);
|
ensure_space_for(os_window, tabs, Tab, os_window->num_tabs + 1, capacity, 1, true);
|
||||||
memset(os_window->tabs + os_window->num_tabs, 0, sizeof(Tab));
|
memset(os_window->tabs + os_window->num_tabs, 0, sizeof(Tab));
|
||||||
os_window->tabs[os_window->num_tabs].id = ++global_state.tab_id_counter;
|
os_window->tabs[os_window->num_tabs].id = ++global_state.tab_id_counter;
|
||||||
|
|||||||
@@ -2,10 +2,13 @@
|
|||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
|
||||||
from kitty.constants import is_macos
|
from kitty.constants import is_macos
|
||||||
from kitty.fast_data_types import (
|
from kitty.fast_data_types import (
|
||||||
DECAWM, sprite_map_set_layout, sprite_map_set_limits, test_render_line,
|
DECAWM, get_fallback_font, sprite_map_set_layout, sprite_map_set_limits,
|
||||||
test_sprite_position_for, wcwidth
|
test_render_line, test_sprite_position_for, wcwidth
|
||||||
)
|
)
|
||||||
from kitty.fonts.box_drawing import box_chars
|
from kitty.fonts.box_drawing import box_chars
|
||||||
from kitty.fonts.render import render_string, setup_for_testing, shape_string
|
from kitty.fonts.render import render_string, setup_for_testing, shape_string
|
||||||
@@ -106,3 +109,16 @@ class Rendering(BaseTest):
|
|||||||
s.draw('\ufe0f')
|
s.draw('\ufe0f')
|
||||||
self.ae((s.cursor.x, s.cursor.y), (2, 4))
|
self.ae((s.cursor.x, s.cursor.y), (2, 4))
|
||||||
self.ae(str(s.line(s.cursor.y)), '\u2716\ufe0f')
|
self.ae(str(s.line(s.cursor.y)), '\u2716\ufe0f')
|
||||||
|
|
||||||
|
@unittest.skipUnless(is_macos, 'Only macOS has a Last Resort font')
|
||||||
|
def test_fallback_font_not_last_resort(self):
|
||||||
|
# Ensure that the LastResort font is not reported as a fallback font on
|
||||||
|
# macOS. See https://github.com/kovidgoyal/kitty/issues/799
|
||||||
|
from io import StringIO
|
||||||
|
orig, buf = sys.stderr, StringIO()
|
||||||
|
sys.stderr = buf
|
||||||
|
try:
|
||||||
|
self.assertRaises(ValueError, get_fallback_font, '\U0010FFFF', False, False)
|
||||||
|
finally:
|
||||||
|
sys.stderr = orig
|
||||||
|
self.assertIn('LastResort', buf.getvalue())
|
||||||
|
|||||||
@@ -222,6 +222,15 @@ class TestScreen(BaseTest):
|
|||||||
for i in range(1, 5):
|
for i in range(1, 5):
|
||||||
self.ae(str(s.line(i)), '12345')
|
self.ae(str(s.line(i)), '12345')
|
||||||
|
|
||||||
|
def test_backspace_wide_characters(self):
|
||||||
|
s = self.create_screen()
|
||||||
|
s.draw('⛅')
|
||||||
|
self.ae(s.cursor.x, 2)
|
||||||
|
s.backspace()
|
||||||
|
s.draw(' ')
|
||||||
|
s.backspace()
|
||||||
|
self.ae(s.cursor.x, 0)
|
||||||
|
|
||||||
def test_resize(self):
|
def test_resize(self):
|
||||||
s = self.create_screen(scrollback=6)
|
s = self.create_screen(scrollback=6)
|
||||||
s.draw(''.join([str(i) * s.columns for i in range(s.lines)]))
|
s.draw(''.join([str(i) * s.columns for i in range(s.lines)]))
|
||||||
|
|||||||
@@ -331,8 +331,14 @@ def require_git_master(branch='master'):
|
|||||||
raise SystemExit('You must be in the {} git branch'.format(branch))
|
raise SystemExit('You must be in the {} git branch'.format(branch))
|
||||||
|
|
||||||
|
|
||||||
|
def require_penv():
|
||||||
|
if 'PENV' not in os.environ:
|
||||||
|
raise SystemExit('The PENV env var is not present, required for uploading releases')
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
require_git_master()
|
require_git_master()
|
||||||
|
require_penv()
|
||||||
parser = argparse.ArgumentParser(description='Publish kitty')
|
parser = argparse.ArgumentParser(description='Publish kitty')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--only',
|
'--only',
|
||||||
|
|||||||
Reference in New Issue
Block a user