Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7926a140b8 | ||
|
|
023487a1d7 | ||
|
|
71f84b1023 | ||
|
|
ce85382c68 | ||
|
|
94d248d812 | ||
|
|
62db7639a8 | ||
|
|
80f3fe6e9f | ||
|
|
9ba2c4b397 | ||
|
|
bfa53d4b45 | ||
|
|
69f3ceb903 | ||
|
|
d0f839bbd8 | ||
|
|
48538c0e25 | ||
|
|
02ab22f08c | ||
|
|
b3b216a6dc | ||
|
|
431564c8bc | ||
|
|
57f1bbb71a | ||
|
|
faac8649e4 | ||
|
|
1e6a41186f | ||
|
|
eddee67130 | ||
|
|
9b834014e9 | ||
|
|
a467e79144 | ||
|
|
90cc785b55 | ||
|
|
09710425ab | ||
|
|
dd36727adf | ||
|
|
47b665a624 | ||
|
|
7011329761 | ||
|
|
ea24c23fde | ||
|
|
be8f2b8106 | ||
|
|
71db024a3b | ||
|
|
3790af6897 | ||
|
|
dc6ef63ddd | ||
|
|
ef762f7a2b | ||
|
|
4ed646e062 | ||
|
|
afbd843388 | ||
|
|
18c3598ef2 | ||
|
|
1c2623e609 | ||
|
|
e232863028 | ||
|
|
a89309153d | ||
|
|
fee7c8e573 |
@@ -3,6 +3,41 @@ Changelog
|
||||
|
||||
kitty is a feature full, cross-platform, *fast*, GPU based terminal emulator.
|
||||
|
||||
version 0.8.4 [2018-03-31]
|
||||
-----------------------------
|
||||
|
||||
- Fix presence of XDG_CONFIG_DIRS and absence of XDG_CONFIG_HOME preventing
|
||||
kitty from starting
|
||||
|
||||
- Revert change in last release to cell width calculation. Instead just clip
|
||||
the right edges of characters that overflow the cell by at most two pixels
|
||||
|
||||
|
||||
version 0.8.3 [2018-03-29]
|
||||
-----------------------------
|
||||
|
||||
- Fix a regression that broke the visual bell and invert screen colors escape
|
||||
code
|
||||
|
||||
- Allow double-click and triple-click + drag to extend selections word at a
|
||||
time or line at a time
|
||||
|
||||
- Add a keyboard shortcut to set the tab title
|
||||
|
||||
- Fix setting window title to empty via OSC escape code not working correctly
|
||||
|
||||
- Linux: Fix cell width calculation incorrect for some fonts (cell widths are
|
||||
now calculated by actually rendering bitmaps, which is slower but more
|
||||
accurate)
|
||||
|
||||
- Allow specifying a system wide kitty config file, for all users
|
||||
|
||||
- Add a --debug-config command line flag to output data about the system and
|
||||
kitty configuration.
|
||||
|
||||
- Wayland: Fix auto-repeat of keys not working
|
||||
|
||||
|
||||
version 0.8.2 [2018-03-17]
|
||||
-----------------------------
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
:sc_scroll_page_down: pass:quotes[`ctrl+shift+page_down`]
|
||||
:sc_scroll_page_up: pass:quotes[`ctrl+shift+page_up`]
|
||||
:sc_second_window: pass:quotes[`ctrl+shift+2`]
|
||||
:sc_set_tab_title: pass:quotes[`ctrl+shift+alt+t`]
|
||||
:sc_seventh_window: pass:quotes[`ctrl+shift+7`]
|
||||
:sc_show_scrollback: pass:quotes[`ctrl+shift+h`]
|
||||
:sc_sixth_window: pass:quotes[`ctrl+shift+6`]
|
||||
@@ -132,6 +133,8 @@ python3 .
|
||||
|
||||
* Gentoo: https://packages.gentoo.org/packages/x11-terms/kitty
|
||||
|
||||
* openSUSE: https://build.opensuse.org/package/show/X11:terminals/kitty
|
||||
|
||||
=== macOS packages
|
||||
|
||||
kitty is available as a macOS `dmg` file for easy installation from the
|
||||
@@ -200,6 +203,7 @@ windows are:
|
||||
|Next layout | {sc_next_layout}
|
||||
|Move tab forward | {sc_move_tab_forward}
|
||||
|Move tab backward | {sc_move_tab_backward}
|
||||
|Set tab title | {sc_set_tab_title}
|
||||
|
||||
|===
|
||||
|
||||
@@ -317,11 +321,15 @@ a pull request!
|
||||
|
||||
kitty is highly customizable, everything from keyboard shortcuts, to painting
|
||||
frames-per-second. See the heavily commented link:kitty/kitty.conf[default
|
||||
config file]. By default kitty looks for a config file in the OS config
|
||||
directory (usually `~/.config/kitty/kitty.conf` on Linux and
|
||||
config file] for an overview of all customization possibilities.
|
||||
|
||||
By default kitty looks for a config file in the OS config directories (usually
|
||||
`~/.config/kitty/kitty.conf` and additionally
|
||||
`~/Library/Preferences/kitty/kitty.conf` on macOS) but you can pass a specific
|
||||
path via the `--config` option or use the `KITTY_CONFIG_DIRECTORY` environment
|
||||
variable.
|
||||
variable. See the help for the `--config` option in `kitty --help` for full
|
||||
details.
|
||||
|
||||
|
||||
== Startup Sessions
|
||||
|
||||
@@ -438,6 +446,8 @@ installs the terminfo file and `kitty` that installs the main program.
|
||||
This allows users to install the terminfo file on servers into which they ssh,
|
||||
without needing to install all of kitty.
|
||||
|
||||
You also need `tic` to compile the terminfo files, it is usually found in the development package of `ncurses`
|
||||
|
||||
This applies to creating packages for kitty for macOS package managers such as
|
||||
brew or MacPorts as well.
|
||||
|
||||
@@ -464,7 +474,7 @@ makes use of the extra cell to render them in two cells.
|
||||
|
||||
=== How do I build kitty.app on macOS?
|
||||
|
||||
Install `optipng` and `librsvg` using `brew` or similar (needed for the logo generation step).
|
||||
Install `imagemagick`, `optipng` and `librsvg` using `brew` or similar (needed for the logo generation step).
|
||||
|
||||
And run:
|
||||
```
|
||||
|
||||
@@ -582,6 +582,9 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
||||
window->ns.xscale = xscale;
|
||||
window->ns.yscale = yscale;
|
||||
_glfwInputWindowContentScale(window, xscale, yscale);
|
||||
|
||||
if (window->ns.layer)
|
||||
[window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1358,7 +1361,7 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
|
||||
[NSApp requestUserAttention:NSInformationalRequest];
|
||||
}
|
||||
|
||||
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t param)
|
||||
int _glfwPlatformWindowBell(_GLFWwindow* window)
|
||||
{
|
||||
NSBeep();
|
||||
return GLFW_TRUE;
|
||||
@@ -1871,6 +1874,7 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
||||
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
||||
}
|
||||
|
||||
[window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]];
|
||||
[window->ns.view setWantsLayer:YES];
|
||||
|
||||
memset(&sci, 0, sizeof(sci));
|
||||
@@ -1902,4 +1906,3 @@ GLFWAPI id glfwGetCocoaWindow(GLFWwindow* handle)
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(nil);
|
||||
return window->ns.object;
|
||||
}
|
||||
|
||||
|
||||
13
glfw/glfw.py
13
glfw/glfw.py
@@ -10,6 +10,10 @@ import sys
|
||||
|
||||
_plat = sys.platform.lower()
|
||||
is_macos = 'darwin' in _plat
|
||||
is_freebsd = 'freebsd' in _plat
|
||||
is_netbsd = 'netbsd' in _plat
|
||||
is_dragonflybsd = 'dragonfly' in _plat
|
||||
is_bsd = is_freebsd or is_netbsd or is_dragonflybsd
|
||||
base = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
@@ -100,13 +104,14 @@ def collect_source_information():
|
||||
'common': dict(extract_sources('common')),
|
||||
'wayland_protocols': wayland_protocols,
|
||||
}
|
||||
joystick = 'null' if is_bsd else 'linux'
|
||||
for group in 'cocoa win32 x11 wayland osmesa'.split():
|
||||
m = re.search('_GLFW_' + group.upper(), raw)
|
||||
ans[group] = dict(extract_sources('glfw', m.start()))
|
||||
if group == 'x11':
|
||||
ans[group]['headers'].append('linux_joystick.h')
|
||||
ans[group]['sources'].append('linux_joystick.c')
|
||||
elif group == 'wayland':
|
||||
if group in ('x11', 'wayland'):
|
||||
ans[group]['headers'].append('{}_joystick.h'.format(joystick))
|
||||
ans[group]['sources'].append('{}_joystick.c'.format(joystick))
|
||||
if group == 'wayland':
|
||||
ans[group]['protocols'] = p = []
|
||||
for m in re.finditer(r'WAYLAND_PROTOCOLS_PKGDATADIR\}/(.+?)"?$', raw, flags=re.M):
|
||||
p.append(m.group(1))
|
||||
|
||||
11
glfw/glfw3.h
vendored
11
glfw/glfw3.h
vendored
@@ -3188,13 +3188,9 @@ GLFWAPI void glfwRequestWindowAttention(GLFWwindow* window);
|
||||
/*! @brief Sounds an audible bell associated with the window
|
||||
*
|
||||
* This function sounds an audible bell, on platforms where it is
|
||||
* supported. Currently (macOS, Windows and X11).
|
||||
* supported. Currently (macOS, Windows, X11 and Wayland).
|
||||
*
|
||||
* @param[in] window The window with which the bell is associated.
|
||||
* @param[in] param The meaning of this parameter is platform dependent. On
|
||||
* X11 it corresponds to the percentage controlling bell volume (see man
|
||||
* XBell). On Windows it is the type of sound to make, see the MSDN docs for
|
||||
* MessageBeep. On macOS, it is ignored.
|
||||
* @return GLFW_TRUE if the bell succeeded otherwise GLFW_FALSE
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
|
||||
@@ -3209,7 +3205,7 @@ GLFWAPI void glfwRequestWindowAttention(GLFWwindow* window);
|
||||
*
|
||||
* @ingroup window
|
||||
*/
|
||||
GLFWAPI int glfwWindowBell(GLFWwindow* window, int64_t param);
|
||||
GLFWAPI int glfwWindowBell(GLFWwindow* window);
|
||||
|
||||
|
||||
/*! @brief Returns the monitor that the window uses for full screen mode.
|
||||
@@ -4901,7 +4897,7 @@ GLFWAPI const char* glfwGetGamepadName(int jid);
|
||||
*
|
||||
* Not all devices have all the buttons or axes provided by @ref
|
||||
* GLFWgamepadstate. Unavailable buttons and axes will always report
|
||||
* `GLFW_RELEASE` and 1.0 respectively.
|
||||
* `GLFW_RELEASE` and 0.0 respectively.
|
||||
*
|
||||
* @param[in] jid The [joystick](@ref joysticks) to query.
|
||||
* @param[out] state The gamepad input state of the joystick.
|
||||
@@ -5546,4 +5542,3 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window
|
||||
#endif
|
||||
|
||||
#endif /* _glfw3_h_ */
|
||||
|
||||
|
||||
5
glfw/internal.h
vendored
5
glfw/internal.h
vendored
@@ -128,7 +128,7 @@ typedef enum VkStructureType
|
||||
VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000,
|
||||
VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR = 1000007000,
|
||||
VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000,
|
||||
VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000053000,
|
||||
VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000123000,
|
||||
VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF
|
||||
} VkStructureType;
|
||||
|
||||
@@ -653,7 +653,7 @@ void _glfwPlatformMaximizeWindow(_GLFWwindow* window);
|
||||
void _glfwPlatformShowWindow(_GLFWwindow* window);
|
||||
void _glfwPlatformHideWindow(_GLFWwindow* window);
|
||||
void _glfwPlatformRequestWindowAttention(_GLFWwindow* window);
|
||||
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t);
|
||||
int _glfwPlatformWindowBell(_GLFWwindow* window);
|
||||
void _glfwPlatformFocusWindow(_GLFWwindow* window);
|
||||
void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor,
|
||||
int xpos, int ypos, int width, int height,
|
||||
@@ -769,4 +769,3 @@ void _glfwTerminateVulkan(void);
|
||||
const char* _glfwGetVulkanResultString(VkResult result);
|
||||
|
||||
char* _glfw_strdup(const char* source);
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
|
||||
// Info.plist for unbundled applications
|
||||
// HACK: This assumes that NSOpenGLPixelFormat will remain
|
||||
// a straightforward wrapper of its CGL counterpart
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 100800
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
|
||||
addAttrib(kCGLPFASupportsAutomaticGraphicsSwitching);
|
||||
#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/
|
||||
}
|
||||
|
||||
3
glfw/null_window.c
vendored
3
glfw/null_window.c
vendored
@@ -205,7 +205,7 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
|
||||
{
|
||||
}
|
||||
|
||||
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t param)
|
||||
int _glfwPlatformWindowBell(_GLFWwindow* window)
|
||||
{
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
@@ -323,4 +323,3 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
||||
// This seems like the most appropriate error to return here
|
||||
return VK_ERROR_INITIALIZATION_FAILED;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,12 +55,13 @@
|
||||
"wayland": {
|
||||
"headers": [
|
||||
"wl_platform.h",
|
||||
"linux_joystick.h",
|
||||
"posix_time.h",
|
||||
"posix_thread.h",
|
||||
"xkb_glfw.h",
|
||||
"xkb_unicode.h",
|
||||
"egl_context.h",
|
||||
"osmesa_context.h"
|
||||
"osmesa_context.h",
|
||||
"linux_joystick.h"
|
||||
],
|
||||
"protocols": [
|
||||
"stable/xdg-shell/xdg-shell.xml",
|
||||
@@ -73,12 +74,12 @@
|
||||
"wl_init.c",
|
||||
"wl_monitor.c",
|
||||
"wl_window.c",
|
||||
"linux_joystick.c",
|
||||
"posix_time.c",
|
||||
"posix_thread.c",
|
||||
"xkb_unicode.c",
|
||||
"egl_context.c",
|
||||
"osmesa_context.c"
|
||||
"osmesa_context.c",
|
||||
"linux_joystick.c"
|
||||
]
|
||||
},
|
||||
"wayland_protocols": [
|
||||
|
||||
5
glfw/win32_window.c
vendored
5
glfw/win32_window.c
vendored
@@ -1474,9 +1474,9 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
|
||||
FlashWindow(window->win32.handle, TRUE);
|
||||
}
|
||||
|
||||
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t param)
|
||||
int _glfwPlatformWindowBell(_GLFWwindow* window)
|
||||
{
|
||||
return MessageBeep(0xFFFFFFFF & param) ? GLFW_TRUE : GLFW_FALSE;
|
||||
return MessageBeep(0xFFFFFFFF) ? GLFW_TRUE : GLFW_FALSE;
|
||||
}
|
||||
|
||||
void _glfwPlatformFocusWindow(_GLFWwindow* window)
|
||||
@@ -2014,4 +2014,3 @@ GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle)
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
return window->win32.handle;
|
||||
}
|
||||
|
||||
|
||||
5
glfw/window.c
vendored
5
glfw/window.c
vendored
@@ -768,14 +768,14 @@ GLFWAPI void glfwRequestWindowAttention(GLFWwindow* handle)
|
||||
_glfwPlatformRequestWindowAttention(window);
|
||||
}
|
||||
|
||||
GLFWAPI int glfwWindowBell(GLFWwindow* handle, int64_t param)
|
||||
GLFWAPI int glfwWindowBell(GLFWwindow* handle)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
|
||||
|
||||
return _glfwPlatformWindowBell(window, param);
|
||||
return _glfwPlatformWindowBell(window);
|
||||
}
|
||||
|
||||
GLFWAPI void glfwHideWindow(GLFWwindow* handle)
|
||||
@@ -1106,4 +1106,3 @@ GLFWAPI void glfwPostEmptyEvent(void)
|
||||
|
||||
_glfwPlatformPostEmptyEvent();
|
||||
}
|
||||
|
||||
|
||||
377
glfw/wl_init.c
vendored
377
glfw/wl_init.c
vendored
@@ -32,7 +32,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
@@ -350,16 +349,7 @@ static void keyboardHandleKeymap(void* data,
|
||||
int fd,
|
||||
uint32_t size)
|
||||
{
|
||||
struct xkb_keymap* keymap;
|
||||
struct xkb_state* state;
|
||||
|
||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
||||
struct xkb_compose_table* composeTable;
|
||||
struct xkb_compose_state* composeState;
|
||||
#endif
|
||||
|
||||
char* mapStr;
|
||||
const char* locale;
|
||||
|
||||
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
|
||||
{
|
||||
@@ -372,78 +362,10 @@ static void keyboardHandleKeymap(void* data,
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context,
|
||||
mapStr,
|
||||
XKB_KEYMAP_FORMAT_TEXT_V1,
|
||||
0);
|
||||
xkb_glfw_compile_keymap(mapStr);
|
||||
munmap(mapStr, size);
|
||||
close(fd);
|
||||
|
||||
if (!keymap)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Failed to compile keymap");
|
||||
return;
|
||||
}
|
||||
|
||||
state = xkb_state_new(keymap);
|
||||
if (!state)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Failed to create XKB state");
|
||||
xkb_keymap_unref(keymap);
|
||||
return;
|
||||
}
|
||||
|
||||
// Look up the preferred locale, falling back to "C" as default.
|
||||
locale = getenv("LC_ALL");
|
||||
if (!locale)
|
||||
locale = getenv("LC_CTYPE");
|
||||
if (!locale)
|
||||
locale = getenv("LANG");
|
||||
if (!locale)
|
||||
locale = "C";
|
||||
|
||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
||||
composeTable =
|
||||
xkb_compose_table_new_from_locale(_glfw.wl.xkb.context, locale,
|
||||
XKB_COMPOSE_COMPILE_NO_FLAGS);
|
||||
if (composeTable)
|
||||
{
|
||||
composeState =
|
||||
xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS);
|
||||
xkb_compose_table_unref(composeTable);
|
||||
if (composeState)
|
||||
_glfw.wl.xkb.composeState = composeState;
|
||||
else
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Failed to create XKB compose state");
|
||||
}
|
||||
else
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Failed to create XKB compose table");
|
||||
}
|
||||
#endif
|
||||
|
||||
xkb_keymap_unref(_glfw.wl.xkb.keymap);
|
||||
xkb_state_unref(_glfw.wl.xkb.state);
|
||||
_glfw.wl.xkb.keymap = keymap;
|
||||
_glfw.wl.xkb.state = state;
|
||||
|
||||
_glfw.wl.xkb.controlMask =
|
||||
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Control");
|
||||
_glfw.wl.xkb.altMask =
|
||||
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod1");
|
||||
_glfw.wl.xkb.shiftMask =
|
||||
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift");
|
||||
_glfw.wl.xkb.superMask =
|
||||
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4");
|
||||
_glfw.wl.xkb.capsLockMask =
|
||||
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Lock");
|
||||
_glfw.wl.xkb.numLockMask =
|
||||
1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod2");
|
||||
}
|
||||
|
||||
static void keyboardHandleEnter(void* data,
|
||||
@@ -482,15 +404,6 @@ static void keyboardHandleLeave(void* data,
|
||||
_glfwInputWindowFocus(window, GLFW_FALSE);
|
||||
}
|
||||
|
||||
static int toGLFWKeyCode(uint32_t key)
|
||||
{
|
||||
if (key < sizeof(_glfw.wl.keycodes) / sizeof(_glfw.wl.keycodes[0]))
|
||||
return _glfw.wl.keycodes[key];
|
||||
|
||||
return GLFW_KEY_UNKNOWN;
|
||||
}
|
||||
|
||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
||||
static xkb_keysym_t composeSymbol(xkb_keysym_t sym)
|
||||
{
|
||||
if (sym == XKB_KEY_NoSymbol || !_glfw.wl.xkb.composeState)
|
||||
@@ -510,9 +423,8 @@ static xkb_keysym_t composeSymbol(xkb_keysym_t sym)
|
||||
return sym;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static GLFWbool inputChar(_GLFWwindow* window, uint32_t key)
|
||||
static void inputChar(_GLFWwindow* window, uint32_t key, GLFWbool *shouldRepeat)
|
||||
{
|
||||
uint32_t code, numSyms;
|
||||
long cp;
|
||||
@@ -521,24 +433,23 @@ static GLFWbool inputChar(_GLFWwindow* window, uint32_t key)
|
||||
|
||||
code = key + 8;
|
||||
numSyms = xkb_state_key_get_syms(_glfw.wl.xkb.state, code, &syms);
|
||||
*shouldRepeat = xkb_keymap_key_repeats(_glfw.wl.xkb.keymap, code);
|
||||
|
||||
if (numSyms == 1)
|
||||
{
|
||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
||||
sym = composeSymbol(syms[0]);
|
||||
#else
|
||||
sym = syms[0];
|
||||
#endif
|
||||
cp = _glfwKeySym2Unicode(sym);
|
||||
if (cp != -1)
|
||||
{
|
||||
const int mods = _glfw.wl.xkb.modifiers;
|
||||
const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
|
||||
if (*shouldRepeat) {
|
||||
_glfw.wl.keyRepeatInfo.codepoint = cp;
|
||||
_glfw.wl.keyRepeatInfo.plain = plain;
|
||||
}
|
||||
_glfwInputChar(window, cp, mods, plain);
|
||||
}
|
||||
}
|
||||
|
||||
return xkb_keymap_key_repeats(_glfw.wl.xkb.keymap, syms[0]);
|
||||
}
|
||||
|
||||
static void keyboardHandleKey(void* data,
|
||||
@@ -551,34 +462,32 @@ static void keyboardHandleKey(void* data,
|
||||
int keyCode;
|
||||
int action;
|
||||
_GLFWwindow* window = _glfw.wl.keyboardFocus;
|
||||
GLFWbool shouldRepeat;
|
||||
struct itimerspec timer = {};
|
||||
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
keyCode = toGLFWKeyCode(key);
|
||||
keyCode = xkb_glfw_to_glfw_key_code(key);
|
||||
action = state == WL_KEYBOARD_KEY_STATE_PRESSED
|
||||
? GLFW_PRESS : GLFW_RELEASE;
|
||||
_glfw.wl.keyRepeatInfo.nextRepeatAt = 0;
|
||||
_glfw.wl.keyRepeatInfo.codepoint = -1;
|
||||
|
||||
_glfwInputKey(window, keyCode, key, action,
|
||||
_glfw.wl.xkb.modifiers);
|
||||
|
||||
if (action == GLFW_PRESS)
|
||||
{
|
||||
shouldRepeat = inputChar(window, key);
|
||||
GLFWbool shouldRepeat = GLFW_FALSE;
|
||||
inputChar(window, key, &shouldRepeat);
|
||||
|
||||
if (shouldRepeat && _glfw.wl.keyboardRepeatRate > 0)
|
||||
{
|
||||
_glfw.wl.keyboardLastKey = keyCode;
|
||||
_glfw.wl.keyboardLastScancode = key;
|
||||
timer.it_interval.tv_sec = _glfw.wl.keyboardRepeatRate / 1000;
|
||||
timer.it_interval.tv_nsec = (_glfw.wl.keyboardRepeatRate % 1000) * 1000000;
|
||||
timer.it_value.tv_sec = _glfw.wl.keyboardRepeatDelay / 1000;
|
||||
timer.it_value.tv_nsec = (_glfw.wl.keyboardRepeatDelay % 1000) * 1000000;
|
||||
_glfw.wl.keyRepeatInfo.glfwKeyCode = keyCode;
|
||||
_glfw.wl.keyRepeatInfo.scancode = key;
|
||||
_glfw.wl.keyRepeatInfo.nextRepeatAt = glfwGetTime() + (double)(_glfw.wl.keyboardRepeatDelay) / 1000.0;
|
||||
_glfw.wl.keyRepeatInfo.keyboardFocus = window;
|
||||
}
|
||||
}
|
||||
timerfd_settime(_glfw.wl.timerfd, 0, &timer, NULL);
|
||||
}
|
||||
|
||||
static void keyboardHandleModifiers(void* data,
|
||||
@@ -589,41 +498,9 @@ static void keyboardHandleModifiers(void* data,
|
||||
uint32_t modsLocked,
|
||||
uint32_t group)
|
||||
{
|
||||
xkb_mod_mask_t mask;
|
||||
unsigned int modifiers = 0;
|
||||
|
||||
if (!_glfw.wl.xkb.keymap)
|
||||
return;
|
||||
|
||||
xkb_state_update_mask(_glfw.wl.xkb.state,
|
||||
modsDepressed,
|
||||
modsLatched,
|
||||
modsLocked,
|
||||
0,
|
||||
0,
|
||||
group);
|
||||
|
||||
mask = xkb_state_serialize_mods(_glfw.wl.xkb.state,
|
||||
XKB_STATE_MODS_DEPRESSED |
|
||||
XKB_STATE_LAYOUT_DEPRESSED |
|
||||
XKB_STATE_MODS_LATCHED |
|
||||
XKB_STATE_LAYOUT_LATCHED);
|
||||
if (mask & _glfw.wl.xkb.controlMask)
|
||||
modifiers |= GLFW_MOD_CONTROL;
|
||||
if (mask & _glfw.wl.xkb.altMask)
|
||||
modifiers |= GLFW_MOD_ALT;
|
||||
if (mask & _glfw.wl.xkb.shiftMask)
|
||||
modifiers |= GLFW_MOD_SHIFT;
|
||||
if (mask & _glfw.wl.xkb.superMask)
|
||||
modifiers |= GLFW_MOD_SUPER;
|
||||
if (mask & _glfw.wl.xkb.capsLockMask)
|
||||
modifiers |= GLFW_MOD_CAPS_LOCK;
|
||||
if (mask & _glfw.wl.xkb.numLockMask)
|
||||
modifiers |= GLFW_MOD_NUM_LOCK;
|
||||
_glfw.wl.xkb.modifiers = modifiers;
|
||||
xkb_glfw_update_modifiers(modsDepressed, modsLatched, modsLocked, group);
|
||||
}
|
||||
|
||||
#ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION
|
||||
static void keyboardHandleRepeatInfo(void* data,
|
||||
struct wl_keyboard* keyboard,
|
||||
int32_t rate,
|
||||
@@ -635,7 +512,6 @@ static void keyboardHandleRepeatInfo(void* data,
|
||||
_glfw.wl.keyboardRepeatRate = rate;
|
||||
_glfw.wl.keyboardRepeatDelay = delay;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct wl_keyboard_listener keyboardListener = {
|
||||
keyboardHandleKeymap,
|
||||
@@ -643,9 +519,7 @@ static const struct wl_keyboard_listener keyboardListener = {
|
||||
keyboardHandleLeave,
|
||||
keyboardHandleKey,
|
||||
keyboardHandleModifiers,
|
||||
#ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION
|
||||
keyboardHandleRepeatInfo,
|
||||
#endif
|
||||
};
|
||||
|
||||
static void seatHandleCapabilities(void* data,
|
||||
@@ -798,140 +672,6 @@ static const struct wl_registry_listener registryListener = {
|
||||
registryHandleGlobalRemove
|
||||
};
|
||||
|
||||
// Create key code translation tables
|
||||
//
|
||||
static void createKeyTables(void)
|
||||
{
|
||||
int scancode;
|
||||
|
||||
memset(_glfw.wl.keycodes, -1, sizeof(_glfw.wl.keycodes));
|
||||
memset(_glfw.wl.scancodes, -1, sizeof(_glfw.wl.scancodes));
|
||||
|
||||
_glfw.wl.keycodes[KEY_GRAVE] = GLFW_KEY_GRAVE_ACCENT;
|
||||
_glfw.wl.keycodes[KEY_1] = GLFW_KEY_1;
|
||||
_glfw.wl.keycodes[KEY_2] = GLFW_KEY_2;
|
||||
_glfw.wl.keycodes[KEY_3] = GLFW_KEY_3;
|
||||
_glfw.wl.keycodes[KEY_4] = GLFW_KEY_4;
|
||||
_glfw.wl.keycodes[KEY_5] = GLFW_KEY_5;
|
||||
_glfw.wl.keycodes[KEY_6] = GLFW_KEY_6;
|
||||
_glfw.wl.keycodes[KEY_7] = GLFW_KEY_7;
|
||||
_glfw.wl.keycodes[KEY_8] = GLFW_KEY_8;
|
||||
_glfw.wl.keycodes[KEY_9] = GLFW_KEY_9;
|
||||
_glfw.wl.keycodes[KEY_0] = GLFW_KEY_0;
|
||||
_glfw.wl.keycodes[KEY_SPACE] = GLFW_KEY_SPACE;
|
||||
_glfw.wl.keycodes[KEY_MINUS] = GLFW_KEY_MINUS;
|
||||
_glfw.wl.keycodes[KEY_EQUAL] = GLFW_KEY_EQUAL;
|
||||
_glfw.wl.keycodes[KEY_Q] = GLFW_KEY_Q;
|
||||
_glfw.wl.keycodes[KEY_W] = GLFW_KEY_W;
|
||||
_glfw.wl.keycodes[KEY_E] = GLFW_KEY_E;
|
||||
_glfw.wl.keycodes[KEY_R] = GLFW_KEY_R;
|
||||
_glfw.wl.keycodes[KEY_T] = GLFW_KEY_T;
|
||||
_glfw.wl.keycodes[KEY_Y] = GLFW_KEY_Y;
|
||||
_glfw.wl.keycodes[KEY_U] = GLFW_KEY_U;
|
||||
_glfw.wl.keycodes[KEY_I] = GLFW_KEY_I;
|
||||
_glfw.wl.keycodes[KEY_O] = GLFW_KEY_O;
|
||||
_glfw.wl.keycodes[KEY_P] = GLFW_KEY_P;
|
||||
_glfw.wl.keycodes[KEY_LEFTBRACE] = GLFW_KEY_LEFT_BRACKET;
|
||||
_glfw.wl.keycodes[KEY_RIGHTBRACE] = GLFW_KEY_RIGHT_BRACKET;
|
||||
_glfw.wl.keycodes[KEY_A] = GLFW_KEY_A;
|
||||
_glfw.wl.keycodes[KEY_S] = GLFW_KEY_S;
|
||||
_glfw.wl.keycodes[KEY_D] = GLFW_KEY_D;
|
||||
_glfw.wl.keycodes[KEY_F] = GLFW_KEY_F;
|
||||
_glfw.wl.keycodes[KEY_G] = GLFW_KEY_G;
|
||||
_glfw.wl.keycodes[KEY_H] = GLFW_KEY_H;
|
||||
_glfw.wl.keycodes[KEY_J] = GLFW_KEY_J;
|
||||
_glfw.wl.keycodes[KEY_K] = GLFW_KEY_K;
|
||||
_glfw.wl.keycodes[KEY_L] = GLFW_KEY_L;
|
||||
_glfw.wl.keycodes[KEY_SEMICOLON] = GLFW_KEY_SEMICOLON;
|
||||
_glfw.wl.keycodes[KEY_APOSTROPHE] = GLFW_KEY_APOSTROPHE;
|
||||
_glfw.wl.keycodes[KEY_Z] = GLFW_KEY_Z;
|
||||
_glfw.wl.keycodes[KEY_X] = GLFW_KEY_X;
|
||||
_glfw.wl.keycodes[KEY_C] = GLFW_KEY_C;
|
||||
_glfw.wl.keycodes[KEY_V] = GLFW_KEY_V;
|
||||
_glfw.wl.keycodes[KEY_B] = GLFW_KEY_B;
|
||||
_glfw.wl.keycodes[KEY_N] = GLFW_KEY_N;
|
||||
_glfw.wl.keycodes[KEY_M] = GLFW_KEY_M;
|
||||
_glfw.wl.keycodes[KEY_COMMA] = GLFW_KEY_COMMA;
|
||||
_glfw.wl.keycodes[KEY_DOT] = GLFW_KEY_PERIOD;
|
||||
_glfw.wl.keycodes[KEY_SLASH] = GLFW_KEY_SLASH;
|
||||
_glfw.wl.keycodes[KEY_BACKSLASH] = GLFW_KEY_BACKSLASH;
|
||||
_glfw.wl.keycodes[KEY_ESC] = GLFW_KEY_ESCAPE;
|
||||
_glfw.wl.keycodes[KEY_TAB] = GLFW_KEY_TAB;
|
||||
_glfw.wl.keycodes[KEY_LEFTSHIFT] = GLFW_KEY_LEFT_SHIFT;
|
||||
_glfw.wl.keycodes[KEY_RIGHTSHIFT] = GLFW_KEY_RIGHT_SHIFT;
|
||||
_glfw.wl.keycodes[KEY_LEFTCTRL] = GLFW_KEY_LEFT_CONTROL;
|
||||
_glfw.wl.keycodes[KEY_RIGHTCTRL] = GLFW_KEY_RIGHT_CONTROL;
|
||||
_glfw.wl.keycodes[KEY_LEFTALT] = GLFW_KEY_LEFT_ALT;
|
||||
_glfw.wl.keycodes[KEY_RIGHTALT] = GLFW_KEY_RIGHT_ALT;
|
||||
_glfw.wl.keycodes[KEY_LEFTMETA] = GLFW_KEY_LEFT_SUPER;
|
||||
_glfw.wl.keycodes[KEY_RIGHTMETA] = GLFW_KEY_RIGHT_SUPER;
|
||||
_glfw.wl.keycodes[KEY_MENU] = GLFW_KEY_MENU;
|
||||
_glfw.wl.keycodes[KEY_NUMLOCK] = GLFW_KEY_NUM_LOCK;
|
||||
_glfw.wl.keycodes[KEY_CAPSLOCK] = GLFW_KEY_CAPS_LOCK;
|
||||
_glfw.wl.keycodes[KEY_PRINT] = GLFW_KEY_PRINT_SCREEN;
|
||||
_glfw.wl.keycodes[KEY_SCROLLLOCK] = GLFW_KEY_SCROLL_LOCK;
|
||||
_glfw.wl.keycodes[KEY_PAUSE] = GLFW_KEY_PAUSE;
|
||||
_glfw.wl.keycodes[KEY_DELETE] = GLFW_KEY_DELETE;
|
||||
_glfw.wl.keycodes[KEY_BACKSPACE] = GLFW_KEY_BACKSPACE;
|
||||
_glfw.wl.keycodes[KEY_ENTER] = GLFW_KEY_ENTER;
|
||||
_glfw.wl.keycodes[KEY_HOME] = GLFW_KEY_HOME;
|
||||
_glfw.wl.keycodes[KEY_END] = GLFW_KEY_END;
|
||||
_glfw.wl.keycodes[KEY_PAGEUP] = GLFW_KEY_PAGE_UP;
|
||||
_glfw.wl.keycodes[KEY_PAGEDOWN] = GLFW_KEY_PAGE_DOWN;
|
||||
_glfw.wl.keycodes[KEY_INSERT] = GLFW_KEY_INSERT;
|
||||
_glfw.wl.keycodes[KEY_LEFT] = GLFW_KEY_LEFT;
|
||||
_glfw.wl.keycodes[KEY_RIGHT] = GLFW_KEY_RIGHT;
|
||||
_glfw.wl.keycodes[KEY_DOWN] = GLFW_KEY_DOWN;
|
||||
_glfw.wl.keycodes[KEY_UP] = GLFW_KEY_UP;
|
||||
_glfw.wl.keycodes[KEY_F1] = GLFW_KEY_F1;
|
||||
_glfw.wl.keycodes[KEY_F2] = GLFW_KEY_F2;
|
||||
_glfw.wl.keycodes[KEY_F3] = GLFW_KEY_F3;
|
||||
_glfw.wl.keycodes[KEY_F4] = GLFW_KEY_F4;
|
||||
_glfw.wl.keycodes[KEY_F5] = GLFW_KEY_F5;
|
||||
_glfw.wl.keycodes[KEY_F6] = GLFW_KEY_F6;
|
||||
_glfw.wl.keycodes[KEY_F7] = GLFW_KEY_F7;
|
||||
_glfw.wl.keycodes[KEY_F8] = GLFW_KEY_F8;
|
||||
_glfw.wl.keycodes[KEY_F9] = GLFW_KEY_F9;
|
||||
_glfw.wl.keycodes[KEY_F10] = GLFW_KEY_F10;
|
||||
_glfw.wl.keycodes[KEY_F11] = GLFW_KEY_F11;
|
||||
_glfw.wl.keycodes[KEY_F12] = GLFW_KEY_F12;
|
||||
_glfw.wl.keycodes[KEY_F13] = GLFW_KEY_F13;
|
||||
_glfw.wl.keycodes[KEY_F14] = GLFW_KEY_F14;
|
||||
_glfw.wl.keycodes[KEY_F15] = GLFW_KEY_F15;
|
||||
_glfw.wl.keycodes[KEY_F16] = GLFW_KEY_F16;
|
||||
_glfw.wl.keycodes[KEY_F17] = GLFW_KEY_F17;
|
||||
_glfw.wl.keycodes[KEY_F18] = GLFW_KEY_F18;
|
||||
_glfw.wl.keycodes[KEY_F19] = GLFW_KEY_F19;
|
||||
_glfw.wl.keycodes[KEY_F20] = GLFW_KEY_F20;
|
||||
_glfw.wl.keycodes[KEY_F21] = GLFW_KEY_F21;
|
||||
_glfw.wl.keycodes[KEY_F22] = GLFW_KEY_F22;
|
||||
_glfw.wl.keycodes[KEY_F23] = GLFW_KEY_F23;
|
||||
_glfw.wl.keycodes[KEY_F24] = GLFW_KEY_F24;
|
||||
_glfw.wl.keycodes[KEY_KPSLASH] = GLFW_KEY_KP_DIVIDE;
|
||||
_glfw.wl.keycodes[KEY_KPDOT] = GLFW_KEY_KP_MULTIPLY;
|
||||
_glfw.wl.keycodes[KEY_KPMINUS] = GLFW_KEY_KP_SUBTRACT;
|
||||
_glfw.wl.keycodes[KEY_KPPLUS] = GLFW_KEY_KP_ADD;
|
||||
_glfw.wl.keycodes[KEY_KP0] = GLFW_KEY_KP_0;
|
||||
_glfw.wl.keycodes[KEY_KP1] = GLFW_KEY_KP_1;
|
||||
_glfw.wl.keycodes[KEY_KP2] = GLFW_KEY_KP_2;
|
||||
_glfw.wl.keycodes[KEY_KP3] = GLFW_KEY_KP_3;
|
||||
_glfw.wl.keycodes[KEY_KP4] = GLFW_KEY_KP_4;
|
||||
_glfw.wl.keycodes[KEY_KP5] = GLFW_KEY_KP_5;
|
||||
_glfw.wl.keycodes[KEY_KP6] = GLFW_KEY_KP_6;
|
||||
_glfw.wl.keycodes[KEY_KP7] = GLFW_KEY_KP_7;
|
||||
_glfw.wl.keycodes[KEY_KP8] = GLFW_KEY_KP_8;
|
||||
_glfw.wl.keycodes[KEY_KP9] = GLFW_KEY_KP_9;
|
||||
_glfw.wl.keycodes[KEY_KPCOMMA] = GLFW_KEY_KP_DECIMAL;
|
||||
_glfw.wl.keycodes[KEY_KPEQUAL] = GLFW_KEY_KP_EQUAL;
|
||||
_glfw.wl.keycodes[KEY_KPENTER] = GLFW_KEY_KP_ENTER;
|
||||
|
||||
for (scancode = 0; scancode < 256; scancode++)
|
||||
{
|
||||
if (_glfw.wl.keycodes[scancode] > 0)
|
||||
_glfw.wl.scancodes[_glfw.wl.keycodes[scancode]] = scancode;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW platform API //////
|
||||
@@ -971,53 +711,7 @@ int _glfwPlatformInit(void)
|
||||
_glfw.wl.egl.window_resize = (PFN_wl_egl_window_resize)
|
||||
_glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_resize");
|
||||
|
||||
_glfw.wl.xkb.handle = _glfw_dlopen("libxkbcommon.so.0");
|
||||
if (!_glfw.wl.xkb.handle)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Failed to open libxkbcommon");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
_glfw.wl.xkb.context_new = (PFN_xkb_context_new)
|
||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_new");
|
||||
_glfw.wl.xkb.context_unref = (PFN_xkb_context_unref)
|
||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_unref");
|
||||
_glfw.wl.xkb.keymap_new_from_string = (PFN_xkb_keymap_new_from_string)
|
||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_new_from_string");
|
||||
_glfw.wl.xkb.keymap_unref = (PFN_xkb_keymap_unref)
|
||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_unref");
|
||||
_glfw.wl.xkb.keymap_mod_get_index = (PFN_xkb_keymap_mod_get_index)
|
||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_mod_get_index");
|
||||
_glfw.wl.xkb.keymap_key_repeats = (PFN_xkb_keymap_key_repeats)
|
||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_key_repeats");
|
||||
_glfw.wl.xkb.state_new = (PFN_xkb_state_new)
|
||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_new");
|
||||
_glfw.wl.xkb.state_unref = (PFN_xkb_state_unref)
|
||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_unref");
|
||||
_glfw.wl.xkb.state_key_get_syms = (PFN_xkb_state_key_get_syms)
|
||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_key_get_syms");
|
||||
_glfw.wl.xkb.state_update_mask = (PFN_xkb_state_update_mask)
|
||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_update_mask");
|
||||
_glfw.wl.xkb.state_serialize_mods = (PFN_xkb_state_serialize_mods)
|
||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_serialize_mods");
|
||||
|
||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
||||
_glfw.wl.xkb.compose_table_new_from_locale = (PFN_xkb_compose_table_new_from_locale)
|
||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_new_from_locale");
|
||||
_glfw.wl.xkb.compose_table_unref = (PFN_xkb_compose_table_unref)
|
||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_unref");
|
||||
_glfw.wl.xkb.compose_state_new = (PFN_xkb_compose_state_new)
|
||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_new");
|
||||
_glfw.wl.xkb.compose_state_unref = (PFN_xkb_compose_state_unref)
|
||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_unref");
|
||||
_glfw.wl.xkb.compose_state_feed = (PFN_xkb_compose_state_feed)
|
||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_feed");
|
||||
_glfw.wl.xkb.compose_state_get_status = (PFN_xkb_compose_state_get_status)
|
||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_status");
|
||||
_glfw.wl.xkb.compose_state_get_one_sym = (PFN_xkb_compose_state_get_one_sym)
|
||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_one_sym");
|
||||
#endif
|
||||
load_glfw_xkb();
|
||||
|
||||
_glfw.wl.display = wl_display_connect(NULL);
|
||||
if (!_glfw.wl.display)
|
||||
@@ -1030,15 +724,7 @@ int _glfwPlatformInit(void)
|
||||
_glfw.wl.registry = wl_display_get_registry(_glfw.wl.display);
|
||||
wl_registry_add_listener(_glfw.wl.registry, ®istryListener, NULL);
|
||||
|
||||
createKeyTables();
|
||||
|
||||
_glfw.wl.xkb.context = xkb_context_new(0);
|
||||
if (!_glfw.wl.xkb.context)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Failed to initialize xkb context");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
create_glfw_xkb_context();
|
||||
|
||||
// Sync so we got all registry objects
|
||||
wl_display_roundtrip(_glfw.wl.display);
|
||||
@@ -1046,15 +732,13 @@ int _glfwPlatformInit(void)
|
||||
// Sync so we got all initial output events
|
||||
wl_display_roundtrip(_glfw.wl.display);
|
||||
|
||||
#ifdef __linux__
|
||||
if (!_glfwInitJoysticksLinux())
|
||||
return GLFW_FALSE;
|
||||
#endif
|
||||
|
||||
_glfwInitTimerPOSIX();
|
||||
|
||||
_glfw.wl.timerfd = -1;
|
||||
if (_glfw.wl.seatVersion >= 4)
|
||||
_glfw.wl.timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
|
||||
|
||||
if (_glfw.wl.pointer && _glfw.wl.shm)
|
||||
{
|
||||
_glfw.wl.cursorTheme = wl_cursor_theme_load(NULL, 32, _glfw.wl.shm);
|
||||
@@ -1073,7 +757,9 @@ int _glfwPlatformInit(void)
|
||||
|
||||
void _glfwPlatformTerminate(void)
|
||||
{
|
||||
#ifdef __linux__
|
||||
_glfwTerminateJoysticksLinux();
|
||||
#endif
|
||||
_glfwTerminateEGL();
|
||||
if (_glfw.wl.egl.handle)
|
||||
{
|
||||
@@ -1081,21 +767,7 @@ void _glfwPlatformTerminate(void)
|
||||
_glfw.wl.egl.handle = NULL;
|
||||
}
|
||||
|
||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
||||
if (_glfw.wl.xkb.composeState)
|
||||
xkb_compose_state_unref(_glfw.wl.xkb.composeState);
|
||||
#endif
|
||||
if (_glfw.wl.xkb.keymap)
|
||||
xkb_keymap_unref(_glfw.wl.xkb.keymap);
|
||||
if (_glfw.wl.xkb.state)
|
||||
xkb_state_unref(_glfw.wl.xkb.state);
|
||||
if (_glfw.wl.xkb.context)
|
||||
xkb_context_unref(_glfw.wl.xkb.context);
|
||||
if (_glfw.wl.xkb.handle)
|
||||
{
|
||||
_glfw_dlclose(_glfw.wl.xkb.handle);
|
||||
_glfw.wl.xkb.handle = NULL;
|
||||
}
|
||||
release_glfw_xkb();
|
||||
|
||||
if (_glfw.wl.cursorTheme)
|
||||
wl_cursor_theme_destroy(_glfw.wl.cursorTheme);
|
||||
@@ -1154,4 +826,3 @@ const char* _glfwPlatformGetVersionString(void)
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
104
glfw/wl_platform.h
vendored
104
glfw/wl_platform.h
vendored
@@ -25,10 +25,6 @@
|
||||
//========================================================================
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
||||
#include <xkbcommon/xkbcommon-compose.h>
|
||||
#endif
|
||||
#include <dlfcn.h>
|
||||
|
||||
typedef VkFlags VkWaylandSurfaceCreateFlagsKHR;
|
||||
@@ -47,7 +43,13 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
|
||||
|
||||
#include "posix_thread.h"
|
||||
#include "posix_time.h"
|
||||
#ifdef __linux__
|
||||
#include "linux_joystick.h"
|
||||
#else
|
||||
#include "null_joystick.h"
|
||||
#endif
|
||||
#define GLFW_XKB_GLOBAL_NAME _glfw.wl.xkb
|
||||
#include "xkb_glfw.h"
|
||||
#include "xkb_unicode.h"
|
||||
#include "egl_context.h"
|
||||
#include "osmesa_context.h"
|
||||
@@ -101,46 +103,6 @@ typedef void (* PFN_wl_egl_window_resize)(struct wl_egl_window*, int, int, int,
|
||||
#define wl_egl_window_destroy _glfw.wl.egl.window_destroy
|
||||
#define wl_egl_window_resize _glfw.wl.egl.window_resize
|
||||
|
||||
typedef struct xkb_context* (* PFN_xkb_context_new)(enum xkb_context_flags);
|
||||
typedef void (* PFN_xkb_context_unref)(struct xkb_context*);
|
||||
typedef struct xkb_keymap* (* PFN_xkb_keymap_new_from_string)(struct xkb_context*, const char*, enum xkb_keymap_format, enum xkb_keymap_compile_flags);
|
||||
typedef void (* PFN_xkb_keymap_unref)(struct xkb_keymap*);
|
||||
typedef xkb_mod_index_t (* PFN_xkb_keymap_mod_get_index)(struct xkb_keymap*, const char*);
|
||||
typedef int (* PFN_xkb_keymap_key_repeats)(struct xkb_keymap*, xkb_keycode_t);
|
||||
typedef struct xkb_state* (* PFN_xkb_state_new)(struct xkb_keymap*);
|
||||
typedef void (* PFN_xkb_state_unref)(struct xkb_state*);
|
||||
typedef int (* PFN_xkb_state_key_get_syms)(struct xkb_state*, xkb_keycode_t, const xkb_keysym_t**);
|
||||
typedef enum xkb_state_component (* PFN_xkb_state_update_mask)(struct xkb_state*, xkb_mod_mask_t, xkb_mod_mask_t, xkb_mod_mask_t, xkb_layout_index_t, xkb_layout_index_t, xkb_layout_index_t);
|
||||
typedef xkb_mod_mask_t (* PFN_xkb_state_serialize_mods)(struct xkb_state*, enum xkb_state_component);
|
||||
#define xkb_context_new _glfw.wl.xkb.context_new
|
||||
#define xkb_context_unref _glfw.wl.xkb.context_unref
|
||||
#define xkb_keymap_new_from_string _glfw.wl.xkb.keymap_new_from_string
|
||||
#define xkb_keymap_unref _glfw.wl.xkb.keymap_unref
|
||||
#define xkb_keymap_mod_get_index _glfw.wl.xkb.keymap_mod_get_index
|
||||
#define xkb_keymap_key_repeats _glfw.wl.xkb.keymap_key_repeats
|
||||
#define xkb_state_new _glfw.wl.xkb.state_new
|
||||
#define xkb_state_unref _glfw.wl.xkb.state_unref
|
||||
#define xkb_state_key_get_syms _glfw.wl.xkb.state_key_get_syms
|
||||
#define xkb_state_update_mask _glfw.wl.xkb.state_update_mask
|
||||
#define xkb_state_serialize_mods _glfw.wl.xkb.state_serialize_mods
|
||||
|
||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
||||
typedef struct xkb_compose_table* (* PFN_xkb_compose_table_new_from_locale)(struct xkb_context*, const char*, enum xkb_compose_compile_flags);
|
||||
typedef void (* PFN_xkb_compose_table_unref)(struct xkb_compose_table*);
|
||||
typedef struct xkb_compose_state* (* PFN_xkb_compose_state_new)(struct xkb_compose_table*, enum xkb_compose_state_flags);
|
||||
typedef void (* PFN_xkb_compose_state_unref)(struct xkb_compose_state*);
|
||||
typedef enum xkb_compose_feed_result (* PFN_xkb_compose_state_feed)(struct xkb_compose_state*, xkb_keysym_t);
|
||||
typedef enum xkb_compose_status (* PFN_xkb_compose_state_get_status)(struct xkb_compose_state*);
|
||||
typedef xkb_keysym_t (* PFN_xkb_compose_state_get_one_sym)(struct xkb_compose_state*);
|
||||
#define xkb_compose_table_new_from_locale _glfw.wl.xkb.compose_table_new_from_locale
|
||||
#define xkb_compose_table_unref _glfw.wl.xkb.compose_table_unref
|
||||
#define xkb_compose_state_new _glfw.wl.xkb.compose_state_new
|
||||
#define xkb_compose_state_unref _glfw.wl.xkb.compose_state_unref
|
||||
#define xkb_compose_state_feed _glfw.wl.xkb.compose_state_feed
|
||||
#define xkb_compose_state_get_status _glfw.wl.xkb.compose_state_get_status
|
||||
#define xkb_compose_state_get_one_sym _glfw.wl.xkb.compose_state_get_one_sym
|
||||
#endif
|
||||
|
||||
#define _GLFW_DECORATION_WIDTH 4
|
||||
#define _GLFW_DECORATION_TOP 24
|
||||
#define _GLFW_DECORATION_VERTICAL (_GLFW_DECORATION_TOP + _GLFW_DECORATION_WIDTH)
|
||||
@@ -241,52 +203,15 @@ typedef struct _GLFWlibraryWayland
|
||||
|
||||
int32_t keyboardRepeatRate;
|
||||
int32_t keyboardRepeatDelay;
|
||||
int keyboardLastKey;
|
||||
int keyboardLastScancode;
|
||||
int timerfd;
|
||||
short int keycodes[256];
|
||||
short int scancodes[GLFW_KEY_LAST + 1];
|
||||
|
||||
struct {
|
||||
void* handle;
|
||||
struct xkb_context* context;
|
||||
struct xkb_keymap* keymap;
|
||||
struct xkb_state* state;
|
||||
|
||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
||||
struct xkb_compose_state* composeState;
|
||||
#endif
|
||||
|
||||
xkb_mod_mask_t controlMask;
|
||||
xkb_mod_mask_t altMask;
|
||||
xkb_mod_mask_t shiftMask;
|
||||
xkb_mod_mask_t superMask;
|
||||
xkb_mod_mask_t capsLockMask;
|
||||
xkb_mod_mask_t numLockMask;
|
||||
unsigned int modifiers;
|
||||
|
||||
PFN_xkb_context_new context_new;
|
||||
PFN_xkb_context_unref context_unref;
|
||||
PFN_xkb_keymap_new_from_string keymap_new_from_string;
|
||||
PFN_xkb_keymap_unref keymap_unref;
|
||||
PFN_xkb_keymap_mod_get_index keymap_mod_get_index;
|
||||
PFN_xkb_keymap_key_repeats keymap_key_repeats;
|
||||
PFN_xkb_state_new state_new;
|
||||
PFN_xkb_state_unref state_unref;
|
||||
PFN_xkb_state_key_get_syms state_key_get_syms;
|
||||
PFN_xkb_state_update_mask state_update_mask;
|
||||
PFN_xkb_state_serialize_mods state_serialize_mods;
|
||||
|
||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
||||
PFN_xkb_compose_table_new_from_locale compose_table_new_from_locale;
|
||||
PFN_xkb_compose_table_unref compose_table_unref;
|
||||
PFN_xkb_compose_state_new compose_state_new;
|
||||
PFN_xkb_compose_state_unref compose_state_unref;
|
||||
PFN_xkb_compose_state_feed compose_state_feed;
|
||||
PFN_xkb_compose_state_get_status compose_state_get_status;
|
||||
PFN_xkb_compose_state_get_one_sym compose_state_get_one_sym;
|
||||
#endif
|
||||
} xkb;
|
||||
long codepoint;
|
||||
int plain;
|
||||
int glfwKeyCode;
|
||||
int scancode;
|
||||
double nextRepeatAt;
|
||||
_GLFWwindow* keyboardFocus;
|
||||
} keyRepeatInfo;
|
||||
_GLFWXKBData xkb;
|
||||
|
||||
_GLFWwindow* pointerFocus;
|
||||
_GLFWwindow* keyboardFocus;
|
||||
@@ -336,4 +261,3 @@ typedef struct _GLFWcursorWayland
|
||||
|
||||
|
||||
void _glfwAddOutputWayland(uint32_t name, uint32_t version);
|
||||
|
||||
|
||||
65
glfw/wl_window.c
vendored
65
glfw/wl_window.c
vendored
@@ -35,7 +35,6 @@
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <poll.h>
|
||||
|
||||
|
||||
@@ -690,16 +689,34 @@ static GLFWbool createXdgSurface(_GLFWwindow* window)
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
dispatchPendingKeyRepeats() {
|
||||
if (_glfw.wl.keyRepeatInfo.nextRepeatAt <= 0 || _glfw.wl.keyRepeatInfo.keyboardFocus != _glfw.wl.keyboardFocus || _glfw.wl.keyboardRepeatRate == 0) return;
|
||||
double now = glfwGetTime();
|
||||
const int mods = _glfw.wl.xkb.modifiers;
|
||||
while (_glfw.wl.keyRepeatInfo.nextRepeatAt <= now) {
|
||||
_glfwInputKey(_glfw.wl.keyRepeatInfo.keyboardFocus, _glfw.wl.keyRepeatInfo.glfwKeyCode, _glfw.wl.keyRepeatInfo.scancode, GLFW_REPEAT, mods);
|
||||
if (_glfw.wl.keyRepeatInfo.codepoint > -1) _glfwInputChar(_glfw.wl.keyRepeatInfo.keyboardFocus, _glfw.wl.keyRepeatInfo.codepoint, mods, _glfw.wl.keyRepeatInfo.plain);
|
||||
_glfw.wl.keyRepeatInfo.nextRepeatAt += 1.0 / _glfw.wl.keyboardRepeatRate;
|
||||
now = glfwGetTime();
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
adjustTimeoutForKeyRepeat(int timeout) {
|
||||
if (_glfw.wl.keyRepeatInfo.nextRepeatAt <= 0 || _glfw.wl.keyRepeatInfo.keyboardFocus != _glfw.wl.keyboardFocus || _glfw.wl.keyboardRepeatRate == 0) return timeout;
|
||||
double now = glfwGetTime();
|
||||
if (timeout < 0 || now + timeout / 1000. > _glfw.wl.keyRepeatInfo.nextRepeatAt) {
|
||||
timeout = _glfw.wl.keyRepeatInfo.nextRepeatAt <= now ? 0 : ( (_glfw.wl.keyRepeatInfo.nextRepeatAt - now) * 1000 + 1 );
|
||||
}
|
||||
return timeout;
|
||||
}
|
||||
|
||||
static void
|
||||
handleEvents(int timeout)
|
||||
{
|
||||
struct wl_display* display = _glfw.wl.display;
|
||||
struct pollfd fds[] = {
|
||||
{ wl_display_get_fd(display), POLLIN },
|
||||
{ _glfw.wl.timerfd, POLLIN },
|
||||
};
|
||||
ssize_t read_ret;
|
||||
uint64_t repeats, i;
|
||||
struct pollfd pfd = { wl_display_get_fd(display), POLLIN };
|
||||
|
||||
while (wl_display_prepare_read(display) != 0)
|
||||
wl_display_dispatch_pending(display);
|
||||
@@ -719,9 +736,12 @@ handleEvents(int timeout)
|
||||
return;
|
||||
}
|
||||
|
||||
if (poll(fds, 2, timeout) > 0)
|
||||
dispatchPendingKeyRepeats();
|
||||
timeout = adjustTimeoutForKeyRepeat(timeout);
|
||||
|
||||
if (poll(&pfd, 1, timeout) > 0)
|
||||
{
|
||||
if (fds[0].revents & POLLIN)
|
||||
if (pfd.revents & POLLIN)
|
||||
{
|
||||
wl_display_read_events(display);
|
||||
wl_display_dispatch_pending(display);
|
||||
@@ -731,22 +751,12 @@ handleEvents(int timeout)
|
||||
wl_display_cancel_read(display);
|
||||
}
|
||||
|
||||
if (fds[1].revents & POLLIN)
|
||||
{
|
||||
read_ret = read(_glfw.wl.timerfd, &repeats, sizeof(repeats));
|
||||
if (read_ret != 8)
|
||||
return;
|
||||
|
||||
for (i = 0; i < repeats; ++i)
|
||||
_glfwInputKey(_glfw.wl.keyboardFocus, _glfw.wl.keyboardLastKey,
|
||||
_glfw.wl.keyboardLastScancode, GLFW_REPEAT,
|
||||
_glfw.wl.xkb.modifiers);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wl_display_cancel_read(display);
|
||||
}
|
||||
dispatchPendingKeyRepeats();
|
||||
}
|
||||
|
||||
// Translates a GLFW standard cursor to a theme cursor name
|
||||
@@ -1079,11 +1089,15 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
|
||||
"Wayland: Window attention request not implemented yet");
|
||||
}
|
||||
|
||||
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t param)
|
||||
int _glfwPlatformWindowBell(_GLFWwindow* window)
|
||||
{
|
||||
// TODO
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Window bell request not implemented yet");
|
||||
// TODO: Use an actual Wayland API to implement this when one becomes available
|
||||
int fd = open("/dev/tty", O_WRONLY | O_CLOEXEC);
|
||||
if (fd > -1) {
|
||||
int ret = write(fd, "\x07", 1) == 1 ? GLFW_TRUE : GLFW_FALSE;
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
@@ -1236,7 +1250,7 @@ const char* _glfwPlatformGetScancodeName(int scancode)
|
||||
|
||||
int _glfwPlatformGetKeyScancode(int key)
|
||||
{
|
||||
return _glfw.wl.scancodes[key];
|
||||
return _glfw.wl.xkb.scancodes[key];
|
||||
}
|
||||
|
||||
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
||||
@@ -1547,4 +1561,3 @@ GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle)
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
return window->wl.surface;
|
||||
}
|
||||
|
||||
|
||||
5
glfw/x11_window.c
vendored
5
glfw/x11_window.c
vendored
@@ -2340,9 +2340,9 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
|
||||
0, 1, 0);
|
||||
}
|
||||
|
||||
int _glfwPlatformWindowBell(_GLFWwindow* window, int64_t param)
|
||||
int _glfwPlatformWindowBell(_GLFWwindow* window)
|
||||
{
|
||||
return XkbBell(_glfw.x11.display, window->x11.handle, (int)param, (Atom)0) ? GLFW_TRUE : GLFW_FALSE;
|
||||
return XkbBell(_glfw.x11.display, window->x11.handle, 100, (Atom)0) ? GLFW_TRUE : GLFW_FALSE;
|
||||
}
|
||||
|
||||
void _glfwPlatformFocusWindow(_GLFWwindow* window)
|
||||
@@ -3044,4 +3044,3 @@ GLFWAPI const char* glfwGetX11SelectionString(void)
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
return getSelectionString(_glfw.x11.PRIMARY);
|
||||
}
|
||||
|
||||
|
||||
374
glfw/xkb_glfw.h
vendored
Normal file
374
glfw/xkb_glfw.h
vendored
Normal file
@@ -0,0 +1,374 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 XKB - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2014 Kovid Goyal <kovid@kovidgoyal.net>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would
|
||||
// be appreciated but is not required.
|
||||
//
|
||||
// 2. Altered source versions must be plainly marked as such, and must not
|
||||
// be misrepresented as being the original software.
|
||||
//
|
||||
// 3. This notice may not be removed or altered from any source
|
||||
// distribution.
|
||||
//
|
||||
//========================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <xkbcommon/xkbcommon-compose.h>
|
||||
|
||||
typedef struct xkb_context* (* PFN_xkb_context_new)(enum xkb_context_flags);
|
||||
typedef void (* PFN_xkb_context_unref)(struct xkb_context*);
|
||||
typedef struct xkb_keymap* (* PFN_xkb_keymap_new_from_string)(struct xkb_context*, const char*, enum xkb_keymap_format, enum xkb_keymap_compile_flags);
|
||||
typedef void (* PFN_xkb_keymap_unref)(struct xkb_keymap*);
|
||||
typedef xkb_mod_index_t (* PFN_xkb_keymap_mod_get_index)(struct xkb_keymap*, const char*);
|
||||
typedef int (* PFN_xkb_keymap_key_repeats)(struct xkb_keymap*, xkb_keycode_t);
|
||||
typedef struct xkb_state* (* PFN_xkb_state_new)(struct xkb_keymap*);
|
||||
typedef void (* PFN_xkb_state_unref)(struct xkb_state*);
|
||||
typedef int (* PFN_xkb_state_key_get_syms)(struct xkb_state*, xkb_keycode_t, const xkb_keysym_t**);
|
||||
typedef enum xkb_state_component (* PFN_xkb_state_update_mask)(struct xkb_state*, xkb_mod_mask_t, xkb_mod_mask_t, xkb_mod_mask_t, xkb_layout_index_t, xkb_layout_index_t, xkb_layout_index_t);
|
||||
typedef xkb_mod_mask_t (* PFN_xkb_state_serialize_mods)(struct xkb_state*, enum xkb_state_component);
|
||||
|
||||
#define xkb_context_new GLFW_XKB_GLOBAL_NAME.context_new
|
||||
#define xkb_context_unref GLFW_XKB_GLOBAL_NAME.context_unref
|
||||
#define xkb_keymap_new_from_string GLFW_XKB_GLOBAL_NAME.keymap_new_from_string
|
||||
#define xkb_keymap_unref GLFW_XKB_GLOBAL_NAME.keymap_unref
|
||||
#define xkb_keymap_mod_get_index GLFW_XKB_GLOBAL_NAME.keymap_mod_get_index
|
||||
#define xkb_keymap_key_repeats GLFW_XKB_GLOBAL_NAME.keymap_key_repeats
|
||||
#define xkb_state_new GLFW_XKB_GLOBAL_NAME.state_new
|
||||
#define xkb_state_unref GLFW_XKB_GLOBAL_NAME.state_unref
|
||||
#define xkb_state_key_get_syms GLFW_XKB_GLOBAL_NAME.state_key_get_syms
|
||||
#define xkb_state_update_mask GLFW_XKB_GLOBAL_NAME.state_update_mask
|
||||
#define xkb_state_serialize_mods GLFW_XKB_GLOBAL_NAME.state_serialize_mods
|
||||
|
||||
typedef struct xkb_compose_table* (* PFN_xkb_compose_table_new_from_locale)(struct xkb_context*, const char*, enum xkb_compose_compile_flags);
|
||||
typedef void (* PFN_xkb_compose_table_unref)(struct xkb_compose_table*);
|
||||
typedef struct xkb_compose_state* (* PFN_xkb_compose_state_new)(struct xkb_compose_table*, enum xkb_compose_state_flags);
|
||||
typedef void (* PFN_xkb_compose_state_unref)(struct xkb_compose_state*);
|
||||
typedef enum xkb_compose_feed_result (* PFN_xkb_compose_state_feed)(struct xkb_compose_state*, xkb_keysym_t);
|
||||
typedef enum xkb_compose_status (* PFN_xkb_compose_state_get_status)(struct xkb_compose_state*);
|
||||
typedef xkb_keysym_t (* PFN_xkb_compose_state_get_one_sym)(struct xkb_compose_state*);
|
||||
|
||||
#define xkb_compose_table_new_from_locale GLFW_XKB_GLOBAL_NAME.compose_table_new_from_locale
|
||||
#define xkb_compose_table_unref GLFW_XKB_GLOBAL_NAME.compose_table_unref
|
||||
#define xkb_compose_state_new GLFW_XKB_GLOBAL_NAME.compose_state_new
|
||||
#define xkb_compose_state_unref GLFW_XKB_GLOBAL_NAME.compose_state_unref
|
||||
#define xkb_compose_state_feed GLFW_XKB_GLOBAL_NAME.compose_state_feed
|
||||
#define xkb_compose_state_get_status GLFW_XKB_GLOBAL_NAME.compose_state_get_status
|
||||
#define xkb_compose_state_get_one_sym GLFW_XKB_GLOBAL_NAME.compose_state_get_one_sym
|
||||
|
||||
|
||||
typedef struct {
|
||||
void* handle;
|
||||
struct xkb_context* context;
|
||||
struct xkb_keymap* keymap;
|
||||
struct xkb_state* state;
|
||||
struct xkb_compose_state* composeState;
|
||||
short int keycodes[256];
|
||||
short int scancodes[GLFW_KEY_LAST + 1];
|
||||
|
||||
xkb_mod_mask_t controlMask;
|
||||
xkb_mod_mask_t altMask;
|
||||
xkb_mod_mask_t shiftMask;
|
||||
xkb_mod_mask_t superMask;
|
||||
xkb_mod_mask_t capsLockMask;
|
||||
xkb_mod_mask_t numLockMask;
|
||||
unsigned int modifiers;
|
||||
|
||||
PFN_xkb_context_new context_new;
|
||||
PFN_xkb_context_unref context_unref;
|
||||
PFN_xkb_keymap_new_from_string keymap_new_from_string;
|
||||
PFN_xkb_keymap_unref keymap_unref;
|
||||
PFN_xkb_keymap_mod_get_index keymap_mod_get_index;
|
||||
PFN_xkb_keymap_key_repeats keymap_key_repeats;
|
||||
PFN_xkb_state_new state_new;
|
||||
PFN_xkb_state_unref state_unref;
|
||||
PFN_xkb_state_key_get_syms state_key_get_syms;
|
||||
PFN_xkb_state_update_mask state_update_mask;
|
||||
PFN_xkb_state_serialize_mods state_serialize_mods;
|
||||
|
||||
PFN_xkb_compose_table_new_from_locale compose_table_new_from_locale;
|
||||
PFN_xkb_compose_table_unref compose_table_unref;
|
||||
PFN_xkb_compose_state_new compose_state_new;
|
||||
PFN_xkb_compose_state_unref compose_state_unref;
|
||||
PFN_xkb_compose_state_feed compose_state_feed;
|
||||
PFN_xkb_compose_state_get_status compose_state_get_status;
|
||||
PFN_xkb_compose_state_get_one_sym compose_state_get_one_sym;
|
||||
} _GLFWXKBData;
|
||||
|
||||
#define bind_xkb_sym(name) GLFW_XKB_GLOBAL_NAME.name = (PFN_xkb_##name) _glfw_dlsym(GLFW_XKB_GLOBAL_NAME.handle, "xkb_" #name)
|
||||
#define load_glfw_xkb() {\
|
||||
GLFW_XKB_GLOBAL_NAME.handle = _glfw_dlopen("libxkbcommon.so.0"); \
|
||||
if (!GLFW_XKB_GLOBAL_NAME.handle) \
|
||||
{ \
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, \
|
||||
"Failed to open libxkbcommon"); \
|
||||
return GLFW_FALSE; \
|
||||
} \
|
||||
bind_xkb_sym(context_new); \
|
||||
bind_xkb_sym(context_unref); \
|
||||
bind_xkb_sym(keymap_new_from_string); \
|
||||
bind_xkb_sym(keymap_unref); \
|
||||
bind_xkb_sym(keymap_mod_get_index); \
|
||||
bind_xkb_sym(keymap_key_repeats); \
|
||||
bind_xkb_sym(state_new); \
|
||||
bind_xkb_sym(state_unref); \
|
||||
bind_xkb_sym(state_key_get_syms); \
|
||||
bind_xkb_sym(state_update_mask); \
|
||||
bind_xkb_sym(state_serialize_mods); \
|
||||
bind_xkb_sym(compose_table_new_from_locale); \
|
||||
bind_xkb_sym(compose_table_unref); \
|
||||
bind_xkb_sym(compose_state_new); \
|
||||
bind_xkb_sym(compose_state_unref); \
|
||||
bind_xkb_sym(compose_state_feed); \
|
||||
bind_xkb_sym(compose_state_get_status); \
|
||||
bind_xkb_sym(compose_state_get_one_sym); \
|
||||
}
|
||||
|
||||
#define release_glfw_xkb() {\
|
||||
if (GLFW_XKB_GLOBAL_NAME.composeState) { \
|
||||
xkb_compose_state_unref(GLFW_XKB_GLOBAL_NAME.composeState); \
|
||||
GLFW_XKB_GLOBAL_NAME.composeState = NULL; \
|
||||
} \
|
||||
if (GLFW_XKB_GLOBAL_NAME.keymap) { \
|
||||
xkb_keymap_unref(GLFW_XKB_GLOBAL_NAME.keymap); \
|
||||
GLFW_XKB_GLOBAL_NAME.keymap = NULL; \
|
||||
} \
|
||||
if (GLFW_XKB_GLOBAL_NAME.state) { \
|
||||
xkb_state_unref(GLFW_XKB_GLOBAL_NAME.state); \
|
||||
GLFW_XKB_GLOBAL_NAME.state = NULL; \
|
||||
} \
|
||||
if (GLFW_XKB_GLOBAL_NAME.context) { \
|
||||
xkb_context_unref(GLFW_XKB_GLOBAL_NAME.context); \
|
||||
GLFW_XKB_GLOBAL_NAME.context = NULL; \
|
||||
} \
|
||||
if (GLFW_XKB_GLOBAL_NAME.handle) { \
|
||||
_glfw_dlclose(GLFW_XKB_GLOBAL_NAME.handle); \
|
||||
GLFW_XKB_GLOBAL_NAME.handle = NULL; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define create_glfw_xkb_context() {\
|
||||
GLFW_XKB_GLOBAL_NAME.context = xkb_context_new(0); \
|
||||
if (!GLFW_XKB_GLOBAL_NAME.context) \
|
||||
{ \
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, \
|
||||
"Failed to initialize XKB context"); \
|
||||
return GLFW_FALSE; \
|
||||
} \
|
||||
int scancode; \
|
||||
memset(GLFW_XKB_GLOBAL_NAME.keycodes, -1, sizeof(GLFW_XKB_GLOBAL_NAME.keycodes)); \
|
||||
memset(GLFW_XKB_GLOBAL_NAME.scancodes, -1, sizeof(GLFW_XKB_GLOBAL_NAME.scancodes)); \
|
||||
\
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_GRAVE] = GLFW_KEY_GRAVE_ACCENT; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_1] = GLFW_KEY_1; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_2] = GLFW_KEY_2; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_3] = GLFW_KEY_3; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_4] = GLFW_KEY_4; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_5] = GLFW_KEY_5; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_6] = GLFW_KEY_6; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_7] = GLFW_KEY_7; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_8] = GLFW_KEY_8; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_9] = GLFW_KEY_9; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_0] = GLFW_KEY_0; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_SPACE] = GLFW_KEY_SPACE; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_MINUS] = GLFW_KEY_MINUS; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_EQUAL] = GLFW_KEY_EQUAL; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_Q] = GLFW_KEY_Q; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_W] = GLFW_KEY_W; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_E] = GLFW_KEY_E; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_R] = GLFW_KEY_R; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_T] = GLFW_KEY_T; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_Y] = GLFW_KEY_Y; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_U] = GLFW_KEY_U; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_I] = GLFW_KEY_I; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_O] = GLFW_KEY_O; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_P] = GLFW_KEY_P; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_LEFTBRACE] = GLFW_KEY_LEFT_BRACKET; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_RIGHTBRACE] = GLFW_KEY_RIGHT_BRACKET; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_A] = GLFW_KEY_A; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_S] = GLFW_KEY_S; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_D] = GLFW_KEY_D; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F] = GLFW_KEY_F; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_G] = GLFW_KEY_G; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_H] = GLFW_KEY_H; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_J] = GLFW_KEY_J; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_K] = GLFW_KEY_K; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_L] = GLFW_KEY_L; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_SEMICOLON] = GLFW_KEY_SEMICOLON; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_APOSTROPHE] = GLFW_KEY_APOSTROPHE; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_Z] = GLFW_KEY_Z; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_X] = GLFW_KEY_X; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_C] = GLFW_KEY_C; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_V] = GLFW_KEY_V; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_B] = GLFW_KEY_B; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_N] = GLFW_KEY_N; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_M] = GLFW_KEY_M; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_COMMA] = GLFW_KEY_COMMA; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_DOT] = GLFW_KEY_PERIOD; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_SLASH] = GLFW_KEY_SLASH; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_BACKSLASH] = GLFW_KEY_BACKSLASH; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_ESC] = GLFW_KEY_ESCAPE; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_TAB] = GLFW_KEY_TAB; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_LEFTSHIFT] = GLFW_KEY_LEFT_SHIFT; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_RIGHTSHIFT] = GLFW_KEY_RIGHT_SHIFT; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_LEFTCTRL] = GLFW_KEY_LEFT_CONTROL; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_RIGHTCTRL] = GLFW_KEY_RIGHT_CONTROL; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_LEFTALT] = GLFW_KEY_LEFT_ALT; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_RIGHTALT] = GLFW_KEY_RIGHT_ALT; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_LEFTMETA] = GLFW_KEY_LEFT_SUPER; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_RIGHTMETA] = GLFW_KEY_RIGHT_SUPER; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_MENU] = GLFW_KEY_MENU; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_NUMLOCK] = GLFW_KEY_NUM_LOCK; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_CAPSLOCK] = GLFW_KEY_CAPS_LOCK; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_PRINT] = GLFW_KEY_PRINT_SCREEN; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_SCROLLLOCK] = GLFW_KEY_SCROLL_LOCK; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_PAUSE] = GLFW_KEY_PAUSE; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_DELETE] = GLFW_KEY_DELETE; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_BACKSPACE] = GLFW_KEY_BACKSPACE; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_ENTER] = GLFW_KEY_ENTER; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_HOME] = GLFW_KEY_HOME; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_END] = GLFW_KEY_END; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_PAGEUP] = GLFW_KEY_PAGE_UP; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_PAGEDOWN] = GLFW_KEY_PAGE_DOWN; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_INSERT] = GLFW_KEY_INSERT; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_LEFT] = GLFW_KEY_LEFT; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_RIGHT] = GLFW_KEY_RIGHT; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_DOWN] = GLFW_KEY_DOWN; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_UP] = GLFW_KEY_UP; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F1] = GLFW_KEY_F1; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F2] = GLFW_KEY_F2; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F3] = GLFW_KEY_F3; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F4] = GLFW_KEY_F4; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F5] = GLFW_KEY_F5; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F6] = GLFW_KEY_F6; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F7] = GLFW_KEY_F7; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F8] = GLFW_KEY_F8; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F9] = GLFW_KEY_F9; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F10] = GLFW_KEY_F10; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F11] = GLFW_KEY_F11; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F12] = GLFW_KEY_F12; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F13] = GLFW_KEY_F13; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F14] = GLFW_KEY_F14; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F15] = GLFW_KEY_F15; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F16] = GLFW_KEY_F16; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F17] = GLFW_KEY_F17; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F18] = GLFW_KEY_F18; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F19] = GLFW_KEY_F19; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F20] = GLFW_KEY_F20; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F21] = GLFW_KEY_F21; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F22] = GLFW_KEY_F22; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F23] = GLFW_KEY_F23; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_F24] = GLFW_KEY_F24; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KPSLASH] = GLFW_KEY_KP_DIVIDE; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KPDOT] = GLFW_KEY_KP_MULTIPLY; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KPMINUS] = GLFW_KEY_KP_SUBTRACT; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KPPLUS] = GLFW_KEY_KP_ADD; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KP0] = GLFW_KEY_KP_0; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KP1] = GLFW_KEY_KP_1; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KP2] = GLFW_KEY_KP_2; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KP3] = GLFW_KEY_KP_3; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KP4] = GLFW_KEY_KP_4; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KP5] = GLFW_KEY_KP_5; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KP6] = GLFW_KEY_KP_6; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KP7] = GLFW_KEY_KP_7; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KP8] = GLFW_KEY_KP_8; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KP9] = GLFW_KEY_KP_9; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KPCOMMA] = GLFW_KEY_KP_DECIMAL; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KPEQUAL] = GLFW_KEY_KP_EQUAL; \
|
||||
GLFW_XKB_GLOBAL_NAME.keycodes[KEY_KPENTER] = GLFW_KEY_KP_ENTER; \
|
||||
\
|
||||
for (scancode = 0; scancode < 256; scancode++) \
|
||||
{ \
|
||||
if (GLFW_XKB_GLOBAL_NAME.keycodes[scancode] > 0) \
|
||||
GLFW_XKB_GLOBAL_NAME.scancodes[GLFW_XKB_GLOBAL_NAME.keycodes[scancode]] = scancode; \
|
||||
} \
|
||||
\
|
||||
}
|
||||
|
||||
|
||||
#define xkb_glfw_compile_keymap(map_str) { \
|
||||
const char* locale = NULL; \
|
||||
struct xkb_state* state = NULL; \
|
||||
struct xkb_keymap* keymap = NULL; \
|
||||
struct xkb_compose_table* compose_table = NULL; \
|
||||
struct xkb_compose_state* compose_state = NULL; \
|
||||
\
|
||||
keymap = xkb_keymap_new_from_string(GLFW_XKB_GLOBAL_NAME.context, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, 0); \
|
||||
if (!keymap) _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to compile XKB keymap"); \
|
||||
else { \
|
||||
state = xkb_state_new(keymap); \
|
||||
if (!state) { \
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create XKB state"); \
|
||||
xkb_keymap_unref(keymap); keymap = NULL; \
|
||||
} else { \
|
||||
/* Look up the preferred locale, falling back to "C" as default. */ \
|
||||
locale = getenv("LC_ALL"); \
|
||||
if (!locale) locale = getenv("LC_CTYPE"); \
|
||||
if (!locale) locale = getenv("LANG"); \
|
||||
if (!locale) locale = "C"; \
|
||||
compose_table = xkb_compose_table_new_from_locale(GLFW_XKB_GLOBAL_NAME.context, locale, XKB_COMPOSE_COMPILE_NO_FLAGS); \
|
||||
if (!compose_table) { \
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create XKB compose table"); \
|
||||
xkb_keymap_unref(keymap); keymap = NULL; \
|
||||
xkb_state_unref(state); state = NULL; \
|
||||
} else { \
|
||||
compose_state = xkb_compose_state_new(compose_table, XKB_COMPOSE_STATE_NO_FLAGS); \
|
||||
xkb_compose_table_unref(compose_table); compose_table = NULL; \
|
||||
if (!compose_state) { \
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create XKB compose state"); \
|
||||
xkb_keymap_unref(keymap); keymap = NULL; \
|
||||
xkb_state_unref(state); state = NULL; \
|
||||
}\
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
if (keymap && state && compose_state) { \
|
||||
if (GLFW_XKB_GLOBAL_NAME.composeState) xkb_compose_state_unref(GLFW_XKB_GLOBAL_NAME.composeState); \
|
||||
GLFW_XKB_GLOBAL_NAME.composeState = compose_state; \
|
||||
if (GLFW_XKB_GLOBAL_NAME.keymap) xkb_keymap_unref(GLFW_XKB_GLOBAL_NAME.keymap); \
|
||||
GLFW_XKB_GLOBAL_NAME.keymap = keymap; \
|
||||
if (GLFW_XKB_GLOBAL_NAME.state) xkb_state_unref(GLFW_XKB_GLOBAL_NAME.state); \
|
||||
GLFW_XKB_GLOBAL_NAME.state = state; \
|
||||
}\
|
||||
if (GLFW_XKB_GLOBAL_NAME.keymap) { \
|
||||
GLFW_XKB_GLOBAL_NAME.controlMask = 1 << xkb_keymap_mod_get_index(GLFW_XKB_GLOBAL_NAME.keymap, "Control"); \
|
||||
GLFW_XKB_GLOBAL_NAME.altMask = 1 << xkb_keymap_mod_get_index(GLFW_XKB_GLOBAL_NAME.keymap, "Mod1"); \
|
||||
GLFW_XKB_GLOBAL_NAME.shiftMask = 1 << xkb_keymap_mod_get_index(GLFW_XKB_GLOBAL_NAME.keymap, "Shift"); \
|
||||
GLFW_XKB_GLOBAL_NAME.superMask = 1 << xkb_keymap_mod_get_index(GLFW_XKB_GLOBAL_NAME.keymap, "Mod4"); \
|
||||
GLFW_XKB_GLOBAL_NAME.capsLockMask = 1 << xkb_keymap_mod_get_index(GLFW_XKB_GLOBAL_NAME.keymap, "Lock"); \
|
||||
GLFW_XKB_GLOBAL_NAME.numLockMask = 1 << xkb_keymap_mod_get_index(GLFW_XKB_GLOBAL_NAME.keymap, "Mod2"); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
#define xkb_glfw_update_modifiers(depressed, latched, locked, group) {\
|
||||
xkb_mod_mask_t mask; \
|
||||
unsigned int modifiers = 0; \
|
||||
if (!GLFW_XKB_GLOBAL_NAME.keymap) return; \
|
||||
xkb_state_update_mask(GLFW_XKB_GLOBAL_NAME.state, depressed, latched, locked, 0, 0, group); \
|
||||
mask = xkb_state_serialize_mods(GLFW_XKB_GLOBAL_NAME.state, XKB_STATE_MODS_DEPRESSED | XKB_STATE_LAYOUT_DEPRESSED | XKB_STATE_MODS_LATCHED | XKB_STATE_LAYOUT_LATCHED); \
|
||||
if (mask & GLFW_XKB_GLOBAL_NAME.controlMask) modifiers |= GLFW_MOD_CONTROL; \
|
||||
if (mask & GLFW_XKB_GLOBAL_NAME.altMask) modifiers |= GLFW_MOD_ALT; \
|
||||
if (mask & GLFW_XKB_GLOBAL_NAME.shiftMask) modifiers |= GLFW_MOD_SHIFT; \
|
||||
if (mask & GLFW_XKB_GLOBAL_NAME.superMask) modifiers |= GLFW_MOD_SUPER; \
|
||||
if (mask & GLFW_XKB_GLOBAL_NAME.capsLockMask) modifiers |= GLFW_MOD_CAPS_LOCK; \
|
||||
if (mask & GLFW_XKB_GLOBAL_NAME.numLockMask) modifiers |= GLFW_MOD_NUM_LOCK; \
|
||||
GLFW_XKB_GLOBAL_NAME.modifiers = modifiers; \
|
||||
}
|
||||
|
||||
|
||||
#define xkb_glfw_to_glfw_key_code(key) \
|
||||
((key < sizeof(GLFW_XKB_GLOBAL_NAME.keycodes) / sizeof(GLFW_XKB_GLOBAL_NAME.keycodes[0])) ? GLFW_XKB_GLOBAL_NAME.keycodes[key] : GLFW_KEY_UNKNOWN)
|
||||
0
kittens/ask/__init__.py
Normal file
0
kittens/ask/__init__.py
Normal file
114
kittens/ask/main.py
Normal file
114
kittens/ask/main.py
Normal file
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import json
|
||||
import os
|
||||
import readline
|
||||
import sys
|
||||
|
||||
from kitty.cli import parse_args
|
||||
from kitty.constants import cache_dir
|
||||
|
||||
from ..tui.operations import alternate_screen, styled
|
||||
|
||||
|
||||
def get_history_items():
|
||||
return list(map(readline.get_history_item, range(1, readline.get_current_history_length() + 1)))
|
||||
|
||||
|
||||
def sort_key(item):
|
||||
return len(item), item.lower()
|
||||
|
||||
|
||||
class HistoryCompleter:
|
||||
|
||||
def __init__(self, name=None):
|
||||
self.matches = []
|
||||
self.history_path = None
|
||||
if name:
|
||||
ddir = os.path.join(cache_dir(), 'ask')
|
||||
try:
|
||||
os.makedirs(ddir)
|
||||
except FileExistsError:
|
||||
pass
|
||||
self.history_path = os.path.join(ddir, name)
|
||||
|
||||
def complete(self, text, state):
|
||||
response = None
|
||||
if state == 0:
|
||||
history_values = get_history_items()
|
||||
if text:
|
||||
self.matches = sorted(
|
||||
(h for h in history_values if h and h.startswith(text)), key=sort_key)
|
||||
else:
|
||||
self.matches = []
|
||||
try:
|
||||
response = self.matches[state]
|
||||
except IndexError:
|
||||
response = None
|
||||
return response
|
||||
|
||||
def __enter__(self):
|
||||
if self.history_path:
|
||||
if os.path.exists(self.history_path):
|
||||
readline.read_history_file(self.history_path)
|
||||
readline.set_completer(self.complete)
|
||||
readline.parse_and_bind('tab: complete')
|
||||
return self
|
||||
|
||||
def __exit__(self, *a):
|
||||
if self.history_path:
|
||||
readline.write_history_file(self.history_path)
|
||||
|
||||
|
||||
def option_text():
|
||||
return '''\
|
||||
--type -t
|
||||
choices=line
|
||||
default=line
|
||||
Type of input. Defaults to asking for a line of text.
|
||||
|
||||
|
||||
--message -m
|
||||
The message to display to the user. If not specified a default
|
||||
message is shown.
|
||||
|
||||
|
||||
--name -n
|
||||
The name for this question. Used to store history of previous answers which can
|
||||
be used for completions and via the browse history readline bindings.
|
||||
'''
|
||||
|
||||
|
||||
def real_main(args):
|
||||
msg = 'Ask the user for input'
|
||||
try:
|
||||
args, items = parse_args(args[1:], option_text, '', msg, 'kitty ask')
|
||||
except SystemExit as e:
|
||||
print(e.args[0])
|
||||
input('Press enter to quit...')
|
||||
raise SystemExit(1)
|
||||
|
||||
readline.read_init_file()
|
||||
|
||||
with alternate_screen(), HistoryCompleter(args.name):
|
||||
if args.message:
|
||||
print(styled(args.message, bold=True))
|
||||
|
||||
prompt = '> '
|
||||
try:
|
||||
ans = input(prompt)
|
||||
except (KeyboardInterrupt, EOFError):
|
||||
return
|
||||
print('OK:', json.dumps(ans))
|
||||
|
||||
|
||||
def main(args=sys.argv):
|
||||
try:
|
||||
real_main(args)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
input('Press enter to quit...')
|
||||
raise SystemExit(1)
|
||||
@@ -2,6 +2,7 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import sys
|
||||
from contextlib import contextmanager
|
||||
|
||||
from kitty.terminfo import string_capabilities
|
||||
@@ -151,3 +152,11 @@ def cursor(write):
|
||||
write(SAVE_CURSOR)
|
||||
yield
|
||||
write(RESTORE_CURSOR)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def alternate_screen(f=None):
|
||||
f = f or sys.stdout
|
||||
print(set_mode('ALTERNATE_SCREEN'), end='', file=f)
|
||||
yield
|
||||
print(reset_mode('ALTERNATE_SCREEN'), end='', file=f)
|
||||
|
||||
@@ -231,7 +231,7 @@ expression instead.
|
||||
|
||||
--url-prefixes
|
||||
default={0}
|
||||
Comma separeted list of recognized URL prefixes. Defaults to:
|
||||
Comma separated list of recognized URL prefixes. Defaults to:
|
||||
{0}
|
||||
'''.format, ','.join(sorted(URL_PREFIXES)))
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import atexit
|
||||
import json
|
||||
import re
|
||||
import socket
|
||||
from functools import partial
|
||||
@@ -204,7 +205,6 @@ class Boss:
|
||||
return response
|
||||
|
||||
def peer_message_received(self, msg):
|
||||
import json
|
||||
msg = msg.decode('utf-8')
|
||||
cmd_prefix = '\x1bP@kitty-cmd'
|
||||
if msg.startswith(cmd_prefix):
|
||||
@@ -435,10 +435,17 @@ class Boss:
|
||||
overlay_for=w.id))
|
||||
overlay_window.action_on_close = partial(self.send_unicode_character, w.id)
|
||||
|
||||
def get_output(self, source_window, num_lines=1):
|
||||
output = ''
|
||||
s = source_window.screen
|
||||
for i in range(min(num_lines, s.lines)):
|
||||
output += str(s.linebuf.line(i))
|
||||
return output
|
||||
|
||||
def send_unicode_character(self, target_window_id, source_window):
|
||||
w = self.window_id_map.get(target_window_id)
|
||||
if w is not None:
|
||||
output = str(source_window.screen.linebuf.line(0))
|
||||
output = self.get_output(source_window)
|
||||
if output.startswith('OK: '):
|
||||
try:
|
||||
text = chr(int(output.partition(' ')[2], 16))
|
||||
@@ -448,6 +455,28 @@ class Boss:
|
||||
else:
|
||||
w.paste(text)
|
||||
|
||||
def set_tab_title(self):
|
||||
w = self.active_window
|
||||
tab = self.active_tab
|
||||
if w is not None and tab is not None and w.overlay_for is None:
|
||||
args = ['--name=tab-title', '--message', _('Enter the new title for this tab below.')]
|
||||
overlay_window = tab.new_special_window(
|
||||
SpecialWindow(
|
||||
['kitty', '+runpy', 'from kittens.ask.main import main; main()'] + args,
|
||||
overlay_for=w.id))
|
||||
overlay_window.action_on_close = partial(self.do_set_tab_title, tab.id)
|
||||
|
||||
def do_set_tab_title(self, tab_id, source_window):
|
||||
output = self.get_output(source_window)
|
||||
if output.startswith('OK: '):
|
||||
title = json.loads(output.partition(' ')[2].strip())
|
||||
tm = self.active_tab_manager
|
||||
if tm is not None and title:
|
||||
for tab in tm.tabs:
|
||||
if tab.id == tab_id:
|
||||
tab.set_title(title)
|
||||
break
|
||||
|
||||
def run_simple_kitten(self, type_of_input, kitten, *args):
|
||||
import shlex
|
||||
w = self.active_window
|
||||
|
||||
@@ -6,9 +6,7 @@
|
||||
layout(std140) uniform CellRenderData {
|
||||
float xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity;
|
||||
|
||||
uint default_fg, default_bg, highlight_fg, highlight_bg, cursor_color, url_color, url_style;
|
||||
|
||||
int color1, color2;
|
||||
uint default_fg, default_bg, highlight_fg, highlight_bg, cursor_color, url_color, url_style, inverted;
|
||||
|
||||
uint xnum, ynum, cursor_x, cursor_y, cursor_w;
|
||||
|
||||
@@ -21,7 +19,7 @@ layout(location=1) in uvec4 sprite_coords;
|
||||
layout(location=2) in uint is_selected;
|
||||
|
||||
|
||||
|
||||
const int fg_index_map[] = int[3](0, 1, 0);
|
||||
const uvec2 cell_pos_map[] = uvec2[4](
|
||||
uvec2(1, 0), // right, top
|
||||
uvec2(1, 1), // right, bottom
|
||||
@@ -135,10 +133,10 @@ void main() {
|
||||
|
||||
// set cell color indices {{{
|
||||
uvec2 default_colors = uvec2(default_fg, default_bg);
|
||||
ivec2 color_indices = ivec2(color1, color2);
|
||||
uint text_attrs = sprite_coords[3];
|
||||
int fg_index = color_indices[(text_attrs >> 6) & REVERSE_MASK];
|
||||
int bg_index = color_indices[1 - fg_index];
|
||||
uint is_inverted = ((text_attrs >> 6) & REVERSE_MASK) + inverted;
|
||||
int fg_index = fg_index_map[is_inverted];
|
||||
int bg_index = 1 - fg_index;
|
||||
float cursor = is_cursor(c, r);
|
||||
vec3 bg = to_color(colors[bg_index], default_colors[bg_index]);
|
||||
// }}}
|
||||
|
||||
114
kitty/cli.py
114
kitty/cli.py
@@ -2,16 +2,16 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from collections import deque
|
||||
|
||||
from .config import load_config
|
||||
from .constants import appname, defconf, is_macos, str_version
|
||||
from .config import defaults, load_config
|
||||
from .constants import appname, defconf, is_macos, is_wayland, str_version
|
||||
from .layout import all_layouts
|
||||
|
||||
is_macos
|
||||
OPTIONS = '''
|
||||
--class
|
||||
dest=cls
|
||||
@@ -32,9 +32,21 @@ only use this if you are running a program that does not set titles.
|
||||
|
||||
--config
|
||||
type=list
|
||||
default={config_path}
|
||||
Specify a path to the configuration file(s) to use.
|
||||
Can be specified multiple times to read multiple configuration files in sequence, which are merged.
|
||||
Can be specified multiple times to read multiple configuration files in
|
||||
sequence, which are merged. Use the special value NONE to not load a config
|
||||
file.
|
||||
|
||||
If this option is not specified, config files are searched for in the order:
|
||||
"$XDG_CONFIG_HOME/kitty/kitty.conf", "~/.config/kitty/kitty.conf", {macos_confpath}
|
||||
"$XDG_CONFIG_DIRS/kitty/kitty.conf". The first one that exists is used as the
|
||||
config file.
|
||||
|
||||
If the environment variable "KITTY_CONFIG_DIRECTORY" is specified, that
|
||||
directory is always used and the above searching does not happen.
|
||||
|
||||
If "/etc/xdg/kitty/kitty.conf" exists it is used as a base config file onto
|
||||
which any user config files are merged.
|
||||
|
||||
|
||||
--override -o
|
||||
@@ -122,6 +134,11 @@ Debug OpenGL commands. This will cause all OpenGL calls to check for errors inst
|
||||
--debug-font-fallback
|
||||
type=bool-set
|
||||
Print out information about the selection of fallback fonts for characters not present in the main font.
|
||||
|
||||
|
||||
--debug-config
|
||||
type=bool-set
|
||||
Print out information about the system and kitty configuration.
|
||||
'''
|
||||
|
||||
|
||||
@@ -201,10 +218,10 @@ def parse_option_spec(spec=OPTIONS):
|
||||
current_cmd['choices'] = {x.strip() for x in current_cmd['choices'].split(',')}
|
||||
elif state is HELP:
|
||||
if line:
|
||||
current_cmd['help'] += ' ' + line
|
||||
current_cmd['help'] += ' ' + line.lstrip()
|
||||
else:
|
||||
if prev_line:
|
||||
current_cmd['help'] += '\n'
|
||||
current_cmd['help'] += '\n\n\t'
|
||||
else:
|
||||
state = NORMAL
|
||||
(seq if current_cmd.get('condition', True) else disabled).append(current_cmd)
|
||||
@@ -453,7 +470,7 @@ def parse_cmdline(oc, disabled, args=None):
|
||||
def options_spec():
|
||||
if not hasattr(options_spec, 'ans'):
|
||||
options_spec.ans = OPTIONS.format(
|
||||
appname=appname, config_path=defconf,
|
||||
appname=appname, macos_confpath='~/Library/Preferences/kitty/kitty.conf' if is_macos else '',
|
||||
window_layout_choices=', '.join(all_layouts)
|
||||
)
|
||||
return options_spec.ans
|
||||
@@ -466,8 +483,85 @@ def parse_args(args=None, ospec=options_spec, usage=None, message=None, appname=
|
||||
return parse_cmdline(oc, disabled, args=args)
|
||||
|
||||
|
||||
def create_opts(args):
|
||||
config = args.config or (defconf, )
|
||||
SYSTEM_CONF = '/etc/xdg/kitty/kitty.conf'
|
||||
|
||||
|
||||
def resolve_config(config_files_on_cmd_line):
|
||||
if config_files_on_cmd_line:
|
||||
if 'NONE' not in config_files_on_cmd_line:
|
||||
yield SYSTEM_CONF
|
||||
for cf in config_files_on_cmd_line:
|
||||
yield cf
|
||||
else:
|
||||
yield SYSTEM_CONF
|
||||
yield defconf
|
||||
|
||||
|
||||
def print_shortcut(key, action):
|
||||
if not getattr(print_shortcut, 'maps', None):
|
||||
from kitty.keys import defines
|
||||
v = vars(defines)
|
||||
mmap = {m.split('_')[-1].lower(): x for m, x in v.items() if m.startswith('GLFW_MOD_')}
|
||||
kmap = {k.split('_')[-1].lower(): x for k, x in v.items() if k.startswith('GLFW_KEY_')}
|
||||
krmap = {v: k for k, v in kmap.items()}
|
||||
print_shortcut.maps = mmap, krmap
|
||||
mmap, krmap = print_shortcut.maps
|
||||
names = []
|
||||
mods, key = key
|
||||
for name, val in mmap.items():
|
||||
if mods & val:
|
||||
names.append(name)
|
||||
if key:
|
||||
names.append(krmap[key])
|
||||
print('\t', '+'.join(names), action)
|
||||
|
||||
|
||||
def compare_keymaps(final, initial):
|
||||
added = set(final) - set(initial)
|
||||
removed = set(initial) - set(final)
|
||||
changed = {k for k in set(final) & set(initial) if final[k] != initial[k]}
|
||||
if added:
|
||||
print(title('Added shortcuts:'))
|
||||
for k in added:
|
||||
print_shortcut(k, final[k])
|
||||
if removed:
|
||||
print(title('Removed shortcuts:'))
|
||||
for k in removed:
|
||||
print_shortcut(k, initial[k])
|
||||
if changed:
|
||||
print(title('Changed shortcuts:'))
|
||||
for k in changed:
|
||||
print_shortcut(k, final[k])
|
||||
|
||||
|
||||
def compare_opts(opts):
|
||||
print('\nConfig options different from defaults:')
|
||||
for f in sorted(defaults._fields):
|
||||
if getattr(opts, f) != getattr(defaults, f):
|
||||
if f == 'keymap':
|
||||
compare_keymaps(opts.keymap, defaults.keymap)
|
||||
else:
|
||||
print(title('{:20s}'.format(f)), getattr(opts, f))
|
||||
|
||||
|
||||
def create_opts(args, debug_config=False):
|
||||
config = tuple(resolve_config(args.config))
|
||||
if debug_config:
|
||||
print(version())
|
||||
print(' '.join(os.uname()))
|
||||
if is_macos:
|
||||
print(' '.join(subprocess.check_output(['sw_vers']).decode('utf-8').splitlines()).strip())
|
||||
else:
|
||||
print('Running under:', green('Wayland' if is_wayland else 'X11'))
|
||||
if os.path.exists('/etc/issue'):
|
||||
print(open('/etc/issue', encoding='utf-8', errors='replace').read().strip())
|
||||
if os.path.exists('/etc/lsb-release'):
|
||||
print(open('/etc/lsb-release', encoding='utf-8', errors='replace').read().strip())
|
||||
config = tuple(x for x in config if os.path.exists(x))
|
||||
if config:
|
||||
print(green('Loaded config files:'), ', '.join(config))
|
||||
overrides = (a.replace('=', ' ', 1) for a in args.override or ())
|
||||
opts = load_config(*config, overrides=overrides)
|
||||
if debug_config:
|
||||
compare_opts(opts)
|
||||
return opts
|
||||
|
||||
@@ -50,7 +50,7 @@ def to_cursor_shape(x):
|
||||
def parse_mods(parts):
|
||||
|
||||
def map_mod(m):
|
||||
return {'CTRL': 'CONTROL', 'CMD': 'SUPER', 'OPTION': 'ALT'}.get(m, m)
|
||||
return {'CTRL': 'CONTROL', 'CMD': 'SUPER', '⌘': 'SUPER', '⌥': 'ALT', 'OPTION': 'ALT'}.get(m, m)
|
||||
|
||||
mods = 0
|
||||
for m in parts:
|
||||
@@ -293,7 +293,6 @@ type_map = {
|
||||
'macos_option_as_alt': to_bool,
|
||||
'macos_titlebar_color': macos_titlebar_color,
|
||||
'box_drawing_scale': box_drawing_scale,
|
||||
'x11_bell_volume': int,
|
||||
'background_opacity': unit_float,
|
||||
'tab_separator': tab_separator,
|
||||
'active_tab_font_style': tab_font_style,
|
||||
|
||||
@@ -11,7 +11,7 @@ from collections import namedtuple
|
||||
from .fast_data_types import set_boss as set_c_boss
|
||||
|
||||
appname = 'kitty'
|
||||
version = (0, 8, 2)
|
||||
version = (0, 8, 4)
|
||||
str_version = '.'.join(map(str, version))
|
||||
_plat = sys.platform.lower()
|
||||
is_macos = 'darwin' in _plat
|
||||
@@ -27,7 +27,22 @@ def _get_config_dir():
|
||||
if 'KITTY_CONFIG_DIRECTORY' in os.environ:
|
||||
return os.path.abspath(os.path.expanduser(os.environ['KITTY_CONFIG_DIRECTORY']))
|
||||
|
||||
candidate = os.path.abspath(os.path.expanduser(os.environ.get('XDG_CONFIG_HOME') or ('~/Library/Preferences' if is_macos else '~/.config')))
|
||||
locations = []
|
||||
if 'XDG_CONFIG_HOME' in os.environ:
|
||||
locations.append(os.path.abspath(os.path.expanduser(os.environ['XDG_CONFIG_HOME'])))
|
||||
locations.append(os.path.expanduser('~/.config'))
|
||||
if is_macos:
|
||||
locations.append(os.path.expanduser('~/Library/Preferences'))
|
||||
if 'XDG_CONFIG_DIRS' in os.environ:
|
||||
for loc in os.environ['XDG_CONFIG_DIRS'].split(os.pathsep):
|
||||
locations.append(os.path.abspath(os.path.expanduser(loc)))
|
||||
for loc in locations:
|
||||
if loc:
|
||||
q = os.path.join(loc, appname)
|
||||
if os.access(q, os.W_OK) and os.path.exists(os.path.join(q, 'kitty.conf')):
|
||||
return q
|
||||
|
||||
candidate = os.path.abspath(os.path.expanduser(os.environ.get('XDG_CONFIG_HOME') or '~/.config'))
|
||||
ans = os.path.join(candidate, appname)
|
||||
os.makedirs(ans, exist_ok=True)
|
||||
return ans
|
||||
@@ -46,10 +61,7 @@ def _get_cache_dir():
|
||||
else:
|
||||
candidate = os.environ.get('XDG_CACHE_HOME', '~/.cache')
|
||||
candidate = os.path.join(os.path.expanduser(candidate), appname)
|
||||
try:
|
||||
os.makedirs(candidate)
|
||||
except FileExistsError:
|
||||
pass
|
||||
os.makedirs(candidate, exist_ok=True)
|
||||
return candidate
|
||||
|
||||
|
||||
|
||||
@@ -279,7 +279,7 @@ calc_cell_width(Face *self) {
|
||||
for (char_type i = 32; i < 128; i++) {
|
||||
int glyph_index = FT_Get_Char_Index(self->face, i);
|
||||
if (load_glyph(self, glyph_index, FT_LOAD_DEFAULT)) {
|
||||
ans = MAX(ans, (unsigned long)ceilf((float)self->face->glyph->metrics.horiAdvance / 64.f));
|
||||
ans = MAX(ans, (unsigned int)ceilf((float)self->face->glyph->metrics.horiAdvance / 64.f));
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
@@ -324,7 +324,6 @@ typedef struct {
|
||||
unsigned int factor, right_edge;
|
||||
} ProcessedBitmap;
|
||||
|
||||
|
||||
static inline void
|
||||
trim_borders(ProcessedBitmap *ans, size_t extra) {
|
||||
bool column_has_text = false;
|
||||
@@ -354,9 +353,13 @@ render_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_
|
||||
ans->rows = bitmap->rows;
|
||||
ans->pixel_mode = bitmap->pixel_mode;
|
||||
if (ans->width > max_width) {
|
||||
size_t extra = bitmap->width - max_width;
|
||||
size_t extra = ans->width - max_width;
|
||||
if (italic && extra < cell_width / 2) {
|
||||
trim_borders(ans, extra);
|
||||
} else if (extra == 2 && num_cells == 1) {
|
||||
// there exist fonts that have bitmaps just a couple of pixels
|
||||
// wider than their advances, rather than rescale, which looks
|
||||
// bad, we just crop the bitmap on the right. See https://github.com/kovidgoyal/kitty/issues/352
|
||||
} else if (rescale && self->is_scalable && extra > 1) {
|
||||
FT_F26Dot6 char_width = self->char_width, char_height = self->char_height;
|
||||
float ar = (float)max_width / (float)bitmap->width;
|
||||
|
||||
2
kitty/glfw-wrapper.h
generated
2
kitty/glfw-wrapper.h
generated
@@ -1585,7 +1585,7 @@ typedef void (*glfwRequestWindowAttention_func)(GLFWwindow*);
|
||||
glfwRequestWindowAttention_func glfwRequestWindowAttention_impl;
|
||||
#define glfwRequestWindowAttention glfwRequestWindowAttention_impl
|
||||
|
||||
typedef int (*glfwWindowBell_func)(GLFWwindow*, int64_t);
|
||||
typedef int (*glfwWindowBell_func)(GLFWwindow*);
|
||||
glfwWindowBell_func glfwWindowBell_impl;
|
||||
#define glfwWindowBell glfwWindowBell_impl
|
||||
|
||||
|
||||
@@ -604,7 +604,7 @@ ring_audio_bell(OSWindow *w) {
|
||||
if (now - last_bell_at <= 0.1) return;
|
||||
last_bell_at = now;
|
||||
if (w->handle) {
|
||||
glfwWindowBell(w->handle, OPT(x11_bell_volume));
|
||||
glfwWindowBell(w->handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -392,6 +392,7 @@ map ctrl+shift+q close_tab
|
||||
map ctrl+shift+l next_layout
|
||||
map ctrl+shift+. move_tab_forward
|
||||
map ctrl+shift+, move_tab_backward
|
||||
map ctrl+shift+alt+t set_tab_title
|
||||
# You can also create shortcuts to go to specific tabs, with 1 being the first tab
|
||||
# map ctrl+alt+1 goto_tab 1
|
||||
# map ctrl+alt+2 goto_tab 2
|
||||
@@ -462,8 +463,4 @@ macos_hide_titlebar no
|
||||
# break any Alt+key keyboard shortcuts in your terminal programs, but you
|
||||
# can use the macOS unicode input technique.
|
||||
macos_option_as_alt yes
|
||||
|
||||
# The number is a percentage of maximum volume.
|
||||
# See man XBell for details.
|
||||
x11_bell_volume 80
|
||||
# }}}
|
||||
|
||||
@@ -141,10 +141,17 @@ def _main():
|
||||
if is_macos and os.environ.pop('KITTY_LAUNCHED_BY_LAUNCH_SERVICES', None) == '1':
|
||||
os.chdir(os.path.expanduser('~'))
|
||||
args = macos_cmdline()
|
||||
if not os.path.isdir(os.getcwd()):
|
||||
try:
|
||||
cwd_ok = os.path.isdir(os.getcwd())
|
||||
except Exception:
|
||||
cwd_ok = False
|
||||
if not cwd_ok:
|
||||
os.chdir(os.path.expanduser('~'))
|
||||
args, rest = parse_args(args=args)
|
||||
args.args = rest
|
||||
if args.debug_config:
|
||||
create_opts(args, debug_config=True)
|
||||
return
|
||||
if getattr(args, 'detach', False):
|
||||
detach()
|
||||
if args.cmd:
|
||||
|
||||
@@ -16,13 +16,13 @@
|
||||
static MouseShape mouse_cursor_shape = BEAM;
|
||||
typedef enum MouseActions { PRESS, RELEASE, DRAG, MOVE } MouseAction;
|
||||
|
||||
// Encoding of mouse events {{{
|
||||
#define SHIFT_INDICATOR (1 << 2)
|
||||
#define ALT_INDICATOR (1 << 3)
|
||||
#define CONTROL_INDICATOR (1 << 4)
|
||||
#define MOTION_INDICATOR (1 << 5)
|
||||
#define EXTRA_BUTTON_INDICATOR (1 << 6)
|
||||
|
||||
static int last_multi_clicks = 0;
|
||||
|
||||
static inline unsigned int
|
||||
button_map(int button) {
|
||||
@@ -91,6 +91,8 @@ encode_mouse_event(Window *w, int button, MouseAction action, int mods) {
|
||||
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
static inline bool
|
||||
contains_mouse(Window *w) {
|
||||
WindowGeometry *g = &w->geometry;
|
||||
@@ -118,10 +120,9 @@ update_drag(bool from_button, Window *w, bool is_release, int modifiers) {
|
||||
Screen *screen = w->render_data.screen;
|
||||
if (from_button) {
|
||||
if (is_release) screen_update_selection(screen, w->mouse_cell_x, w->mouse_cell_y, true);
|
||||
else screen_start_selection(screen, w->mouse_cell_x, w->mouse_cell_y, modifiers == (int)OPT(rectangle_select_modifiers));
|
||||
else screen_start_selection(screen, w->mouse_cell_x, w->mouse_cell_y, modifiers == (int)OPT(rectangle_select_modifiers), EXTEND_CELL);
|
||||
} else if (screen->selection.in_progress) {
|
||||
screen_update_selection(screen, w->mouse_cell_x, w->mouse_cell_y, false);
|
||||
call_boss(set_primary_selection, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,12 +153,8 @@ drag_scroll(Window *w, OSWindow *frame) {
|
||||
static inline void
|
||||
extend_selection(Window *w) {
|
||||
Screen *screen = w->render_data.screen;
|
||||
index_type start, end;
|
||||
if (screen_has_selection(screen)) {
|
||||
bool found_selectable_word = screen_selection_range_for_word(screen, w->mouse_cell_x, w->mouse_cell_y, &start, &end);
|
||||
if (last_multi_clicks >= 2 && found_selectable_word) screen_update_selection(screen, end, w->mouse_cell_y, true);
|
||||
else screen_update_selection(screen, w->mouse_cell_x, w->mouse_cell_y, false);
|
||||
call_boss(set_primary_selection, NULL);
|
||||
screen_update_selection(screen, w->mouse_cell_x, w->mouse_cell_y, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,20 +231,22 @@ multi_click(Window *w, unsigned int count) {
|
||||
Screen *screen = w->render_data.screen;
|
||||
index_type start, end;
|
||||
bool found_selection = false;
|
||||
SelectionExtendMode mode = EXTEND_CELL;
|
||||
switch(count) {
|
||||
case 2:
|
||||
found_selection = screen_selection_range_for_word(screen, w->mouse_cell_x, w->mouse_cell_y, &start, &end);
|
||||
mode = EXTEND_WORD;
|
||||
break;
|
||||
case 3:
|
||||
found_selection = screen_selection_range_for_line(screen, w->mouse_cell_y, &start, &end);
|
||||
mode = EXTEND_LINE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (found_selection) {
|
||||
screen_start_selection(screen, start, w->mouse_cell_y, false);
|
||||
screen_update_selection(screen, end, w->mouse_cell_y, true);
|
||||
call_boss(set_primary_selection, NULL);
|
||||
screen_start_selection(screen, start, w->mouse_cell_y, false, mode);
|
||||
screen_update_selection(screen, end, w->mouse_cell_y, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,11 +259,9 @@ HANDLER(add_click) {
|
||||
q->length++;
|
||||
// Now dispatch the multi-click if any
|
||||
if (q->length > 2 && N(1).at - N(3).at <= 2 * OPT(click_interval)) {
|
||||
last_multi_clicks = 3;
|
||||
multi_click(w, 3);
|
||||
q->length = 0;
|
||||
} else if (q->length > 1 && N(1).at - N(2).at <= OPT(click_interval)) {
|
||||
last_multi_clicks = 2;
|
||||
multi_click(w, 2);
|
||||
}
|
||||
#undef N
|
||||
@@ -294,14 +291,9 @@ HANDLER(handle_button_event) {
|
||||
switch(button) {
|
||||
case GLFW_MOUSE_BUTTON_LEFT:
|
||||
update_drag(true, w, is_release, modifiers);
|
||||
last_multi_clicks = 0;
|
||||
if (is_release) {
|
||||
if (modifiers == (int)OPT(open_url_modifiers)) {
|
||||
open_url(w);
|
||||
} else {
|
||||
if (is_release) add_click(w, button, modifiers, window_idx);
|
||||
}
|
||||
}
|
||||
if (modifiers == (int)OPT(open_url_modifiers)) open_url(w);
|
||||
} else add_click(w, button, modifiers, window_idx);
|
||||
break;
|
||||
case GLFW_MOUSE_BUTTON_MIDDLE:
|
||||
if (is_release && !modifiers) { call_boss(paste_from_selection, NULL); return; }
|
||||
|
||||
@@ -310,7 +310,7 @@ dispatch_osc(Screen *screen, PyObject DUMP_UNUSED *dump_callback) {
|
||||
}
|
||||
if (i > 0) {
|
||||
code = utoi(screen->parser_buf, i);
|
||||
if (i < limit - 1 && screen->parser_buf[i] == ';') i++;
|
||||
if (i < limit && screen->parser_buf[i] == ';') i++;
|
||||
}
|
||||
PyObject *string = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, screen->parser_buf + i, limit - i);
|
||||
if (string != NULL) {
|
||||
|
||||
@@ -1066,7 +1066,7 @@ bool
|
||||
screen_invert_colors(Screen *self) {
|
||||
bool inverted = false;
|
||||
if (self->start_visual_bell_at > 0) {
|
||||
if (monotonic() - self->start_visual_bell_at <= global_state.opts.visual_bell_duration) inverted = true;
|
||||
if (monotonic() - self->start_visual_bell_at <= OPT(visual_bell_duration)) inverted = true;
|
||||
else self->start_visual_bell_at = 0;
|
||||
}
|
||||
if (self->modes.mDECSCNM) inverted = inverted ? false : true;
|
||||
@@ -1076,6 +1076,7 @@ screen_invert_colors(Screen *self) {
|
||||
void
|
||||
screen_bell(Screen *self) {
|
||||
request_window_attention(self->window_id, OPT(enable_audio_bell));
|
||||
if (OPT(visual_bell_duration) > 0.0f) self->start_visual_bell_at = monotonic();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1410,7 +1411,7 @@ screen_apply_selection(Screen *self, void *address, size_t size) {
|
||||
self->last_selection_scrolled_by = self->scrolled_by;
|
||||
self->selection_updated_once = true;
|
||||
selection_limits_(selection, &self->last_rendered_selection_start, &self->last_rendered_selection_end);
|
||||
apply_selection(self, address, &self->last_rendered_selection_start, &self->last_rendered_selection_end, 1, self->rectangle_select);
|
||||
apply_selection(self, address, &self->last_rendered_selection_start, &self->last_rendered_selection_end, 1, self->selection.rectangle_select);
|
||||
selection_limits_(url_range, &self->last_rendered_url_start, &self->last_rendered_url_end);
|
||||
apply_selection(self, address, &self->last_rendered_url_start, &self->last_rendered_url_end, 2, false);
|
||||
}
|
||||
@@ -1626,9 +1627,17 @@ WRAP0(carriage_return)
|
||||
WRAP2(resize, 1, 1)
|
||||
WRAP2(set_margins, 1, 1)
|
||||
WRAP2(rescale_images, 1, 1)
|
||||
WRAP2B(start_selection)
|
||||
WRAP2B(update_selection)
|
||||
|
||||
static PyObject*
|
||||
start_selection(Screen *self, PyObject *args) {
|
||||
unsigned int x, y;
|
||||
int rectangle_select = 0, extend_mode = EXTEND_CELL;
|
||||
if (!PyArg_ParseTuple(args, "II|pp", &x, &y, &rectangle_select, &extend_mode)) return NULL;
|
||||
screen_start_selection(self, x, y, rectangle_select, extend_mode);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
change_scrollback_size(Screen *self, PyObject *args) {
|
||||
unsigned int count = 1;
|
||||
@@ -1643,7 +1652,7 @@ text_for_selection(Screen *self, PyObject *a UNUSED) {
|
||||
full_selection_limits_(selection, &start, &end);
|
||||
PyObject *ans = NULL;
|
||||
if (start.y == end.y && start.x == end.x) ans = PyTuple_New(0);
|
||||
else text_for_range(ans, start, end, self->rectangle_select, true, range_line_, int);
|
||||
else text_for_range(ans, start, end, self->selection.rectangle_select, true, range_line_, int);
|
||||
return ans;
|
||||
}
|
||||
|
||||
@@ -1733,10 +1742,10 @@ screen_is_selection_dirty(Screen *self) {
|
||||
}
|
||||
|
||||
void
|
||||
screen_start_selection(Screen *self, index_type x, index_type y, bool rectangle_select) {
|
||||
self->rectangle_select = rectangle_select;
|
||||
screen_start_selection(Screen *self, index_type x, index_type y, bool rectangle_select, SelectionExtendMode extend_mode) {
|
||||
#define A(attr, val) self->selection.attr = val;
|
||||
A(start_x, x); A(end_x, x); A(start_y, y); A(end_y, y); A(start_scrolled_by, self->scrolled_by); A(end_scrolled_by, self->scrolled_by); A(in_progress, true);
|
||||
A(start_x, x); A(end_x, x); A(start_y, y); A(end_y, y); A(start_scrolled_by, self->scrolled_by); A(end_scrolled_by, self->scrolled_by);
|
||||
A(in_progress, true); A(rectangle_select, rectangle_select); A(extend_mode, extend_mode);
|
||||
#undef A
|
||||
}
|
||||
|
||||
@@ -1751,6 +1760,20 @@ void
|
||||
screen_update_selection(Screen *self, index_type x, index_type y, bool ended) {
|
||||
self->selection.end_x = x; self->selection.end_y = y; self->selection.end_scrolled_by = self->scrolled_by;
|
||||
if (ended) self->selection.in_progress = false;
|
||||
index_type start, end;
|
||||
bool found = false;
|
||||
switch(self->selection.extend_mode) {
|
||||
case EXTEND_WORD:
|
||||
found = screen_selection_range_for_word(self, x, y, &start, &end);
|
||||
break;
|
||||
case EXTEND_LINE:
|
||||
found = screen_selection_range_for_line(self, y, &start, &end);
|
||||
break;
|
||||
case EXTEND_CELL:
|
||||
break;
|
||||
}
|
||||
if (found) self->selection.end_x = end;
|
||||
call_boss(set_primary_selection, NULL);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
|
||||
@@ -22,9 +22,12 @@ typedef struct {
|
||||
unsigned int x, y;
|
||||
} SelectionBoundary;
|
||||
|
||||
typedef enum SelectionExtendModes { EXTEND_CELL, EXTEND_WORD, EXTEND_LINE } SelectionExtendMode;
|
||||
|
||||
typedef struct {
|
||||
unsigned int start_x, start_y, start_scrolled_by, end_x, end_y, end_scrolled_by;
|
||||
bool in_progress;
|
||||
bool in_progress, rectangle_select;
|
||||
SelectionExtendMode extend_mode;
|
||||
} Selection;
|
||||
|
||||
#define SAVEPOINTS_SZ 256
|
||||
@@ -61,7 +64,7 @@ typedef struct {
|
||||
Selection selection;
|
||||
SelectionBoundary last_rendered_selection_start, last_rendered_selection_end, last_rendered_url_start, last_rendered_url_end;
|
||||
Selection url_range;
|
||||
bool use_latin1, selection_updated_once, is_dirty, scroll_changed, rectangle_select;
|
||||
bool use_latin1, selection_updated_once, is_dirty, scroll_changed;
|
||||
Cursor *cursor;
|
||||
SavepointBuffer main_savepoints, alt_savepoints;
|
||||
SavemodesBuffer modes_savepoints;
|
||||
@@ -156,7 +159,7 @@ void screen_update_cell_data(Screen *self, void *address, size_t sz);
|
||||
bool screen_is_cursor_visible(Screen *self);
|
||||
bool screen_selection_range_for_line(Screen *self, index_type y, index_type *start, index_type *end);
|
||||
bool screen_selection_range_for_word(Screen *self, index_type x, index_type y, index_type *start, index_type *end);
|
||||
void screen_start_selection(Screen *self, index_type x, index_type y, bool);
|
||||
void screen_start_selection(Screen *self, index_type x, index_type y, bool, SelectionExtendMode);
|
||||
void screen_update_selection(Screen *self, index_type x, index_type y, bool ended);
|
||||
bool screen_history_scroll(Screen *self, int amt, bool upwards);
|
||||
Line* screen_visual_line(Screen *self, index_type y);
|
||||
|
||||
@@ -214,9 +214,7 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, G
|
||||
struct CellRenderData {
|
||||
GLfloat xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity;
|
||||
|
||||
GLuint default_fg, default_bg, highlight_fg, highlight_bg, cursor_color, url_color, url_style;
|
||||
|
||||
GLint color1, color2;
|
||||
GLuint default_fg, default_bg, highlight_fg, highlight_bg, cursor_color, url_color, url_style, inverted;
|
||||
|
||||
GLuint xnum, ynum, cursor_x, cursor_y, cursor_w;
|
||||
};
|
||||
@@ -241,7 +239,7 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, G
|
||||
unsigned int x, y, z;
|
||||
sprite_tracker_current_layout(&x, &y, &z);
|
||||
rd->sprite_dx = 1.0f / (float)x; rd->sprite_dy = 1.0f / (float)y;
|
||||
rd->color1 = inverted & 1; rd->color2 = 1 - (inverted & 1);
|
||||
rd->inverted = inverted ? 1 : 0;
|
||||
rd->background_opacity = OPT(background_opacity);
|
||||
|
||||
#define COLOR(name) colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.name, screen->color_profile->configured.name)
|
||||
|
||||
@@ -340,7 +340,6 @@ PYWRAP1(set_options) {
|
||||
S(inactive_text_alpha, PyFloat_AsDouble);
|
||||
S(cursor_shape, PyLong_AsLong);
|
||||
S(url_style, PyLong_AsUnsignedLong);
|
||||
S(x11_bell_volume, PyLong_AsLong);
|
||||
S(tab_bar_edge, PyLong_AsLong);
|
||||
S(mouse_hide_wait, PyFloat_AsDouble);
|
||||
S(wheel_scroll_multiplier, PyFloat_AsDouble);
|
||||
|
||||
@@ -26,7 +26,6 @@ typedef struct {
|
||||
bool macos_option_as_alt, macos_hide_titlebar;
|
||||
int adjust_line_height_px, adjust_column_width_px;
|
||||
float adjust_line_height_frac, adjust_column_width_frac;
|
||||
int x11_bell_volume;
|
||||
float background_opacity;
|
||||
float inactive_text_alpha;
|
||||
Edge tab_bar_edge;
|
||||
@@ -137,7 +136,7 @@ typedef struct {
|
||||
|
||||
extern GlobalState global_state;
|
||||
|
||||
#define call_boss(name, ...) { \
|
||||
#define call_boss(name, ...) if (global_state.boss) { \
|
||||
PyObject *cret_ = PyObject_CallMethod(global_state.boss, #name, __VA_ARGS__); \
|
||||
if (cret_ == NULL) { PyErr_Print(); } \
|
||||
else Py_DECREF(cret_); \
|
||||
|
||||
@@ -182,6 +182,9 @@ class TestParser(BaseTest):
|
||||
c.clear()
|
||||
pb('\033]2;;;;\x07', ('set_title', ';;;'))
|
||||
self.ae(c.titlebuf, ';;;')
|
||||
c.clear()
|
||||
pb('\033]2;\x07', ('set_title', ''))
|
||||
self.ae(c.titlebuf, '')
|
||||
pb('\033]110\x07', ('set_dynamic_color', 110, ''))
|
||||
self.ae(c.colorbuf, '')
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ for sz in (16, 32, 64, 128, 256, 512, 1024):
|
||||
iname = 'icon_{0}x{0}.png'.format(sz)
|
||||
iname2x = 'icon_{0}x{0}@2x.png'.format(sz // 2)
|
||||
render(iname, sz)
|
||||
if sz > 16:
|
||||
if sz > 16 and sz != 128:
|
||||
shutil.copy2(iname, iname2x)
|
||||
if sz > 512:
|
||||
if sz in (64, 1024):
|
||||
os.remove(iname)
|
||||
|
||||
Reference in New Issue
Block a user