Compare commits

..

474 Commits

Author SHA1 Message Date
Kovid Goyal
4b0724530e Fix #2912 2020-08-11 14:19:32 +05:30
Kovid Goyal
e5681a93f9 version 0.18.3 2020-08-11 13:38:38 +05:30
Kovid Goyal
fb838a694e ... 2020-08-11 13:08:16 +05:30
Kovid Goyal
8c6f188767 Forgot to use " for key names 2020-08-11 13:01:33 +05:30
Kovid Goyal
55e15dc79e Properly type check os window initial size function
Also use the sizes specified int he session file in preference to those
specified on the command line. Fixes #2909
2020-08-11 09:28:53 +05:30
Kovid Goyal
514073cb75 Fix #2911 2020-08-11 07:52:32 +05:30
Kovid Goyal
f6ece6522e Fix a regression that broke specifying OS window size in the session file
Fixes #2908
2020-08-10 21:55:54 +05:30
Kovid Goyal
817f0997cf Fix one pixel mis-alignment of rounded corners when either the cell dimensions or the thickness of the line is an odd number of pixels
The control points of the Bezier curve and the spread due to thickness
must match up with the values used to draw straight lines. In order to
ensure that, one calculates them before upscaling by the super sampling
factor, rather than after. Fixes #2907
2020-08-10 17:28:08 +05:30
Kovid Goyal
1d651270d3 Merge branch 'weechat-hints-kitten' of https://github.com/GermainZ/kitty into master 2020-08-07 21:55:51 +05:30
GermainZ
6ea58fac1a Add link to weechat-hints kitten 2020-08-07 17:38:38 +02:00
Kovid Goyal
c7eb63d4ff Avoid extra layer update if clearing images has no effect 2020-08-07 09:36:28 +05:30
Kovid Goyal
4062365b19 Revert "Adjust cell height automatically for buggy fonts that draw brackets outside the bounding box"
This reverts commit 5555a66638.
Since it does not fix the issue.
2020-08-06 21:20:54 +05:30
Kovid Goyal
7f48a927de Fix image leaving behind a black rectangle when switching away and back to alternate screen
layer_dirty was being reset if remove_images was called more than once
without an intervening call to update_layers.

Fixes #2901
2020-08-06 19:33:28 +05:30
Kovid Goyal
4a740cc556 Cleanup 2020-08-06 18:14:56 +05:30
Kovid Goyal
628b92f20b Speed up is_ignored_char in the common case 2020-08-06 18:05:33 +05:30
Kovid Goyal
8f9616c230 Speed up wcwidth_std in the common case 2020-08-06 17:57:57 +05:30
Kovid Goyal
df475dfde5 Fix indentation 2020-08-06 17:55:02 +05:30
Kovid Goyal
a835b56a51 Speed up is_combining_char() in the common case 2020-08-06 17:45:40 +05:30
Kovid Goyal
5555a66638 Adjust cell height automatically for buggy fonts that draw brackets outside the bounding box
See https://github.com/cormullion/juliamono/issues/14
2020-08-06 14:06:06 +05:30
Kovid Goyal
dad355b932 oops 2020-08-05 11:06:24 +05:30
Kovid Goyal
90a097d45c Ensure beep works even very soon after startup 2020-08-05 11:04:07 +05:30
Kovid Goyal
dba01bc014 Make ringing bell not dependent on having a OSWindow 2020-08-05 10:58:00 +05:30
Kovid Goyal
1d0a3ce998 Merge branch 'master' of https://github.com/ayang64/kitty into master 2020-08-05 07:46:27 +05:30
Ayan George
076ad850bb Declare _glfwPlatformInitJoysticks() to return bool
This patch declares _glfwPlatformInitJoysticks() to return bool which
matches its prototype in glfw/internal.h.
2020-08-04 20:24:35 -04:00
Kovid Goyal
868546abe4 Fix #2896 2020-08-03 22:14:13 +05:30
Kovid Goyal
6b421fabd1 Wayland: Fix a typo in the previous release that broke reading mouse cursor size
Fixes #2895
2020-08-03 17:08:08 +05:30
Kovid Goyal
ab3c9658bd Add hunter to integrations 2020-08-03 09:05:04 +05:30
Kovid Goyal
8c7886d429 Add a changelog entry for the last PR 2020-08-03 09:00:19 +05:30
Kovid Goyal
110a3e7e0f Merge branch 'master' of https://github.com/jaeheum/kitty into master 2020-08-03 08:58:24 +05:30
Jay Han
ee0b70f51b allow users to choose hint colors 2020-08-02 15:33:17 -07:00
Kovid Goyal
df48061a8f Merge branch 'master' of https://github.com/wengxt/kitty into master 2020-08-01 07:30:09 +05:30
Weng Xuetian
b1387d50f0 Register to bus when connect to ibus.
This is required when the actual dbus is not from ibus but session bus.
This may happen when other run a shim layer for ibus, or ibus is
running with flatpak mode.
2020-07-31 17:54:10 -07:00
Kovid Goyal
73dd218a95 Merge branch 'with-suppress' of https://github.com/Luflosi/kitty into master 2020-07-31 21:36:02 +05:30
Luflosi
ee4cedcc81 Use "with suppress()" to suppress a python exception 2020-07-31 18:00:01 +02:00
Kovid Goyal
d1b2f9c6a9 Dont use SystemExit if kitty exe not found 2020-07-31 20:03:04 +05:30
Kovid Goyal
40dee44978 Merge branch 'kitty-exe-error-message' of https://github.com/Luflosi/kitty into master 2020-07-31 20:02:15 +05:30
Luflosi
db04c70d4a Let kitty_exe() raise an error message if kitty could not be found 2020-07-31 15:46:47 +02:00
Kovid Goyal
7401c6ac97 Merge branch 'dryer' of https://github.com/Luflosi/kitty into master 2020-07-31 16:38:23 +05:30
Luflosi
f7a1222cc3 DRYer 2020-07-30 22:11:41 +02:00
Kovid Goyal
069f09c43c Move wlc destructor to just before the functions are unloaded 2020-07-29 17:18:03 +05:30
Kovid Goyal
35f821f6a1 Ensure wl_cursor_theme_destroy is called before it is released
Fix #2884
2020-07-29 17:13:00 +05:30
Kovid Goyal
67f60847ee Fix the spawn test in the bypy environment
Required the launcher to be built since the pre-built
one was being used. Fixes #2881
2020-07-29 15:11:35 +05:30
Kovid Goyal
65f6b142a5 typo 2020-07-28 15:22:14 +05:30
Kovid Goyal
81bda736d8 Skip multiprocessing spawn monkeypatch test on 32bit 2020-07-28 14:15:31 +05:30
Kovid Goyal
ec3998a9b8 version 0.18.2 2020-07-28 14:01:16 +05:30
Kovid Goyal
01be77e0bc Fix change in window size that does not change number of cells not being reported to the kernel
Fixes #2880
2020-07-25 22:49:39 +05:30
Kovid Goyal
6f40b8d0a1 Simplify Wayland cursor theme handling
Now themes are loaded once per scale and stored centrally not per
window. They are not unloaded till application shutdown. Since there
is unlikely to be more than two or three scales this is not a big waste
of resources. Since cursor lifetime is tied to theme lifetime and cursors
are stored per window, destroying a theme requires destroying all
cursors win all windows referring to that theme, which is too much work.

Should hopefully fix #2810
2020-07-25 15:36:46 +05:30
Kovid Goyal
a083aa04b2 vim also produces CSI > 4;m ignore that as well 2020-07-24 19:19:18 +05:30
Kovid Goyal
1f33348cf6 Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-07-24 15:10:35 +05:30
Luflosi
a45651933c GLFW: Cocoa: Support some sim page joystick elements
From upstream: 0d355379e0.
2020-07-24 11:28:53 +02:00
Kovid Goyal
ab57fbff73 Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-07-24 14:56:54 +05:30
Luflosi
6b3f774f05 GLFW: Cocoa: Fix potential leak of CFNumber object
From upstream: a2674a9034.
2020-07-24 10:59:04 +02:00
Kovid Goyal
1ef2880c6c Also make the platform window id available via kitty @ ls 2020-07-24 08:47:13 +05:30
Kovid Goyal
ace152d8a3 DRYer 2020-07-24 08:41:55 +05:30
Kovid Goyal
8d08036a9d Update changelog for previous PR 2020-07-24 08:35:01 +05:30
Kovid Goyal
b0876fae64 Merge branch 'cocoa-window-number' of https://github.com/eraserhd/kitty 2020-07-24 08:33:16 +05:30
Kovid Goyal
01633776d3 Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-07-24 08:26:18 +05:30
Jason Felice
e4202228eb Update fast_data_types.pyi 2020-07-23 19:09:16 -04:00
Jason Felice
79d5049273 Provide cocoa_window_id() on non-Mac, also
So that its import in tabs.py doesn't have to be conditional.
2020-07-23 18:55:04 -04:00
Jason Felice
9120e3b790 Reorganize window id functions slightly 2020-07-23 18:47:36 -04:00
Jason Felice
974e6140b3 Extract find_os_window() 2020-07-23 18:36:32 -04:00
Jason Felice
1fd338cc6c Set Cocoa window number 2020-07-23 18:36:32 -04:00
Luflosi
bc61a81d7a GLFW: X11: Fix joystick detection before joystick init
From upstream: c01acc267a.
2020-07-23 23:57:03 +02:00
Luflosi
06b30d1cc3 GLFW: Cleanup
From upstream: f771d41292.
2020-07-23 23:56:32 +02:00
Luflosi
785973bb5e GLFW: Make joystick platform code init on demand
From upstream: 782e6b6cef.
2020-07-23 23:56:30 +02:00
Luflosi
78efefcea1 Revert "Linux: Fix slow startup on some systems caused by GLFW searching for joysticks. Since kitty does not use joysticks, disable joystick support."
This reverts commit ab960ea12d.
2020-07-23 23:56:21 +02:00
Kovid Goyal
1bb6242020 macOS: no longer render emoji 20% below the baseline. This causes some emoji to be cut-off and also look misaligned with very high cells
Fixes #2873
2020-07-23 20:38:02 +05:30
Kovid Goyal
1af426bf84 Add support for legacy DECSET codes 47, 1047 and 1048
Fixes #2871
2020-07-22 09:00:51 +05:30
Kovid Goyal
dc4762a69a Dont crash if Wayland returns a cursor image array with some null images 2020-07-21 19:33:05 +05:30
Kovid Goyal
a785b77da9 Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-07-19 09:27:44 +05:30
Luflosi
1342767805 GLFW: Wayland: Fix scroll offsets being inverted
From upstream: f760b124ca.

This commit does not actually change any behaviour, it merely makes the code style of the two lines more similar to the new upstream code.
2020-07-18 19:32:03 +02:00
Kovid Goyal
a3fc3233cd Fix pre-edit text not always being cleared with iBus input
Fixes #2862
2020-07-18 19:31:38 +05:30
Kovid Goyal
8093d70643 Allow setting the class for new OS windows in a session file 2020-07-18 12:11:41 +05:30
Kovid Goyal
9d9ec84b54 fix talk mutex typo unlock -> lock 2020-07-18 11:47:22 +05:30
Kovid Goyal
6b26088c27 move wakeup out of locked region 2020-07-18 11:42:57 +05:30
Kovid Goyal
43af715caa free peer message queue at shutdown 2020-07-18 10:50:09 +05:30
Kovid Goyal
c6eea488b1 Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-07-18 09:24:01 +05:30
Luflosi
b1c3999a81 GLFW: Wayland: Fix partial framebuffer size retrieval
From upstream: 5f60c22cfa.
2020-07-17 21:10:32 +02:00
Kovid Goyal
4f19403518 Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-07-17 19:48:06 +05:30
Luflosi
4d1b537f78 GLFW: Documentation work for GLFW_MOUSE_PASSTHROUGH
From upstream: a122d91303.
2020-07-17 00:08:22 +02:00
Luflosi
dd54db47a9 GLFW: Move mouse passthrough before window showing
From upstream: dfeacee000.
2020-07-17 00:07:36 +02:00
Luflosi
44775e4644 GLFW: Remove no-op call at window creation
From upstream: 1095a43708.
2020-07-16 23:55:24 +02:00
Luflosi
805921d6a3 GLFW: Move management of shared state to shared code
From upstream: 6d2003d07a.
2020-07-16 23:54:16 +02:00
Luflosi
d7cd6edaa5 GLFW: X11: Include X Shape extension headers
From upstream: 68e4261d73.
2020-07-16 23:50:26 +02:00
Luflosi
6a472bd6da GLFW: X11: Fix disabling of GLFW_MOUSE_PASSTHROUGH
From upstream: e81d381256.
2020-07-16 23:24:48 +02:00
Luflosi
d0c6ce9fd7 GLFW: Add support for mouse input transparency
From upstream: d285a9fdeb.
2020-07-16 22:25:12 +02:00
Kovid Goyal
df48e2f9e2 Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-07-16 23:43:07 +05:30
Luflosi
2b4b3bbf18 GLFW: Null: Fix typos in workarea retrieval
From upstream: 6c031af245.
2020-07-16 20:01:57 +02:00
Kovid Goyal
6ea5027bf6 Remote control close_tab should not ask for confirmation 2020-07-16 12:55:25 +05:30
Kovid Goyal
384ccb4fc7 Have the :opt:confirm_os_window_close option also apply when closing tabs with multiple windows
Fixes #2857
2020-07-16 12:51:19 +05:30
Kovid Goyal
ff763b099e Merge branch 'patch-1' of https://github.com/tammersaleh/kitty 2020-07-16 08:06:47 +05:30
Kovid Goyal
741ae4c04b Merge branch 'typo' of https://github.com/Luflosi/kitty 2020-07-16 08:06:20 +05:30
Tammer Saleh
d84ec941c0 Fix typo 2020-07-15 17:44:02 -07:00
Luflosi
f0b5dd9799 Fix typo 2020-07-15 19:09:35 +02:00
Kovid Goyal
cec4371b51 Rewrite the talk loop for interaction with peers
The new code is simpler and hopefully more robust. Supports
multiple requests/responses. Fixes #2852
2020-07-15 14:31:45 +05:30
Kovid Goyal
c0ff39c59d Fix extra close for loops with no signal_read_fd 2020-07-14 20:23:45 +05:30
Kovid Goyal
022634f2d9 Log an extra error message 2020-07-14 20:18:38 +05:30
Kovid Goyal
392f576d07 Instrument safe_close() to make it easy to see where an fd is closed from 2020-07-14 20:18:04 +05:30
Kovid Goyal
6d59e2125e Prevent invalid fds from spinning the talk thread forever 2020-07-14 19:09:04 +05:30
Kovid Goyal
6476a1d2e0 When a character from the Unicode Dingbat block is followed by a space, use the extra space to render a larger version of the character
Fixes #2850
2020-07-12 21:12:34 +05:30
Kovid Goyal
ca998fb4b0 Fix incorrect centering when a PUA or symbol glyph is followed by more than one space 2020-07-12 20:16:16 +05:30
Kovid Goyal
48631c4ea6 Add changelog entry for previous merge
Also clean up use of typing.cast
2020-07-12 14:47:14 +05:30
Kovid Goyal
294be2a772 Merge branch 'neighboring_window_changes' of https://github.com/juho-p/kitty 2020-07-12 14:33:21 +05:30
Kovid Goyal
533688a67a Implement support for box drawing rounded-corners characters
Fixes #2240
2020-07-12 12:35:52 +05:30
Kovid Goyal
4309e2371d Simplify code by using supersampling for diagonal lines as well
Wu antialiasing obviously performs better but since characters are
rendered only once and are very small in pixel size, performance is
irrelevant here.
2020-07-12 10:07:55 +05:30
Juho Peltonen
ea30c84240 Fix grid layout neighbors
Grid layout can have multiple neighbors in one side when either current
column or neighboring column is special.
2020-07-11 23:52:37 +03:00
Juho Peltonen
d4c2b13dda Prefer most recently activated window when selecting neighbors
This affects both activating and moving neighbor windows.
2020-07-11 23:52:33 +03:00
Juho Peltonen
1decae2938 Change splits layout neighbors to only include real neighbors 2020-07-11 21:35:29 +03:00
Kovid Goyal
145ec86d3b Forgot to add supersampling to rendering of filled D 2020-07-11 23:10:43 +05:30
Kovid Goyal
e17c96b20f Improve anti-aliasing of triangular box drawing characters, noticeable on low-resolution screens
Fixes #2844
2020-07-11 23:08:28 +05:30
Kovid Goyal
2cfcefd9eb Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-07-11 22:57:53 +05:30
Luflosi
356e950122 X11: Fix cursor hover query retry behavior
From upstream: 49dbcfcb8c.
2020-07-11 18:59:28 +02:00
Kovid Goyal
64dee63b73 Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-07-11 22:26:32 +05:30
Luflosi
431d013d32 X11: Fix race in window hovered query
From upstream: ceb20c7f97.
2020-07-11 18:18:33 +02:00
Kovid Goyal
40eb8455d0 Fix `launch --location=last` not working
Fixes #2841
2020-07-09 22:29:24 +05:30
Kovid Goyal
c46b36509b macOS: Fix cmd+plus not increasing font size with keyboards where the + key is a shifted key. Fixes #2839 2020-07-09 22:14:07 +05:30
Kovid Goyal
f7ff63132d Merge branch 'remove-trailing-whitespace' of https://github.com/Luflosi/kitty 2020-07-09 07:42:02 +05:30
Luflosi
e17036a7a5 Remove trailing whitespace 2020-07-09 01:57:39 +02:00
Kovid Goyal
e678c41f56 More code scanning warning fixes 2020-07-07 10:24:57 +05:30
Kovid Goyal
ee3462592a Silence more code scanning warnings 2020-07-07 10:01:38 +05:30
Kovid Goyal
521f921424 Fix a bunch of code scanning warnings 2020-07-07 09:52:59 +05:30
Kovid Goyal
ee47a10b37 Simplify code a bit 2020-07-07 09:44:51 +05:30
Kovid Goyal
b81700144d Silence another code scanning warning 2020-07-07 09:38:33 +05:30
Kovid Goyal
9f28dd61d3 Silence a spurious code scanning warning
I hope it silences it anyway
2020-07-07 09:31:00 +05:30
Kovid Goyal
dad7d366b3 Fix out of bounds write on platforms where sizeof(bool) != 1
Found by code scanning
2020-07-07 09:25:14 +05:30
Kovid Goyal
209b78a3ec Silence a code scanning warning 2020-07-07 09:20:59 +05:30
Kovid Goyal
17f34d1f95 Use the more secure localtime_r variant
Pointed out by code scanning
2020-07-07 09:20:27 +05:30
Kovid Goyal
26699cd9ef Fix incorrect format specifier to printf
Detected by code scanning
2020-07-07 09:14:33 +05:30
Kovid Goyal
907db09a58 Only use pythonLocation if it is available 2020-07-07 08:57:50 +05:30
Kovid Goyal
d506fa0565 Use python3 in code analysis build step 2020-07-07 08:41:20 +05:30
Kovid Goyal
de1a0d48a6 Use ci script to build kitty for code scanning 2020-07-07 08:35:04 +05:30
Kovid Goyal
a20e5fefd3 ... 2020-07-07 08:27:10 +05:30
Kovid Goyal
44558560f9 Dont use autobuild for code scanning 2020-07-07 08:24:37 +05:30
Kovid Goyal
677f154f03 Create codeql-analysis.yml
Enable GitHub code scanning for vulnerabilities
2020-07-07 08:21:14 +05:30
Kovid Goyal
0adaf064e7 Fix #2832 2020-07-05 07:34:11 +05:30
Kovid Goyal
3c828feef2 Merge branch 'environmenterror-to-oserror' of https://github.com/Luflosi/kitty 2020-07-02 08:29:27 +05:30
Kovid Goyal
4d3c5e61fd Update changelog 2020-07-02 08:26:37 +05:30
Kovid Goyal
67baefb52a Merge branch 'hints-linenum-expand-home' of https://github.com/trygveaa/kitty 2020-07-02 08:23:59 +05:30
Trygve Aaberge
d509a4fa8c Expand ~ in the path argument for linenum in hints
Fixes #2822
2020-07-02 01:01:14 +02:00
Luflosi
cad1c12b7b Replace EnvironmentError with OSError
See 527ff0238a.
2020-07-01 22:39:01 +02:00
Kovid Goyal
9f751d8715 Add kitti3 to integrations.rst 2020-06-30 08:15:16 +05:30
Kovid Goyal
cf8a1dff33 Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-06-30 08:13:27 +05:30
Luflosi
973d98457f EGL: Add support for EGL_ANGLE_platform_angle
From upstream: 0dea8a4441.
2020-06-30 00:34:32 +02:00
Kovid Goyal
ce262f810d Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-06-29 19:13:34 +05:30
Luflosi
bd31c2b484 Add GLFW_CONTEXT_DEBUG window hint alias
From upstream: b420ca7404.
2020-06-29 15:30:38 +02:00
Kovid Goyal
ef0e610802 Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-06-29 18:45:19 +05:30
Luflosi
3040d4c83f Re-word docs for GLFW_OPENGL_DEBUG_CONTEXT attrib
From upstream: 4e3b43383a.
2020-06-29 11:43:24 +02:00
Kovid Goyal
c9252e8639 When erasing in display upto cursor, the line containing the cursor should be marked as not continued.
Also add some tests for the action of ED on continued flags
2020-06-29 14:29:03 +05:30
Kovid Goyal
e2e701460b Fix the CSI J (Erase in display ED) escape code not removing line continued markers
Fixes #2809

Remains to write tests for this.
2020-06-29 14:17:40 +05:30
Kovid Goyal
f0c3641ebd Some more commands for replay commands 2020-06-29 13:04:10 +05:30
Kovid Goyal
025f6d7701 Add keypad mode support to replay commands
Also name the constants correctly
2020-06-29 12:46:34 +05:30
Kovid Goyal
c68516ca8d Wayland: Fix a crash when using animated mouse cursors
Fixes #2810
2020-06-29 08:03:25 +05:30
Kovid Goyal
a9e739e08d Apply single instance BO override when different from current BO not when less than 1 2020-06-27 17:32:03 +05:30
Kovid Goyal
cc15ff73b7 Respect --override of background_opacity for --single-instance
Fixes #2806
2020-06-27 09:18:41 +05:30
Kovid Goyal
fd3a4536e7 Fix imperative close request being ignored in absence of any other activity 2020-06-26 16:37:17 +05:30
Kovid Goyal
75caafb5ac Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-06-26 16:34:33 +05:30
Kovid Goyal
cf2f8165eb Only ask application to abort once 2020-06-26 16:33:25 +05:30
Luflosi
a2f80740d7 Remove EGL and OSMesa struct member macros
From upstream: bcb8558189.
2020-06-26 12:51:11 +02:00
Kovid Goyal
c7703791d1 Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-06-26 16:19:04 +05:30
Luflosi
c39e9e9c61 EGL: Add support for EGL_EXT_platform_base
From upstream: aec9fae8f3.
2020-06-26 12:39:17 +02:00
Kovid Goyal
713059319c Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-06-26 15:59:34 +05:30
Luflosi
d7a7509da6 EGL: Replace native handle macros with functions
From upstream: d7f7b0e1b5.
2020-06-26 12:17:13 +02:00
Kovid Goyal
57e6e7b2c5 Use ctrl+[] for changing tabs in unicode input as ctrl+tab is taken on macOS
ctrl+tab remains in addition for backwards compat
2020-06-26 12:29:42 +05:30
Kovid Goyal
d675977f10 Better error message for xterm specific key resource CSI codes that vim has started emitting 2020-06-25 23:07:34 +05:30
Kovid Goyal
7ef911e17d Fix #2798 2020-06-25 21:43:54 +05:30
Kovid Goyal
9a384c5045 Allow passing the current selection to kittens
Fixes #2796
2020-06-25 08:46:05 +05:30
Kovid Goyal
2b3753d99c Prevent super large tab margin widths from causing a crash
Fixes #2793
2020-06-24 22:56:54 +05:30
Kovid Goyal
d173ab9f45 macOS: Fix the LC_TYPE env var being set to UTF-8 on systems in which the language and country code do not form a valid locale
Fixes #1233
2020-06-24 22:09:25 +05:30
Kovid Goyal
fbd0e8e26a X11: Improve handling of multiple keyboards
Now pressing a modifier key in one keyboard and a normal key in another works. Fixes #2362

Don't rebuild keymaps on new keyboard events that only change geometry. Fixes #2787

Better handling of multiple keyboards with incompatible layouts (this is for free from the above fixes). Fixes #2726
2020-06-24 12:05:30 +05:30
Kovid Goyal
50414b333a Re-enable the spawn tesRe-enable the spawn test 2020-06-23 20:43:40 +05:30
Kovid Goyal
ee2fdb3d25 Merge branch 'typos' of https://github.com/Luflosi/kitty 2020-06-23 20:16:42 +05:30
Luflosi
2b884377d4 Fix typos 2020-06-23 16:35:13 +02:00
Kovid Goyal
36a20f7b00 Temporarily disable spawn test as it is failing in the Linux build container
Will investigate later
2020-06-23 17:37:04 +05:30
Kovid Goyal
35cbf49b08 version 0.18.1 2020-06-23 17:29:22 +05:30
Kovid Goyal
21836cedda Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-06-21 22:07:44 +05:30
Luflosi
0a892b72e6 Fix GLU header inclusion being disabled
From upstream: 81f475bccb.
2020-06-21 18:07:31 +02:00
Kovid Goyal
2c6e5a6e73 Get multiprocessing working in kitty
Monkeypatch the stdlib multiprocessing module to
use kitty as its python interpreter for spawning worker
processes.
2020-06-21 14:47:24 +05:30
Kovid Goyal
2cb25cf5a8 Build the launcher when freezing on macOS as well
Can be used by the test suite
2020-06-21 14:37:36 +05:30
Kovid Goyal
e07916425e macOS: Fix for diff kitten not working with python 3.8
Fixes #2780
2020-06-20 19:03:45 +05:30
Kovid Goyal
f62e2374e4 Ensure mimetype is sent to github 2020-06-20 13:02:34 +05:30
Kovid Goyal
eb4ee2f0af version 0.18.0 2020-06-20 12:48:59 +05:30
Kovid Goyal
be3c76da20 Fix remote control command docs order randomized 2020-06-20 12:45:05 +05:30
Kovid Goyal
b15e58621c Fix the kitty shell launched via a mapping needlessly requiring :opt:allow_remote_control to be turned on. 2020-06-20 12:43:46 +05:30
Kovid Goyal
601a6c9e3d Allow sending arbitrary signals to the current foreground process in a window using either a mapping in kitty.conf or via remote control
Fixes #2778
2020-06-20 12:37:27 +05:30
Kovid Goyal
3ff184348e Merge branch 'unused-macro' of https://github.com/Luflosi/kitty 2020-06-19 18:33:28 +05:30
Luflosi
c8efcd2be1 Remove unused macro 2020-06-19 14:31:58 +02:00
Kovid Goyal
9c116a2975 Fix #2776 2020-06-19 12:13:32 +05:30
Kovid Goyal
8818b5d8df Set CFBundleAllowMixedLocalizations in the app bundle to have NSLocale.currentLocale return the correct locale on non-English systems 2020-06-18 20:16:36 +05:30
Kovid Goyal
e96dfadae7 Ensure cocoa_get_lang() returns a valid locale or None
See #1233
2020-06-18 20:15:01 +05:30
Kovid Goyal
deb564e5a6 Even more integrations 2020-06-17 18:09:34 +05:30
Kovid Goyal
ffc71c348c A couple more integrations with external tools 2020-06-17 18:07:19 +05:30
Kovid Goyal
19df7b94ea Clarify documentation a bit 2020-06-16 09:53:08 +05:30
Kovid Goyal
a3ecc79f0d Dont actually need -- since kitty's option processing code is smart enough to stop looking for options after the first non-optional argument 2020-06-16 09:31:42 +05:30
Kovid Goyal
b140b15119 Add a -- to linenum hints examples 2020-06-16 08:30:34 +05:30
Kovid Goyal
b7b2b6075a Add mdcat 2020-06-15 09:31:51 +05:30
Kovid Goyal
7525e152b8 Make comment a bit better 2020-06-15 06:55:52 +05:30
Kovid Goyal
bc9dfec615 Fix #2764 2020-06-15 06:53:35 +05:30
Kovid Goyal
7824739034 Ensure selection GPU buffer is refreshed after a resize
Fixes #2747 (I hope since I cannot reproduce)
2020-06-13 22:59:46 +05:30
Kovid Goyal
e7b60a3628 Fix #2763 2020-06-13 20:09:26 +05:30
Kovid Goyal
97c3061b8f Allow having no tab separator
Fixes #2761
2020-06-13 12:33:15 +05:30
Kovid Goyal
1a042c873c Add a link to vim-test 2020-06-11 08:02:17 +05:30
Kovid Goyal
064aa3a150 Wayland: Ensure cursor theme is loaded before trying to set cursor 2020-06-11 07:57:08 +05:30
Kovid Goyal
8a178ebe55 Wayland: Apply the same framebuffer selection optimisation
Not as much of a win, since EGL only causes approx 1K getpid() calls of
which this optimization eliminates only half
2020-06-09 21:59:25 +05:30
Kovid Goyal
9a97f0bced X11: Further reduce startup time
Use the GLX 1.3 API function to choose the best framebuffer config
instead of doing it ourselves. Reduces the number fo getpid() calls by
a further ~15K
2020-06-09 20:18:07 +05:30
Kovid Goyal
5c7471910d Fix #2752 2020-06-09 09:51:15 +05:30
Kovid Goyal
27b6517594 X11: Reduce startup time by ~25% by only querying GLX for framebuffer configurations once
On my system reduces the number of getpid() calls from 67982 to 17327
I dont know why get framebuffer config attributes requires getpid() and
why it is so slow, probably a driver bug. Fixes #2754
2020-06-09 01:04:09 +05:30
Kovid Goyal
cd4dacb406 Fix #2753 2020-06-08 21:40:59 +05:30
Kovid Goyal
5f6faa1bf0 Make a central list of integrations in the docs
Fixes #2750
2020-06-08 14:49:59 +05:30
Kovid Goyal
cfc5a952f8 Add --no-response to @ last-used-layout 2020-06-08 09:16:26 +05:30
Kovid Goyal
10b186957b Update link to termpdf.py 2020-06-08 08:57:50 +05:30
Kovid Goyal
f597481736 Update changelog for last merge 2020-06-06 22:32:52 +05:30
Kovid Goyal
9fa97e6187 Merge branch 'mouse-events' of https://github.com/trygveaa/kitty 2020-06-06 22:27:54 +05:30
Kovid Goyal
3844321f20 Use a multiple of 8 size for mouse_button_pressed 2020-06-06 22:24:01 +05:30
Trygve Aaberge
8efe08c45b Support more mouse buttons for terminal mouse events
Previously, the mouse back and forward buttons sent the same codes as
scroll up and down. Now they instead send the same codes as xterm. Mouse
button 10 (in X11 numbering) also now sends the same as xterm, instead
of not sending anything.

This also changes the `send_mouse_event` function which can be called
from kittens to use X11 numbering for mouse buttons instead of what it
previously used, which turns out to be a hybrid of X11 and GLFW. It was
documented to use GLFW numbering, but GLFW doesn't have numbers for
scroll events (that's separate events with x/y offsets) and 4 and 5 in
GLFW is actually back and forward, while `send_mouse_event` interpreted
it as scroll up and down.

That means that this is a breaking change for `send_mouse_event` because
it swaps the number for the middle and right button to be consistent
with X11. I did this because I think it's better to use one consistent
numbering scheme for the function, and because people probably know X11
numbering better than GLFW numbering and GLFW doesn't have numbers for
the scroll buttons.
2020-06-06 14:19:06 +02:00
Trygve Aaberge
c78563b452 Report modifier key state when sending move and drag events to the terminal program 2020-06-06 12:28:42 +02:00
Trygve Aaberge
122e172092 Track modifier state correctly
When a modifier key is pressed, that modifier is not included in mods.
When it is released, it is included. Therefore, we have to special case
the modifier keys when storing the modifiers that are active. When a
modifier key is pressed, we add the modifier to
mods_at_last_key_or_button_event, and when it is released, we remove it.

This fixes an issue where move and drag events would still be sent to
the terminal program after pressing shift, but would stop being sent
after releasing shift until another key or button was pressed.
2020-06-06 12:28:42 +02:00
Kovid Goyal
8f85c0d167 Use bypy in update-on-ubuntu 2020-06-06 09:23:36 +05:30
Kovid Goyal
ac6d1ce174 Use bypy in the publish script 2020-06-06 09:19:48 +05:30
Kovid Goyal
03e0bf4c0e Migrate update-on-ox to use bypy 2020-06-06 09:11:50 +05:30
Kovid Goyal
1cb183f5e1 Add a note about what sort of ligatures disable_ligatures affects 2020-06-06 09:11:32 +05:30
Kovid Goyal
d78a553910 Fix #2739 2020-06-06 07:50:43 +05:30
Kovid Goyal
1326532850 macOS: Notarize the kitty application
This required the kitty package to be moved from Frameworks to
Resources, because Apple cant codesign .pyc files and its notarization
requires everything in Frameworks to be signed. Hopefully that does not
break anything. Also removed the kitty-deref-symlink since it is not
needed anyway and was only present for backwards compatibility.

Fixes #2040
2020-06-05 22:52:30 +05:30
Kovid Goyal
408508247d Merge branch 'replace_glfw_true' of https://github.com/Luflosi/kitty 2020-06-05 22:32:53 +05:30
Luflosi
e2be09e028 Remove GLFW_TRUE in comment
In 55ad228166 I forgot to replace `GLFW_TRUE` with `true` in a comment.
2020-06-05 18:51:50 +02:00
Kovid Goyal
c7c14f184c X11: Recompile keymaps on XkbNewKeyboardNotify events
Hopefully fixes #2726
2020-06-05 22:11:15 +05:30
Kovid Goyal
24ca919f33 Merge branch 'fake_scroll_correct_window' of https://github.com/Luflosi/kitty 2020-06-05 20:20:40 +05:30
Luflosi
85fe783652 Fix fake scroll when scrolling in unfocused kitty window
Scrolling should happen in the kitty window under the mouse cursor, not in the currently active kitty window.
2020-06-05 16:44:11 +02:00
Kovid Goyal
04b6bf3b74 bypy builds on macOS 2020-06-05 19:38:00 +05:30
Kovid Goyal
8b2fb105d0 Fix #2735 2020-06-05 19:24:40 +05:30
Kovid Goyal
558caa232d Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-06-05 07:10:39 +05:30
Luflosi
d8886edbeb Null: Make platform more conformant
From upstream: e0c77f71f9.
2020-06-05 00:27:47 +02:00
Kovid Goyal
f72ecf1791 Start work on building macOS binary with bypy 2020-06-04 23:18:48 +05:30
Kovid Goyal
45d1b978d8 bypy now builds the Linux binary 2020-06-04 19:54:01 +05:30
Kovid Goyal
0b780cc065 Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-06-04 19:24:40 +05:30
Luflosi
5263f6598c Add feature available/implemented errors
From upstream: 9a87c2a4b4.
2020-06-04 13:55:52 +02:00
Kovid Goyal
f9e60e7ef6 Start work on using bypy to build the binaries 2020-06-04 13:51:50 +05:30
Kovid Goyal
45c00d0b1f Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-06-04 07:15:09 +05:30
Luflosi
c70e2f7d3e Add statement to docs for glfwTerminate
From upstream: 7486e12f88.
2020-06-03 20:07:09 +02:00
Kovid Goyal
1ec0bed1ce Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-06-03 22:45:24 +05:30
Luflosi
46b5e4513a Win32: Fix VS /W4 compile warnings
From upstream: a84a30ab63.
2020-06-03 19:05:05 +02:00
Kovid Goyal
ffb5196f3b Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-06-03 22:04:21 +05:30
Luflosi
107f1af3c4 Fix desktop GL header not always being suppressed
From upstream: 105cf32e0b.
2020-06-03 11:32:59 +02:00
Kovid Goyal
03b1484c96 Wayland: Abort on fatal error should bypass quit confirmation 2020-06-03 14:39:27 +05:30
Kovid Goyal
aaf64ad63e Add links to config settings to change mouse modifiers in the main mouse docs 2020-06-03 08:17:28 +05:30
Kovid Goyal
f548e795d5 Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-06-02 17:05:02 +05:30
Luflosi
767da68239 Reduce difference to GLFW upstream
Only whitespace, formatting and comments were changed. This does not alter any functionality.
2020-06-02 11:45:08 +02:00
Kovid Goyal
f44e0ff4bd Merge branch 'patch-1' of https://github.com/TheDaemoness/kitty 2020-06-02 10:54:44 +05:30
TheDaemoness
7f226f2ea1 Avoid incrementing hidden cursors' images 2020-06-02 05:10:51 +00:00
Kovid Goyal
1c82a39a6e Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-06-02 08:13:33 +05:30
Kovid Goyal
b70cf276b4 Change the cursor image immediately on scale change 2020-06-02 08:09:27 +05:30
Kovid Goyal
80b2df2ef4 Better code when using a stack cursor 2020-06-02 07:59:32 +05:30
Kovid Goyal
ad27452275 Wayland: Avoid infinite timer fires if arrow cursor is animated 2020-06-02 07:53:15 +05:30
Kovid Goyal
196a0b6b7e Merge branch 'patch-1' of https://github.com/TheDaemoness/kitty 2020-06-02 07:49:57 +05:30
Luflosi
f356f63994 wayland: save serial from all input sources
From upstream: 92b3fd02e5.
2020-06-01 21:29:14 +02:00
TheDaemoness
4947fb6b57 Fix a missing cursorWayland-scale assignment. 2020-06-01 17:41:16 +00:00
Kovid Goyal
622f12b84e Add a changelog entry for wayland hidpi cursors 2020-06-01 21:26:18 +05:30
Kovid Goyal
c1f84ce603 cleanup for wayland hidpi cursors merge 2020-06-01 21:14:56 +05:30
Kovid Goyal
14912461ad Use left_ptr as default arrow cursor on Wayland
Matches X11.
2020-06-01 20:33:54 +05:30
Kovid Goyal
a9f265e8b7 Initialize a couple of local vars 2020-06-01 20:33:54 +05:30
TheDaemoness
199ee20adb Fixed an instance of eager cursor loading.
Also fixed a minor formatting issue.
2020-06-01 20:33:53 +05:30
TheDaemoness
7c3c87abf6 Initial implementation of proper HiDPI cursor support on Wayland. 2020-06-01 20:33:53 +05:30
Kovid Goyal
b0ad44bcf2 Fix #2714 2020-06-01 20:31:05 +05:30
Kovid Goyal
9ba808ec3c Merge branch 'ft/csi-code-rep' of https://github.com/keynslug/kitty 2020-06-01 14:31:34 +05:30
Kovid Goyal
eacf849e3a Add bounds check to left_shift_line()
Fixes #2710
2020-05-31 08:10:58 +05:30
Andrew Mayorov
42c2791182 Do not cap repetitions by row length
Instead cap by 65535 as a safeguard. This is more in line with ECMA 48.
2020-05-29 20:36:30 +03:00
Andrew Mayorov
390e883ecf Update terminfo with CSI REP support 2020-05-29 01:06:16 +03:00
Andrew Mayorov
9fe631ee3f Provide support for CSI REP control code 2020-05-29 01:05:49 +03:00
Kovid Goyal
82d8c4b230 Fix #2699 2020-05-27 20:18:05 +05:30
Kovid Goyal
9775bb0a9d Merge branch 'patch-1' of https://github.com/aslpavel/kitty 2020-05-27 09:05:09 +05:30
Pavel
139c9a2d6d Fix incorrect examples in graphic protocol
Examples in `Deleting images` section were incorrect.
2020-05-26 20:39:31 +01:00
Kovid Goyal
542880f87b Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-05-25 22:17:42 +05:30
Luflosi
3846286bee Wayland: Implement raw mouse motion control
From upstream: 44af6bb936.
2020-05-25 18:21:32 +02:00
Kovid Goyal
72be402a2d Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-05-25 21:47:56 +05:30
Luflosi
c6b9cb759a Wayland: Rename handlers for more unicity
From upstream: 754b7feaa1.

1fa66ca45d didn't include the changes to `wl_window.c`.
2020-05-25 15:29:17 +02:00
Kovid Goyal
13ccf269a6 Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-05-25 15:25:01 +05:30
Luflosi
fcc7a0255a Align joystick axis to gamepad button behavior
From upstream: 02874d9c14.
2020-05-25 11:44:14 +02:00
Luflosi
e7447f38cf Fix half-axis to gamepad button value mapping
From upstream: c32dc3a085.
2020-05-25 11:44:02 +02:00
Kovid Goyal
7ae35631a6 Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-05-25 10:32:54 +05:30
Luflosi
55ad228166 Rename raw input to raw mouse motion, cleanup
From upstream: 1155c83013.
2020-05-24 21:57:29 +02:00
Luflosi
48fb051253 Add GLFW_RAW_INPUT and glfwRawInputSupported
From upstream: 9e29f556fd.
2020-05-24 20:49:52 +02:00
Kovid Goyal
f97e8b7eeb The title for @ set-window-title is optional 2020-05-24 21:51:33 +05:30
Kovid Goyal
84f914742b Fix command names using undersore instead of hyphens in completion for the shell 2020-05-24 21:34:34 +05:30
Kovid Goyal
0d704f3715 kitty shell: show the previously active window id 2020-05-24 16:40:10 +05:30
Kovid Goyal
d8896eb210 Fix #2688 2020-05-24 12:40:54 +05:30
Kovid Goyal
b9d74e2012 Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-05-24 08:16:51 +05:30
Luflosi
803fba9cfd Cocoa: Replace display link with IOKit query
From upstream: 4ec7daf3e9.
2020-05-23 16:28:31 +02:00
Kovid Goyal
581548ed67 Merge branch 'freebsd' of https://github.com/rkitover/kitty 2020-05-23 19:56:51 +05:30
Kovid Goyal
9a8c0f8e7e Merge branch 'cut_utf-8' of https://github.com/Luflosi/kitty 2020-05-23 19:53:40 +05:30
Rafael Kitover
328f569c16 Fix child spawn on FreeBSD.
Fix typo: "#ifdef TIOCSTTY" -> "#ifdef TIOCSCTTY"

Include standard header sys/ioctl.h for ioctl(), needed on MacOS.

Include standard header termios.h for TIOCSCTTY.

Fixes this error on spawn of child shell process:

bash: cannot set terminal process group (-1): Inappropriate ioctl for
device
bash: no job control in this shell

Tested on FreeBSD and Linux.

- Fix #2529

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2020-05-23 12:21:57 +00:00
Luflosi
7931654a7c Ensure valid UTF-8 when cutting string 2020-05-23 13:59:23 +02:00
Kovid Goyal
6eb0c10339 Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-05-23 17:24:03 +05:30
Luflosi
269f294d19 Use CALayer instead of NSView for EGLNativeWindowType
From upstream: 91eebe922d.
2020-05-23 12:48:26 +02:00
Kovid Goyal
d19e2f1350 Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-05-23 16:17:30 +05:30
Luflosi
3b745b82db X11: Clarify function name and comment
From upstream: 5f9dfe3d83.
2020-05-23 11:34:17 +02:00
Kovid Goyal
02f30c2a03 Merge branch 'master' of https://github.com/ahlinc/kitty 2020-05-23 10:47:26 +05:30
Andrew Hlynskyi
54002b386a Fixed issue with a cwd for kitty_mod+h scroll buffer viewer
There is an issue with ability to save scroll buffer from a default less
scroll buffer viewer by just typing relative path to an output file,
because the running less instance inherits cwd from Kitty's cwd
instead of cwd of active window.

How to reproduce:
  1. Start Kitty.
  2. Go to some place.
  3. Press 'kitty_mod+h'
  4. Press 's' and you'll see a less prompt like 'log file:'
  5. Type a just file name without any path and press Enter then press 'q'
  6. See that there is no such file in a current shell directory.
2020-05-23 07:19:45 +03:00
Kovid Goyal
0e2ff077af Fix #2682 2020-05-23 09:03:44 +05:30
Kovid Goyal
012cf70321 Merge branch 'glfw_upstream' of https://github.com/Luflosi/kitty 2020-05-23 07:22:07 +05:30
Luflosi
9da0ba52db X11: Clarify code flow for Clang static analysis
From upstream: ef5220d6b1.
2020-05-22 23:32:12 +02:00
Kovid Goyal
7bfb07e59e Merge branch 'simplify' of https://github.com/Luflosi/kitty 2020-05-22 19:58:48 +05:30
Luflosi
9cb2c4b1ff Simplify code
There is already a check if `m` is `NULL`, so there is no need for a second `NULL` check after that.
2020-05-22 16:04:28 +02:00
Kovid Goyal
8c088fad0f Merge branch 'master' of https://github.com/ahlinc/kitty 2020-05-22 08:23:55 +05:30
Andrew Hlynskyi
392722aed0 Fixed misprint in .github/ISSUE_TEMPLATE/bug_report.md 2020-05-21 22:24:04 +03:00
Kovid Goyal
fabfb30fc4 Merge branch 'convert-tabs-to-spaces' of https://github.com/Luflosi/kitty 2020-05-21 19:51:02 +05:30
Luflosi
c7cb021e92 Convert two tabs to spaces
The rest of the file is also indented with spaces.
2020-05-21 15:34:08 +02:00
Kovid Goyal
be05d9c3e9 Proper confirm_os_window_close check condition 2020-05-21 15:12:20 +05:30
Kovid Goyal
e38690e8d9 Dont link against librt and libdl on OpenBSD
These are part of libc there
2020-05-21 11:43:55 +05:30
Kovid Goyal
7172115559 Merge branch 'patch-1' of https://github.com/TheDaemoness/kitty 2020-05-21 07:37:56 +05:30
TheDaemoness
7314019832 Fixed a typo and added some missing files. 2020-05-20 20:10:37 +00:00
TheDaemoness
b9684941c4 Create .editorconfig
[Most major editors](https://editorconfig.org/#download) support EditorConfig for declaring per-file per-project indentation rules. It costs almost nothing to maintain, and it makes it easier to follow the coding styles already used in kitty.
2020-05-20 19:46:13 +00:00
Kovid Goyal
a27289352c Merge branch 'master' of https://github.com/ahlinc/kitty 2020-05-20 19:56:58 +05:30
Andrew Hlynskyi
dc77009e55 Fixed confirm_os_window_close bug introduced in commit 7bf83603c8 2020-05-20 15:29:21 +03:00
Kovid Goyal
d12e10830d Wayland: Fix kitty being killed on some Wayland compositors if a hidden window has a lot of output
Sway falls over and dies if it receives many render frame
requests. So send only a single one per damaged window and hope and pray
that the compositor hasn't dropped it. Shrug, Wayland, no surprise.
Fixes #2329
2020-05-20 14:19:09 +05:30
Kovid Goyal
936f6c22f0 Merge branch 'configure_library_paths' of https://github.com/Luflosi/kitty 2020-05-20 07:17:07 +05:30
Kovid Goyal
9712be903d Fix #2667 2020-05-20 07:05:35 +05:30
Luflosi
7441cefdbb Make paths to libraries configurable at build time 2020-05-19 18:36:46 +02:00
Kovid Goyal
7bf83603c8 Forgot to treat confirm_os_window_close as a count 2020-05-19 08:19:17 +05:30
Kovid Goyal
6768bc1705 Fix SIGTERM/SIGINT on macOS causing kitty to enter an infinite loop 2020-05-18 17:53:35 +05:30
Kovid Goyal
707cb37212 Have the confirm on close also apply to quitting kitty
Use a dedicated API for handling quit requests on macOS rather than a
quit canary. Also create a mappable "quit" action for all platforms.
2020-05-18 16:06:40 +05:30
Kovid Goyal
1cf0a8b78e Allow only a single OS window close confirmation per OS window 2020-05-18 13:56:19 +05:30
Kovid Goyal
3d2cb37af0 Linux: Workaround for broken Nvidia drivers for old cards
Fixes #456
2020-05-18 13:41:23 +05:30
Kovid Goyal
09fbd6f5e0 Merge branch 'master' of https://github.com/ahlinc/kitty 2020-05-18 09:59:31 +05:30
Andrew Hlynskyi
474bef65cb Fixed marks grouping and coloring for simultaneous multi marks definition 2020-05-18 06:53:56 +03:00
Kovid Goyal
e97454c4a5 And ever more coalesce tests 2020-05-17 23:20:26 +05:30
Kovid Goyal
936dc74a27 Some more coalesce tests 2020-05-17 23:08:27 +05:30
Kovid Goyal
362e4266bb Better coalescing of multiple symbol_maps
When specifying multiple symbol_map values in kitty.conf later ones
should override conflicting previous ones. Fixes #2662
2020-05-17 23:03:28 +05:30
Kovid Goyal
d08f2e10f1 Add bug label to bug bug report template 2020-05-17 13:12:35 +05:30
Kovid Goyal
f1e5ee74a6 Add a feature request template 2020-05-17 13:11:23 +05:30
Kovid Goyal
7832d33348 Add a bug report template 2020-05-17 13:07:49 +05:30
Kovid Goyal
de25f94fae When extending selections to words, recognize ://
As part of the word even if : is not in select_word_chars. This allows
double-clicking to select common URLs with the default select_word_chars
2020-05-16 13:49:39 +05:30
Kovid Goyal
98625e9c36 Add an option confirm_on_os_window_close to ask for confirmation when closing an OS window with multiple kitty windows. 2020-05-15 13:39:58 +05:30
Kovid Goyal
5768cc98b6 ... 2020-05-15 13:31:35 +05:30
Kovid Goyal
da1cc6c1c5 Add a yes/no mode to the ask kitten 2020-05-15 13:16:14 +05:30
Kovid Goyal
50384fac97 DRYer 2020-05-14 09:18:29 +05:30
Kovid Goyal
424bd8a6cd Only compile tab template once 2020-05-14 09:15:11 +05:30
Kovid Goyal
421fae2b57 Merge branch 'fix/tab-title-template-f-string' of https://github.com/chriszarate/kitty 2020-05-14 09:09:18 +05:30
chriszarate
8bb8615a5a Compile template and restrict globals for eval 2020-05-13 23:30:12 -04:00
Kovid Goyal
80f8692e92 Tall/fat layout: Fix resizing of windows when number of windows is <= num of full size windows not working 2020-05-13 23:33:23 +05:30
Kovid Goyal
25623c758d Tall and Fat layouts: Add a `mirrored` option to put the full size window on the opposite edge of the screen
Fixes #2654
2020-05-13 23:19:34 +05:30
Kovid Goyal
e1a0a195a9 Forgot to change one call site for switch_focus_to 2020-05-13 21:39:26 +05:30
Kovid Goyal
f8cf49a90c Merge branch 'simplify' of https://github.com/Luflosi/kitty 2020-05-13 17:22:39 +05:30
Luflosi
ac9f8595c9 DRYer
Don't write `cppflags.extend()` twice.
2020-05-13 13:43:43 +02:00
Kovid Goyal
97f22bbff6 Update changelog 2020-05-13 11:01:04 +05:30
chriszarate
d6ddfa9d18 Use f-string instead of format for tab_title templates 2020-05-12 22:29:03 -04:00
Kovid Goyal
5e48ae8bac pep8 2020-05-12 22:54:08 +05:30
Kovid Goyal
cdd943ded4 Port layout tests to new API 2020-05-12 22:43:55 +05:30
Kovid Goyal
39b2bf963c Port the splits layout to the new groups API 2020-05-12 22:43:54 +05:30
Kovid Goyal
06827fd437 Update docs to point to new layouts package 2020-05-12 22:43:54 +05:30
Kovid Goyal
70ccc1cf6d Port Grid layout to new groups API 2020-05-12 22:43:54 +05:30
Kovid Goyal
f801c47d1e Port vertical/horz layouts to new groups API 2020-05-12 22:43:54 +05:30
Kovid Goyal
01c0e8da93 Tall and Fat layouts ported to new groups API 2020-05-12 22:43:54 +05:30
Kovid Goyal
e844ad6db3 Respect per-window margin/padding in stack layout
Also use the margin/padding of the base window for all overlay windows
2020-05-12 22:43:54 +05:30
Kovid Goyal
3f8c042bc3 Preserve active idx when removing windows/tabs 2020-05-12 22:43:54 +05:30
Kovid Goyal
707ad705a4 Notify on active window changed when adding/removing to same group 2020-05-12 22:43:54 +05:30
Kovid Goyal
79a780004f Remove references to overlay_for from boss 2020-05-12 22:43:54 +05:30
Kovid Goyal
81b28bc1bd More work on refactoring window groups, stack layout works 2020-05-12 22:43:54 +05:30
Kovid Goyal
e9c4d540b1 More work on refactoring window groups 2020-05-12 22:43:54 +05:30
Kovid Goyal
50d9718c68 Start work on refactoring management of windows 2020-05-12 22:43:53 +05:30
Kovid Goyal
e39da2b2bb Merge branch 'icat-http-timeout' of https://github.com/lamby/kitty 2020-05-11 16:58:21 +05:30
Chris Lamb
f08ec1522a Set a default socket timeout when retrieving remote items in icat.
Otherwise we can easily hang when "icat"-ing resources from unreliable
websites/webcams, etc.
2020-05-11 12:24:57 +01:00
Kovid Goyal
e61e93712c Merge branch 'patch-3' of https://github.com/ctrlcctrlv/kitty 2020-05-11 11:47:14 +05:30
Fredrick Brennan
c9e198e02e Graphics protocol: note best detection method
Per https://github.com/kovidgoyal/kitty/issues/2636#issuecomment-626467852
2020-05-10 23:02:12 -07:00
Kovid Goyal
0ce181b578 Fix #2639 2020-05-10 21:28:55 +05:30
Kovid Goyal
99950fe448 DECQRM responses for mouse tracking modes 2020-05-09 20:55:51 +05:30
Kovid Goyal
d8daf84a09 Fix #2636 2020-05-09 17:21:23 +05:30
Kovid Goyal
3d32202b3a version 0.17.4 2020-05-09 10:48:21 +05:30
Kovid Goyal
4f4d562670 Fix background_tint making the window semi-transparent
Fixes #2618
2020-05-09 09:06:18 +05:30
Kovid Goyal
28fc9c69da Allow showing the name of the current layout and the number of windows in tab titles
Fixes #2634
2020-05-09 08:00:17 +05:30
Kovid Goyal
6edf00e699 Wayland: Fix OS window title not updating for hidden windows
Fixes #2629
2020-05-05 17:51:36 +05:30
Kovid Goyal
a0d6445dda Move expandvars to utils 2020-05-05 08:22:08 +05:30
Kovid Goyal
e74797037c ... 2020-05-04 13:30:35 +05:30
Kovid Goyal
3fb125dba7 Fix mypy editor integration for changes in syntastic 2020-05-04 13:19:03 +05:30
Kovid Goyal
3c8640f9ad Infrastructure for close confirmation 2020-05-02 18:19:08 +05:30
Kovid Goyal
2e63a4c83f Wayland: Remove unused link-time dependency
6aca3e99f0
2020-05-01 15:00:38 +05:30
Kovid Goyal
9da5bee178 X11: Filter out Xlib errors from other connections
2c8e0512dd
2020-05-01 14:59:16 +05:30
Kovid Goyal
2fc6f3b542 Merge changes from upstream
e65de2941c
2020-05-01 14:57:55 +05:30
Kovid Goyal
4754f42e51 Merge branch 'omit-final-tab-separator' of https://github.com/trygveaa/kitty 2020-05-01 14:05:26 +05:30
Trygve Aaberge
941fe918fb Omit final tab separator
This makes tab separators only drawn between the tabs.

Relates to #2616
2020-05-01 10:29:18 +02:00
Kovid Goyal
f4e82e972e ... 2020-05-01 08:10:11 +05:30
Kovid Goyal
f66dbf111d Pass env to get_vcs_revs() 2020-05-01 08:03:31 +05:30
Kovid Goyal
1a2da5c49d Use python3 in session.vim 2020-04-30 23:12:37 +05:30
Kovid Goyal
8327dabb29 Allow @ send-text over a socket to work with more than 1MB of text 2020-04-30 08:58:46 +05:30
Kovid Goyal
195ea6c140 Dont send back an rc disallowed response for commands that dont expect responses 2020-04-29 21:53:20 +05:30
Kovid Goyal
fcc0707174 Fix kitty @ send-text not working with text larger than 1024 bytes when using --listen-on
Fixes #2607
2020-04-29 21:48:09 +05:30
Kovid Goyal
aa9c3cd634 Report modifier key state when sending wheel events to the terminal program 2020-04-29 20:02:55 +05:30
Kovid Goyal
b541341681 Avoid use of goto 2020-04-29 14:37:26 +05:30
Kovid Goyal
e5d50aca6d ... 2020-04-29 09:21:36 +05:30
Kovid Goyal
b738f4d4ff Fix a regression that broke drag and drop on macOS
Fixes #2605
2020-04-29 09:12:04 +05:30
Kovid Goyal
5c38aabf0e ... 2020-04-29 07:50:01 +05:30
Kovid Goyal
03e9ed6947 Fix set_window_padding() only working on first window 2020-04-28 22:34:32 +05:30
Kovid Goyal
63493fad22 Fix a regression that caused clicking in the padding/margins of windows in the stack layout to switch the window to the first window
Fixes #2604
2020-04-28 22:23:38 +05:30
Kovid Goyal
696b857f91 By default, double clicking no longer considers the : as part of words
Fixes #2602
2020-04-28 19:57:40 +05:30
Kovid Goyal
53ae2bbe0d Merge branch 'use_loop' of https://github.com/Luflosi/kitty 2020-04-28 19:40:40 +05:30
Luflosi
01069e919d Use loop instead of calling dlopen() multiple times
Having an array of library names and iterating over it looks nicer than having several variables and calling `dlopen()` multiple times manually.
It should also make it easier to use custom library names instead by using the preprocessor, which I plan to do in the near future.
2020-04-28 15:53:58 +02:00
Kovid Goyal
fc0095f505 Merge branch 'master' of https://github.com/ixydo/kitty 2020-04-27 21:34:52 +05:30
Jinn Koriech
421528e130 Update note about icat + tmux to be more generic 2020-04-27 16:40:24 +01:00
Jinn Koriech
7b70356515 Add note clarifying that icat won't work with tmux
When using icat with tmux we get the following:

```
$ kitty icat ~/Pictures/fc.jpg 
Terminal does not support reporting screen sizes via the TIOCGWINSZ ioctl
```

I found a suggestion[1] this is due to limitations between kitty and tmux, so I'm raising this PR to either confirm this is the case and make it clear in the documentation, or to have it rejected so an issue can be raised to address the feature.

1: https://unix.stackexchange.com/a/484776/41996
2020-04-27 14:11:16 +01:00
Kovid Goyal
e803505aad CoreText: When centering glyphs ignore negative origins
This is needed for FiraCode ligatures to line up correctly. See
https://github.com/kovidgoyal/kitty/issues/2591#issuecomment-619937030
2020-04-27 18:02:53 +05:30
Kovid Goyal
ad1ff455a0 macOS: Fix a regression in the previous release that caused ligatures to be not be centered horizontally
Fixes #2591
2020-04-27 16:17:05 +05:30
Kovid Goyal
7692cb5c3d Merge branch 'graphics-big-endian' of https://github.com/jamessan/kitty 2020-04-26 09:36:53 +05:30
James McCoy
a94e208db3 Use T_PYSSIZET for GraphicsManager.image_count
GraphicsManager.image_count is declared as a size_t, so the Python type
needs to be the same width.  On 64-bit big-endian architectures, this
mismatch was showing up as test failures:

    ======================================================================
    FAIL: test_gr_delete (kitty_tests.graphics.TestGraphics)
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "/<<PKGBUILDDIR>>/kitty_tests/graphics.py", line 319, in test_gr_delete
        self.ae(len(layers(s)), 0), self.ae(s.grman.image_count, 1)
        ch = 20
        cw = 10
        delete = <function TestGraphics.test_gr_delete.<locals>.delete at 0x3ffb7fc38b0>
        dx = 0.2
        dy = 0.4
        layers = <function put_helpers.<locals>.layers at 0x3ffb77709d0>
        put_image = <function put_helpers.<locals>.put_image at 0x3ffb77708b0>
        put_ref = <function put_helpers.<locals>.put_ref at 0x3ffb7770940>
        rect_eq = <function put_helpers.<locals>.rect_eq at 0x3ffb7770a60>
        s = <fast_data_types.Screen object at 0x1b60df0>
        self = <kitty_tests.graphics.TestGraphics testMethod=test_gr_delete>
    AssertionError: 0 != 1
2020-04-25 23:25:18 -04:00
Kovid Goyal
ba9d5cf3ff Merge branch 'use-fPIC' of https://github.com/jamessan/kitty 2020-04-25 21:11:10 +05:30
James McCoy
0dff0d03f9 Use -fPIC instead of -fpic
With large objects, use of -fpic will fail to link on certain
architectures due to size limitations of the global offset table (GOT).
For example, the link stage for glfw-wayland.so fails on sparc64, as
below.  Using -fPIC avoids these size limitations.

    ./glfw/osmesa_context.c:73:(.text+0xf2c): relocation truncated to fit: R_SPARC_GOT13 against symbol `_glfw' defined in .bss section in /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o
    /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o: in function `swapIntervalEGL':
    ./glfw/egl_context.c:238:(.text+0xf68): relocation truncated to fit: R_SPARC_GOT13 against symbol `_glfw' defined in .bss section in /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o
    /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o: in function `destroyContextOSMesa':
    ./glfw/osmesa_context.c:80:(.text+0x10a4): relocation truncated to fit: R_SPARC_GOT13 against symbol `_glfw' defined in .bss section in /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o
    /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o: in function `data_offer_action':
    ./glfw/wl_window.c:1692:(.text+0x15e4): relocation truncated to fit: R_SPARC_GOT13 against symbol `_glfw' defined in .bss section in /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o
    /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o: in function `data_offer_source_actions':
    ./glfw/wl_window.c:1683:(.text+0x1648): relocation truncated to fit: R_SPARC_GOT13 against symbol `_glfw' defined in .bss section in /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o
    /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o: in function `keyboardHandleRepeatInfo':
    ./glfw/wl_init.c:471:(.text+0x1704): relocation truncated to fit: R_SPARC_GOT13 against symbol `_glfw' defined in .bss section in /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o
    /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o: in function `checkScaleChange':
    ./glfw/wl_window.c:55:(.text+0x1768): relocation truncated to fit: R_SPARC_GOT13 against symbol `_glfw' defined in .bss section in /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o
    /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o: in function `primary_selection_copy_callback_done':
    ./glfw/wl_window.c:1833:(.text+0x18bc): relocation truncated to fit: R_SPARC_GOT13 against symbol `_glfw' defined in .bss section in /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o
    /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o: in function `clipboard_copy_callback_done':
    ./glfw/wl_window.c:1825:(.text+0x1910): relocation truncated to fit: R_SPARC_GOT13 against symbol `_glfw' defined in .bss section in /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o
    /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o: in function `primary_selection_source_canceled':
    ./glfw/wl_window.c:1582:(.text+0x1968): relocation truncated to fit: R_SPARC_GOT13 against symbol `_glfw' defined in .bss section in /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o
    /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o: in function `data_source_canceled':
    ./glfw/wl_window.c:1576:(.text+0x19c0): additional relocation overflows omitted from the output
    collect2: error: ld returned 1 exit status
2020-04-25 10:08:28 -04:00
Kovid Goyal
c92aca5d19 Use a Literal as the type for choice based options 2020-04-24 19:45:51 +05:30
Kovid Goyal
5762baeed7 Typing fixes 2020-04-24 19:27:22 +05:30
Kovid Goyal
601cf01222 Fix #2581 2020-04-24 19:24:11 +05:30
Kovid Goyal
0eef48b516 Use literals as the type for cli opts that take choices 2020-04-24 15:28:04 +05:30
Kovid Goyal
3c1954f329 ... 2020-04-24 15:07:44 +05:30
Kovid Goyal
cc29640604 version 0.17.3 2020-04-23 08:39:07 +05:30
Kovid Goyal
d0329460b8 Fix #2573 2020-04-21 22:04:34 +05:30
Kovid Goyal
717c0c96de Allow mapping arbitrary remote control commands to key presses in kitty.conf 2020-04-19 20:14:23 +05:30
Kovid Goyal
b3f161efce ... 2020-04-19 19:39:56 +05:30
Kovid Goyal
0286940163 Allow changing margings/padding via remote control
Fixes #2546
2020-04-19 19:29:36 +05:30
Kovid Goyal
d4a4ee386f Fix minimal borders in grid layout 2020-04-19 17:58:35 +05:30
Kovid Goyal
33498f9133 ... 2020-04-19 11:01:22 +05:30
Kovid Goyal
da3d4d1bef ... 2020-04-19 09:51:06 +05:30
Kovid Goyal
c69b8870d2 Allow individually setting margins and padding for each edge (left, right, top, bottom) 2020-04-19 09:36:26 +05:30
Kovid Goyal
85b55b31b6 Add a new :opt:listen_on option in kitty.conf
Also allow using environment variables in this option
Fixes #2569
2020-04-19 09:23:52 +05:30
Kovid Goyal
948919e42b Forgot to apply --title on creation of subsequent OS windows
Also update changelog
2020-04-19 08:42:30 +05:30
Kovid Goyal
85c40090ed Fix #2570 2020-04-19 08:36:29 +05:30
Kovid Goyal
98b50ec145 ... 2020-04-19 08:28:40 +05:30
Kovid Goyal
1ab4f7d2d0 Fix a regression in 0.17 that broke displaying graphics over SSH
The new types GraphicsCommand can generate a command with only a payload
and no control keys, as the last command during chunked transfer.
Fix the parser to handle this. Fixes #2568
2020-04-19 08:13:33 +05:30
Kovid Goyal
98801b6fa6 Update changelog 2020-04-19 07:22:02 +05:30
Kovid Goyal
bdd68284b5 Merge branch 'terminfo' of https://github.com/wladh/kitty 2020-04-19 07:21:10 +05:30
Vlad Hanciuta
a61d70ea9a Add strikethrough capabilities to terminfo
The termcap aliases are not standard, but they are used by Vim, it
seems. As far as I can tell, there are no standard termcap attribute for
strikethrough.
2020-04-18 16:37:09 +01:00
Vlad Hanciuta
e271719dd2 Add strikeout capability to terminfo 2020-04-18 16:05:30 +01:00
Kovid Goyal
a08efa1fc5 Fix failing test 2020-04-17 19:57:37 +05:30
Kovid Goyal
ffac3600e0 Layouts: Apply per window padding and margin values
Also make the calculation of blank rects more robust and with less code,
which should hopefully fix #1762
2020-04-17 19:47:34 +05:30
Kovid Goyal
6499f8ac24 Allow window objects to store custom margin/padding 2020-04-17 09:02:08 +05:30
Kovid Goyal
63307cb1d8 Code to serialize state 2020-04-17 08:42:45 +05:30
Kovid Goyal
0a6fb362f6 kitty @ kitten: Allow no_ui kittens to return data to the remote client
Fixes #2552 since the OP can now write a kitten to query the window
manager and return position data even over SSH.
2020-04-15 08:29:55 +05:30
Kovid Goyal
457fcbfb90 Merge branch 'master' of https://github.com/akobel/kitty 2020-04-15 07:44:27 +05:30
Alexander Kobel
3b6277b9e2 Hide "No render frame received" without --debug-rendering 2020-04-14 19:36:58 +02:00
Alexander Kobel
e6e61028b1 Rename --debug-gl to --debug-rendering 2020-04-14 19:35:40 +02:00
Kovid Goyal
e9be66126b Merge branch 'hints-support-empty-word-characters' of https://github.com/trygveaa/kitty 2020-04-14 08:17:13 +05:30
Trygve Aaberge
681fe7f2f2 Don't set word_characters to select_by_word_characters when empty string
This allows you to pass in --word-characters "" which will make hints
only match \w.
2020-04-13 23:02:44 +02:00
Kovid Goyal
0196920533 icat kitten: Fix display of 16-bit depth images
Fixes #2542
2020-04-13 23:05:10 +05:30
Kovid Goyal
5f5bb3ace0 Update Changelog 2020-04-12 20:10:29 +05:30
Kovid Goyal
3c970e0122 Merge branch 'kitten-send-mouse-events' of https://github.com/trygveaa/kitty 2020-04-12 20:09:45 +05:30
Trygve Aaberge
43af6e3b8a Add support for sending mouse events from a kitten
This allows you to a control a program running in kitty from a kitten
using mouse events. If the program is not receiving mouse events of that
type, it is not sent.
2020-04-12 16:19:50 +02:00
Kovid Goyal
ac8cfc12fb Merge branch 'docs-fix-kitten-custom-indentation' of https://github.com/trygveaa/kitty 2020-04-12 19:49:18 +05:30
Trygve Aaberge
b19472e0f5 Fix indentation in kittens/custom.rst to 4 spaces
This file used 3 spaces some places and 4 spaces other places. Other
files like diff.rst and hints.rst use 4 spaces.
2020-04-12 16:04:32 +02:00
Kovid Goyal
b4d08044a0 ... 2020-04-12 17:01:39 +05:30
Kovid Goyal
b2fe02e63f Merge branch 'patch-1' of https://github.com/LesnyRumcajs/kitty 2020-04-12 16:55:10 +05:30
Kovid Goyal
d1a5d30233 Add another test for marking 2020-04-12 16:54:38 +05:30
LesnyRumcajs
bba30138e1 Update Fedora link
The link was pointing to a deprecated `copr`. Let's point it to official Fedora repos! (as copr page suggests anyway)
2020-04-12 13:13:02 +02:00
Kovid Goyal
082546a1e7 Marks: Fix marks not handling wide characters and tab characters correctly
Fixes #2534
2020-04-12 13:28:21 +05:30
Kovid Goyal
41ffd58e26 A bit of cleanup 2020-04-11 21:22:17 +05:30
Kovid Goyal
b4f24e4e1e Merge branch 'must_draw_borders_flag' of https://github.com/franco/kitty 2020-04-11 21:19:52 +05:30
Franco Sebregondi
75c8ffd715 Add flag to Layout to enforce drawing a border
Layout.must_draw_borders = True enforces drawing of a border even
though only a single window is visible. See #2531.
2020-04-11 17:01:44 +02:00
Kovid Goyal
688606ff7c Merge branch 'ignore-keyboard-layout-switch-keys' of https://github.com/trygveaa/kitty 2020-04-10 21:46:07 +05:30
Trygve Aaberge
2e3f9dffa7 Ignore keys for switching keyboard layout
XKB has various options for using keys to switch the keyboard layout.
When these are set, XKB will return keysyms which are unknown to GLFW,
so kitty will fallback to using a keymap without the options set, which
causes the keys to be interpreted as the original keysyms.

However, when these options are set, kitty shouldn't interpret the keys.
Therefore, check for some specific keysyms and return before translating
to GLFW keysyms.

There may be more keysyms which should be ignored, but as far as I can
see from https://gitlab.freedesktop.org/xkeyboard-config/xkeyboard-config/-/blob/xkeyboard-config-2.29/symbols/group
these are the ones which can be triggered by XKB options.

Fixes #2519
2020-04-10 17:21:53 +02:00
Kovid Goyal
859033415b macOS: Fix incorrect horizontal positioning of some full-width East Asian characters
Fixes #1457
Also render multi-cell PUA characters centered, matching behavior on other platforms
2020-04-09 13:25:19 +05:30
Kovid Goyal
1f1b52b580 Fix specifying options for layouts in the startup session file not working
Fixes #2520
2020-04-09 12:40:09 +05:30
Kovid Goyal
4e7bf80447 diff kitten: New option to control the background color for filler lines in the margin
Fixes #2518
2020-04-08 20:42:49 +05:30
Kovid Goyal
e39df50884 Merge branch 'patch-1' of https://github.com/georgeabr/kitty 2020-04-08 18:56:15 +05:30
George Abraham
8d6a9d45c2 Changing match target to "font"
As suggested in #2516, changing match target to "font" in the fonts.conf file.
2020-04-08 14:02:54 +01:00
Kovid Goyal
0862e85577 Rewrite wcswidth as a state machine 2020-04-07 10:10:30 +05:30
Kovid Goyal
0b9a37139e Render any flag pair in two cells not just known ones 2020-04-06 23:00:34 +05:30
Kovid Goyal
6357f7b811 Update wcswidth() to match new country flag rendering 2020-04-06 22:56:34 +05:30
Kovid Goyal
24197dc422 Render known country flags designated by a pair of unicode codepoints in two cells instead of four. 2020-04-06 22:16:59 +05:30
Kovid Goyal
9bc2ab3245 Function to detect flag pairs 2020-04-06 21:16:14 +05:30
Kovid Goyal
bf4e8c490c Update to Unicode 13.0
Fixes #2513
2020-04-06 18:59:35 +05:30
Kovid Goyal
590a4d70ee Fix --stdin-add-formatting not working
Fixes #2512
2020-04-06 09:35:16 +05:30
Kovid Goyal
40341d2ddd ... 2020-04-04 09:17:38 +05:30
Kovid Goyal
0cd2c41ee3 Update changelog 2020-04-03 22:42:06 +05:30
Kovid Goyal
5d65cdb6a8 Fix reverse video not being rendered correctly when using transparency or a background image
Fixes #2419
2020-04-03 22:40:33 +05:30
Kovid Goyal
759a15ccbb X11: Handle invalid data being sent with drag events by some applications
Fixes #2505
2020-04-02 21:01:19 +05:30
Kovid Goyal
5aead5eebe Replace new-window with launch in the docs 2020-04-01 08:39:37 +05:30
Kovid Goyal
a325a5fdee Merge branch 'strikethrough-info-from-font' of https://github.com/trygveaa/kitty 2020-03-31 09:34:07 +05:30
Trygve Aaberge
931e91f1a7 Read strikethrough info from font when using FreeType
This reads the strikethrough position and thickness from the font so it
is rendered correctly.

This is only implemented for FreeType, not Core Text, because I didn't
find any way to get the info from Core Text, and I don't have a Mac to
test it on either. When using Core Text or when the font doesn't provide
the info, the same approximation as before is used.
2020-03-30 20:09:30 +02:00
Kovid Goyal
6e13509720 Allow entry points and external command combined completion on first word 2020-03-30 11:38:54 +05:30
Kovid Goyal
5b4844e71e oops
Broke normal completion in previous commit. Delegate should only be used
when actual unknown words are present
2020-03-30 11:12:00 +05:30
Kovid Goyal
66cbbea9bb Add completion for executables at kitty invocation in zsh
So now kitty vi<TAB> and kitty vim something<TAB> delegate
completion to zsh for the command that will be executed inside kitty.
2020-03-30 10:54:17 +05:30
184 changed files with 38850 additions and 32230 deletions

21
.editorconfig Normal file
View File

@@ -0,0 +1,21 @@
root = true
[*]
indent_style = spaces
indent_size = 4
end_of_line = lf
trim_trailing_whitespace = true
[{Makefile,*.terminfo}]
indent_style = tab
# Autogenerated files with tabs below this line.
[kitty/{unicode-data.c,emoji.h,wcwidth-std.h}]
indent_style = tab
[kittens/unicode_input/names.h]
indent_style = tab
[glfw/wayland-*-protocol.{c,h}]
indent_style = tab

34
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,34 @@
---
name: Bug report
about: Create a bug report
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. XXX
2. YYY
3. ZZZ
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Environment details**
OS: Name and version of operating system(s)
```
Output of kitty --debug-config
```
**Additional context**
Try to reproduce the problem with `kitty --config NONE` if you cannot then post a minimal kitty.conf that reproduces the problem. If the problem involves interaction with some other terminal program post a minimal config for that program to reproduce the problem as well.

View File

@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -106,7 +106,7 @@ def main():
if is_bundle:
setup_bundle_env()
else:
if not is_macos:
if not is_macos and 'pythonLocation' in os.environ:
os.environ['LD_LIBRARY_PATH'] = '{}/lib'.format(os.environ['pythonLocation'])
action = sys.argv[-1]
if action in ('build', 'package'):

41
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
name: "Code scanning - action"
on:
push:
branches: [master, ]
pull_request:
# The branches below must be a subset of the branches above
branches: [master]
schedule:
- cron: '0 22 * * 5'
jobs:
CodeQL-Build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
# Override language selection by uncommenting this and choosing your languages
# with:
# languages: go, javascript, csharp, python, cpp, java
- name: Build kitty
run: python3 .github/workflows/ci.py build
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

1
.gitignore vendored
View File

@@ -18,3 +18,4 @@ __pycache__/
/docs/generated/
/.mypy_cache
.DS_Store
bypy/b

View File

@@ -1,5 +1,5 @@
pkg-config
python
imagemagick
harfbuzz
python
imagemagick
harfbuzz
sphinx-doc

View File

@@ -1,7 +1,7 @@
### Reporting bugs
Please first search existing bug reports (especially closed ones) for a report
that matches your issue.
that matches your issue.
When reporting a bug, provide full details of your environment, that means, at
a minimum, kitty version, OS and OS version, kitty config (ideally a minimal

91
bypy/init_env.py Normal file
View File

@@ -0,0 +1,91 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
import os
import re
import shutil
import subprocess
import sys
from contextlib import suppress
from bypy.constants import (
LIBDIR, PREFIX, PYTHON, SRC as KITTY_DIR, ismacos, worker_env
)
from bypy.utils import run_shell
def read_src_file(name):
with open(os.path.join(KITTY_DIR, 'kitty', name), 'rb') as f:
return f.read().decode('utf-8')
def initialize_constants():
kitty_constants = {}
src = read_src_file('constants.py')
nv = re.search(r'Version\((\d+), (\d+), (\d+)\)', src)
kitty_constants['version'] = '%s.%s.%s' % (nv.group(1), nv.group(2), nv.group(3))
kitty_constants['appname'] = re.search(
r'appname: str\s+=\s+(u{0,1})[\'"]([^\'"]+)[\'"]', src
).group(2)
return kitty_constants
def run(*args, **extra_env):
env = os.environ.copy()
env.update(worker_env)
env.update(extra_env)
env['SW'] = PREFIX
env['LD_LIBRARY_PATH'] = LIBDIR
if ismacos:
env['PKGCONFIG_EXE'] = os.path.join(PREFIX, 'bin', 'pkg-config')
cwd = env.pop('cwd', KITTY_DIR)
return subprocess.call(list(args), env=env, cwd=cwd)
def build_c_extensions(ext_dir, args):
writeable_src_dir = os.path.join(ext_dir, 'src')
shutil.copytree(
KITTY_DIR, writeable_src_dir, symlinks=True,
ignore=shutil.ignore_patterns('b', 'build', 'dist', '*_commands.json', '*.o'))
# Build the launcher as it is needed for the spawn test
with suppress(FileNotFoundError):
os.remove(os.path.join(writeable_src_dir, 'kitty', 'launcher', 'kitty'))
if run(PYTHON, 'setup.py', 'build-launcher', cwd=writeable_src_dir) != 0:
print('Building of kitty launcher failed', file=sys.stderr)
os.chdir(KITTY_DIR)
run_shell()
raise SystemExit('Building of kitty launcher failed')
cmd = [PYTHON, 'setup.py']
bundle = 'macos-freeze' if ismacos else 'linux-freeze'
cmd.append(bundle)
dest = kitty_constants['appname'] + ('.app' if ismacos else '')
dest = os.path.join(ext_dir, dest)
cmd += ['--prefix', dest]
if run(*cmd, cwd=writeable_src_dir) != 0:
print('Building of kitty package failed', file=sys.stderr)
os.chdir(KITTY_DIR)
run_shell()
raise SystemExit('Building of kitty package failed')
return ext_dir
def run_tests(path_to_kitty, cwd_on_failure):
kw = {'cwd': cwd_on_failure}
if not ismacos:
# this is needed for the spawn test which starts an interpreter
# using the kitty launcher.
kw['PYTHONHOME'] = PREFIX
ret = run(PYTHON, 'test.py', **kw)
if ret != 0:
os.chdir(cwd_on_failure)
print(
'running kitty tests failed with return code:', ret, file=sys.stderr)
run_shell()
raise SystemExit('running kitty tests failed')
if __name__ == 'program':
kitty_constants = initialize_constants()

3
bypy/linux.conf Normal file
View File

@@ -0,0 +1,3 @@
image 'https://partner-images.canonical.com/core/xenial/current/ubuntu-xenial-core-cloudimg-{}-root.tar.gz'
deps 'bison flex libxcursor-dev libxrandr-dev libxi-dev libxinerama-dev libgl1-mesa-dev libxcb-xkb-dev libfontconfig1-dev libdbus-1-dev'

209
bypy/linux/__main__.py Normal file
View File

@@ -0,0 +1,209 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
import errno
import os
import shutil
import stat
import subprocess
import tarfile
import time
from bypy.constants import (
OUTPUT_DIR, PREFIX, is64bit, python_major_minor_version
)
from bypy.utils import get_dll_path, py_compile, walk
j = os.path.join
self_dir = os.path.dirname(os.path.abspath(__file__))
arch = 'x86_64' if is64bit else 'i686'
py_ver = '.'.join(map(str, python_major_minor_version()))
iv = globals()['init_env']
kitty_constants = iv['kitty_constants']
def binary_includes():
return tuple(map(get_dll_path, (
'expat', 'sqlite3', 'ffi', 'z', 'lzma', 'png16',
# dont include freetype because fontconfig is closely coupled to it
# and also distros often patch freetype
# 'iconv.so.2', 'pcre.so.1', 'graphite2.so.3', 'glib-2.0.so.0', 'freetype.so.6',
'harfbuzz', 'xkbcommon', 'xkbcommon-x11',
'ncursesw', 'readline',
))) + (
get_dll_path('bz2', 2), get_dll_path('ssl', 3), get_dll_path('crypto', 3),
get_dll_path('python' + py_ver, 2),
)
class Env:
def __init__(self, package_dir):
self.base = package_dir
self.lib_dir = j(self.base, 'lib')
self.py_dir = j(self.lib_dir, 'python' + py_ver)
os.makedirs(self.py_dir)
self.bin_dir = j(self.base, 'bin')
def ignore_in_lib(base, items, ignored_dirs=None):
ans = []
if ignored_dirs is None:
ignored_dirs = {'.svn', '.bzr', '.git', 'test', 'tests', 'testing'}
for name in items:
path = j(base, name)
if os.path.isdir(path):
if name in ignored_dirs or not os.path.exists(j(path, '__init__.py')):
if name != 'plugins':
ans.append(name)
else:
if name.rpartition('.')[-1] not in ('so', 'py'):
ans.append(name)
return ans
def import_site_packages(srcdir, dest):
if not os.path.exists(dest):
os.mkdir(dest)
for x in os.listdir(srcdir):
ext = x.rpartition('.')[-1]
f = j(srcdir, x)
if ext in ('py', 'so'):
shutil.copy2(f, dest)
elif ext == 'pth' and x != 'setuptools.pth':
for line in open(f, 'rb').read().splitlines():
src = os.path.abspath(j(srcdir, line))
if os.path.exists(src) and os.path.isdir(src):
import_site_packages(src, dest)
elif os.path.exists(j(f, '__init__.py')):
shutil.copytree(f, j(dest, x), ignore=ignore_in_lib)
def copy_libs(env):
print('Copying libs...')
for x in binary_includes():
dest = env.bin_dir if '/bin/' in x else env.lib_dir
shutil.copy2(x, dest)
def copy_python(env):
print('Copying python...')
srcdir = j(PREFIX, 'lib/python' + py_ver)
for x in os.listdir(srcdir):
y = j(srcdir, x)
ext = os.path.splitext(x)[1]
if os.path.isdir(y) and x not in ('test', 'hotshot', 'distutils', 'tkinter', 'turtledemo',
'site-packages', 'idlelib', 'lib2to3', 'dist-packages'):
shutil.copytree(y, j(env.py_dir, x), ignore=ignore_in_lib)
if os.path.isfile(y) and ext in ('.py', '.so'):
shutil.copy2(y, env.py_dir)
srcdir = j(srcdir, 'site-packages')
dest = j(env.py_dir, 'site-packages')
import_site_packages(srcdir, dest)
py_compile(env.py_dir)
py_compile(os.path.join(env.base, 'lib', 'kitty'))
def is_elf(path):
with open(path, 'rb') as f:
return f.read(4) == b'\x7fELF'
def fix_permissions(files):
for path in files:
os.chmod(path, 0o755)
STRIPCMD = ['strip']
def find_binaries(env):
files = {j(env.bin_dir, x) for x in os.listdir(env.bin_dir)} | {
x for x in {
j(os.path.dirname(env.bin_dir), x) for x in os.listdir(env.bin_dir)} if os.path.exists(x)}
for x in walk(env.lib_dir):
x = os.path.realpath(x)
if x not in files and is_elf(x):
files.add(x)
return files
def strip_files(files, argv_max=(256 * 1024)):
""" Strip a list of files """
while files:
cmd = list(STRIPCMD)
pathlen = sum(len(s) + 1 for s in cmd)
while pathlen < argv_max and files:
f = files.pop()
cmd.append(f)
pathlen += len(f) + 1
if len(cmd) > len(STRIPCMD):
all_files = cmd[len(STRIPCMD):]
unwritable_files = tuple(filter(None, (None if os.access(x, os.W_OK) else (x, os.stat(x).st_mode) for x in all_files)))
[os.chmod(x, stat.S_IWRITE | old_mode) for x, old_mode in unwritable_files]
subprocess.check_call(cmd)
[os.chmod(x, old_mode) for x, old_mode in unwritable_files]
def strip_binaries(files):
print('Stripping %d files...' % len(files))
before = sum(os.path.getsize(x) for x in files)
strip_files(files)
after = sum(os.path.getsize(x) for x in files)
print('Stripped %.1f MB' % ((before - after) / (1024 * 1024.)))
def create_tarfile(env, compression_level='9'):
print('Creating archive...')
base = OUTPUT_DIR
try:
shutil.rmtree(base)
except OSError as err:
if err.errno != errno.ENOENT:
raise
os.mkdir(base)
dist = os.path.join(base, '%s-%s-%s.tar' % (kitty_constants['appname'], kitty_constants['version'], arch))
with tarfile.open(dist, mode='w', format=tarfile.PAX_FORMAT) as tf:
cwd = os.getcwd()
os.chdir(env.base)
try:
for x in os.listdir('.'):
tf.add(x)
finally:
os.chdir(cwd)
print('Compressing archive...')
ans = dist.rpartition('.')[0] + '.txz'
start_time = time.time()
subprocess.check_call(['xz', '--threads=0', '-f', '-' + compression_level, dist])
secs = time.time() - start_time
print('Compressed in %d minutes %d seconds' % (secs // 60, secs % 60))
os.rename(dist + '.xz', ans)
print('Archive %s created: %.2f MB' % (
os.path.basename(ans), os.stat(ans).st_size / (1024.**2)))
def main():
args = globals()['args']
ext_dir = globals()['ext_dir']
if not args.skip_tests:
run_tests = iv['run_tests']
run_tests(None, os.path.join(ext_dir, 'src'))
env = Env(os.path.join(ext_dir, kitty_constants['appname']))
copy_libs(env)
copy_python(env)
files = find_binaries(env)
fix_permissions(files)
if not args.dont_strip:
strip_binaries(files)
create_tarfile(env, args.compression_level)
if __name__ == '__main__':
main()

6
bypy/macos.conf Normal file
View File

@@ -0,0 +1,6 @@
# Requires installation of XCode 10.3 and Python 3 and
# python3 -m pip install certifi
vm_name 'macos-kitty-build'
root '/Users/Shared/kitty-build'
python '/usr/local/bin/python3'

459
bypy/macos/__main__.py Normal file
View File

@@ -0,0 +1,459 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
import glob
import json
import os
import shutil
import stat
import subprocess
import sys
import tempfile
import zipfile
from bypy.constants import PREFIX, PYTHON, SW, python_major_minor_version
from bypy.macos_sign import (
codesign, create_entitlements_file, make_certificate_useable, notarize_app,
verify_signature
)
from bypy.utils import current_dir, py_compile, run_shell, timeit, walk
iv = globals()['init_env']
kitty_constants = iv['kitty_constants']
join = os.path.join
basename = os.path.basename
abspath = os.path.abspath
APPNAME = kitty_constants['appname']
VERSION = kitty_constants['version']
py_ver = '.'.join(map(str, python_major_minor_version()))
def flush(func):
def ff(*args, **kwargs):
sys.stdout.flush()
sys.stderr.flush()
ret = func(*args, **kwargs)
sys.stdout.flush()
sys.stderr.flush()
return ret
return ff
def flipwritable(fn, mode=None):
"""
Flip the writability of a file and return the old mode. Returns None
if the file is already writable.
"""
if os.access(fn, os.W_OK):
return None
old_mode = os.stat(fn).st_mode
os.chmod(fn, stat.S_IWRITE | old_mode)
return old_mode
STRIPCMD = ('/usr/bin/strip', '-x', '-S', '-')
def strip_files(files, argv_max=(256 * 1024)):
"""
Strip a list of files
"""
tostrip = [(fn, flipwritable(fn)) for fn in files if os.path.exists(fn)]
while tostrip:
cmd = list(STRIPCMD)
flips = []
pathlen = sum(len(s) + 1 for s in cmd)
while pathlen < argv_max:
if not tostrip:
break
added, flip = tostrip.pop()
pathlen += len(added) + 1
cmd.append(added)
flips.append((added, flip))
else:
cmd.pop()
tostrip.append(flips.pop())
os.spawnv(os.P_WAIT, cmd[0], cmd)
for args in flips:
flipwritable(*args)
def files_in(folder):
for record in os.walk(folder):
for f in record[-1]:
yield os.path.join(record[0], f)
def expand_dirs(items, exclude=lambda x: x.endswith('.so')):
items = set(items)
dirs = set(x for x in items if os.path.isdir(x))
items.difference_update(dirs)
for x in dirs:
items.update({y for y in files_in(x) if not exclude(y)})
return items
def do_sign(app_dir):
with current_dir(os.path.join(app_dir, 'Contents')):
# Sign all .so files
so_files = {x for x in files_in('.') if x.endswith('.so')}
codesign(so_files)
# Sign everything else in Frameworks
with current_dir('Frameworks'):
fw = set(glob.glob('*.framework'))
codesign(fw)
items = set(os.listdir('.')) - fw
codesign(expand_dirs(items))
# Now sign the main app
codesign(app_dir)
verify_signature(app_dir)
def sign_app(app_dir, notarize):
# Copied from iTerm2: https://github.com/gnachman/iTerm2/blob/master/iTerm2.entitlements
create_entitlements_file({
'com.apple.security.automation.apple-events': True,
'com.apple.security.cs.allow-jit': True,
'com.apple.security.device.audio-input': True,
'com.apple.security.device.camera': True,
'com.apple.security.personal-information.addressbook': True,
'com.apple.security.personal-information.calendars': True,
'com.apple.security.personal-information.location': True,
'com.apple.security.personal-information.photos-library': True,
})
with make_certificate_useable():
do_sign(app_dir)
if notarize:
notarize_app(app_dir)
class Freeze(object):
FID = '@executable_path/../Frameworks'
def __init__(self, build_dir, dont_strip=False, sign_installers=False, notarize=False):
self.build_dir = build_dir
self.sign_installers = sign_installers
self.notarize = notarize
self.dont_strip = dont_strip
self.contents_dir = join(self.build_dir, 'Contents')
self.resources_dir = join(self.contents_dir, 'Resources')
self.frameworks_dir = join(self.contents_dir, 'Frameworks')
self.to_strip = []
self.warnings = []
self.py_ver = py_ver
self.python_stdlib = join(self.resources_dir, 'Python', 'lib', 'python' + self.py_ver)
self.site_packages = self.python_stdlib # hack to avoid needing to add site-packages to path
self.run()
def run_shell(self):
with current_dir(self.contents_dir):
run_shell()
def run(self):
ret = 0
self.add_python_framework()
self.add_site_packages()
self.add_stdlib()
self.add_misc_libraries()
self.compile_py_modules()
self.fix_dependencies_in_kitty()
if not self.dont_strip:
self.strip_files()
# self.run_shell()
ret = self.makedmg(self.build_dir, APPNAME + '-' + VERSION)
return ret
@flush
def strip_files(self):
print('\nStripping files...')
strip_files(self.to_strip)
@flush
def set_id(self, path_to_lib, new_id):
old_mode = flipwritable(path_to_lib)
subprocess.check_call(
['install_name_tool', '-id', new_id, path_to_lib])
if old_mode is not None:
flipwritable(path_to_lib, old_mode)
@flush
def get_dependencies(self, path_to_lib):
install_name = subprocess.check_output(
['otool', '-D', path_to_lib]).decode('utf-8').splitlines()[-1].strip()
raw = subprocess.check_output(['otool', '-L', path_to_lib]).decode('utf-8')
for line in raw.splitlines():
if 'compatibility' not in line or line.strip().endswith(':'):
continue
idx = line.find('(')
path = line[:idx].strip()
yield path, path == install_name
@flush
def get_local_dependencies(self, path_to_lib):
for x, is_id in self.get_dependencies(path_to_lib):
for y in (PREFIX + '/lib/', PREFIX + '/python/Python.framework/'):
if x.startswith(y):
if y == PREFIX + '/python/Python.framework/':
y = PREFIX + '/python/'
yield x, x[len(y):], is_id
break
@flush
def change_dep(self, old_dep, new_dep, is_id, path_to_lib):
cmd = ['-id', new_dep] if is_id else ['-change', old_dep, new_dep]
subprocess.check_call(['install_name_tool'] + cmd + [path_to_lib])
@flush
def fix_dependencies_in_lib(self, path_to_lib):
self.to_strip.append(path_to_lib)
old_mode = flipwritable(path_to_lib)
for dep, bname, is_id in self.get_local_dependencies(path_to_lib):
ndep = self.FID + '/' + bname
self.change_dep(dep, ndep, is_id, path_to_lib)
ldeps = list(self.get_local_dependencies(path_to_lib))
if ldeps:
print('\nFailed to fix dependencies in', path_to_lib)
print('Remaining local dependencies:', ldeps)
raise SystemExit(1)
if old_mode is not None:
flipwritable(path_to_lib, old_mode)
@flush
def add_python_framework(self):
print('\nAdding Python framework')
src = join(PREFIX + '/python', 'Python.framework')
x = join(self.frameworks_dir, 'Python.framework')
curr = os.path.realpath(join(src, 'Versions', 'Current'))
currd = join(x, 'Versions', basename(curr))
rd = join(currd, 'Resources')
os.makedirs(rd)
shutil.copy2(join(curr, 'Resources', 'Info.plist'), rd)
shutil.copy2(join(curr, 'Python'), currd)
self.set_id(
join(currd, 'Python'),
self.FID + '/Python.framework/Versions/%s/Python' % basename(curr))
self.fix_dependencies_in_lib(join(self.contents_dir, 'MacOS', 'kitty'))
# The following is needed for codesign
with current_dir(x):
os.symlink(basename(curr), 'Versions/Current')
for y in ('Python', 'Resources'):
os.symlink('Versions/Current/%s' % y, y)
@flush
def install_dylib(self, path, set_id=True):
shutil.copy2(path, self.frameworks_dir)
if set_id:
self.set_id(
join(self.frameworks_dir, basename(path)),
self.FID + '/' + basename(path))
self.fix_dependencies_in_lib(join(self.frameworks_dir, basename(path)))
@flush
def add_misc_libraries(self):
for x in (
'sqlite3.0',
'z.1',
'harfbuzz.0',
'png16.16',
'crypto.1.0.0',
'ssl.1.0.0',
):
print('\nAdding', x)
x = 'lib%s.dylib' % x
src = join(PREFIX, 'lib', x)
shutil.copy2(src, self.frameworks_dir)
dest = join(self.frameworks_dir, x)
self.set_id(dest, self.FID + '/' + x)
self.fix_dependencies_in_lib(dest)
base = join(self.frameworks_dir, 'kitty')
for lib in walk(base):
if lib.endswith('.so'):
self.set_id(lib, self.FID + '/' + os.path.relpath(lib, self.frameworks_dir))
self.fix_dependencies_in_lib(lib)
@flush
def add_package_dir(self, x, dest=None):
def ignore(root, files):
ans = []
for y in files:
ext = os.path.splitext(y)[1]
if ext not in ('', '.py', '.so') or \
(not ext and not os.path.isdir(join(root, y))):
ans.append(y)
return ans
if dest is None:
dest = self.site_packages
dest = join(dest, basename(x))
shutil.copytree(x, dest, symlinks=True, ignore=ignore)
self.postprocess_package(x, dest)
for f in walk(dest):
if f.endswith('.so'):
self.fix_dependencies_in_lib(f)
@flush
def fix_dependencies_in_kitty(self):
for f in walk(join(self.resources_dir, 'kitty')):
if f.endswith('.so'):
self.fix_dependencies_in_lib(f)
@flush
def postprocess_package(self, src_path, dest_path):
pass
@flush
def add_stdlib(self):
print('\nAdding python stdlib')
src = PREFIX + '/python/Python.framework/Versions/Current/lib/python' + self.py_ver
dest = self.python_stdlib
if not os.path.exists(dest):
os.makedirs(dest)
for x in os.listdir(src):
if x in ('site-packages', 'config', 'test', 'lib2to3', 'lib-tk',
'lib-old', 'idlelib', 'plat-mac', 'plat-darwin',
'site.py', 'distutils', 'turtledemo', 'tkinter'):
continue
x = join(src, x)
if os.path.isdir(x):
self.add_package_dir(x, dest)
elif os.path.splitext(x)[1] in ('.so', '.py'):
shutil.copy2(x, dest)
dest2 = join(dest, basename(x))
if dest2.endswith('.so'):
self.fix_dependencies_in_lib(dest2)
@flush
def add_site_packages(self):
print('\nAdding site-packages')
os.makedirs(self.site_packages)
sys_path = json.loads(subprocess.check_output([
PYTHON, '-c', 'import sys, json; json.dump(sys.path, sys.stdout)']))
paths = reversed(tuple(map(abspath, [x for x in sys_path if x.startswith('/') and not x.startswith('/Library/')])))
upaths = []
for x in paths:
if x not in upaths and (x.endswith('.egg') or x.endswith('/site-packages')):
upaths.append(x)
for x in upaths:
print('\t', x)
tdir = None
try:
if not os.path.isdir(x):
zf = zipfile.ZipFile(x)
tdir = tempfile.mkdtemp()
zf.extractall(tdir)
x = tdir
self.add_modules_from_dir(x)
self.add_packages_from_dir(x)
finally:
if tdir is not None:
shutil.rmtree(tdir)
self.remove_bytecode(self.site_packages)
@flush
def add_modules_from_dir(self, src):
for x in glob.glob(join(src, '*.py')) + glob.glob(join(src, '*.so')):
shutil.copy2(x, self.site_packages)
if x.endswith('.so'):
self.fix_dependencies_in_lib(x)
@flush
def add_packages_from_dir(self, src):
for x in os.listdir(src):
x = join(src, x)
if os.path.isdir(x) and os.path.exists(join(x, '__init__.py')):
if self.filter_package(basename(x)):
continue
self.add_package_dir(x)
@flush
def filter_package(self, name):
return name in ('Cython', 'modulegraph', 'macholib', 'py2app',
'bdist_mpkg', 'altgraph')
@flush
def remove_bytecode(self, dest):
for x in os.walk(dest):
root = x[0]
for f in x[-1]:
if os.path.splitext(f) == '.pyc':
os.remove(join(root, f))
@flush
def compile_py_modules(self):
print('\nCompiling Python modules')
self.remove_bytecode(join(self.resources_dir, 'Python'))
py_compile(join(self.resources_dir, 'Python'))
self.remove_bytecode(join(self.resources_dir, 'kitty'))
py_compile(join(self.resources_dir, 'kitty'))
@flush
def makedmg(self, d, volname, internet_enable=True, format='ULFO'):
''' Copy a directory d into a dmg named volname '''
print('\nMaking dmg...')
sys.stdout.flush()
destdir = os.path.join(SW, 'dist')
try:
shutil.rmtree(destdir)
except FileNotFoundError:
pass
os.mkdir(destdir)
dmg = os.path.join(destdir, volname + '.dmg')
if os.path.exists(dmg):
os.unlink(dmg)
tdir = tempfile.mkdtemp()
appdir = os.path.join(tdir, os.path.basename(d))
shutil.copytree(d, appdir, symlinks=True)
if self.sign_installers:
with timeit() as times:
sign_app(appdir, self.notarize)
print('Signing completed in %d minutes %d seconds' % tuple(times))
os.symlink('/Applications', os.path.join(tdir, 'Applications'))
size_in_mb = int(
subprocess.check_output(['du', '-s', '-k', tdir]).decode('utf-8')
.split()[0]) / 1024.
cmd = [
'/usr/bin/hdiutil', 'create', '-srcfolder', tdir, '-volname',
volname, '-format', format
]
if 190 < size_in_mb < 250:
# We need -size 255m because of a bug in hdiutil. When the size of
# srcfolder is close to 200MB hdiutil fails with
# diskimages-helper: resize request is above maximum size allowed.
cmd += ['-size', '255m']
print('\nCreating dmg...')
with timeit() as times:
subprocess.check_call(cmd + [dmg])
if internet_enable:
subprocess.check_call(
['/usr/bin/hdiutil', 'internet-enable', '-yes', dmg])
print('dmg created in %d minutes and %d seconds' % tuple(times))
shutil.rmtree(tdir)
size = os.stat(dmg).st_size / (1024 * 1024.)
print('\nInstaller size: %.2fMB\n' % size)
return dmg
def main():
args = globals()['args']
ext_dir = globals()['ext_dir']
if not args.skip_tests:
run_tests = iv['run_tests']
run_tests(None, os.path.join(ext_dir, 'src'))
Freeze(
os.path.join(ext_dir, kitty_constants['appname'] + '.app'),
dont_strip=args.dont_strip,
sign_installers=args.sign_installers,
notarize=args.notarize
)
if __name__ == '__main__':
main()

1
bypy/rsync.conf Normal file
View File

@@ -0,0 +1 @@
to_vm_excludes '/build /dist /kitty/launcher/kitty /.build-cache /tags __pycache__ /*_commands.json *.so *.pyd *.pyc'

256
bypy/sources.json Normal file
View File

@@ -0,0 +1,256 @@
[
{
"name": "zlib",
"unix": {
"filename": "zlib-1.2.11.tar.xz",
"hash": "sha256:4ff941449631ace0d4d203e3483be9dbc9da454084111f97ea0a2114e19bf066",
"urls": ["https://zlib.net/{filename}"]
}
},
{
"name": "bzip2",
"os": "linux",
"unix": {
"filename": "bzip2-1.0.8.tar.gz",
"hash": "sha256:ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269",
"urls": ["https://www.sourceware.org/pub/bzip2/bzip2-latest.tar.gz"]
}
},
{
"name": "pkg-config",
"os": "macos",
"unix": {
"filename": "pkg-config-0.29.2.tar.gz",
"hash": "sha256:6fc69c01688c9458a57eb9a1664c9aba372ccda420a02bf4429fe610e7e7d591",
"urls": ["https://pkg-config.freedesktop.org/releases/{filename}"]
}
},
{
"name": "expat",
"unix": {
"filename": "expat-2.2.6.tar.bz2",
"hash": "sha256:17b43c2716d521369f82fc2dc70f359860e90fa440bea65b3b85f0b246ea81f2",
"urls": ["https://github.com/libexpat/libexpat/releases/download/R_2_2_6/{filename}"]
}
},
{
"name": "xkbcommon",
"os": "linux",
"unix": {
"filename": "libxkbcommon-0.10.0.tar.xz",
"hash": "sha256:57c3630cdc38fb4734cd57fa349e92244f5ae3862813e533cedbd86721a0b6f2",
"urls": ["http://xkbcommon.org/download/{filename}"]
}
},
{
"name": "sqlite",
"unix": {
"filename": "sqlite-autoconf-3310100.tar.gz",
"hash": "sha1:0c30f5b22152a8166aa3bebb0f4bc1f3e9cc508b",
"urls": ["https://www.sqlite.org/2020/{filename}"]
}
},
{
"name": "libffi",
"unix": {
"filename": "libffi-3.2.1.tar.gz",
"hash": "sha1:280c265b789e041c02e5c97815793dfc283fb1e6",
"urls": ["ftp://sourceware.org/pub/libffi/{filename}"]
}
},
{
"name": "openssl",
"unix": {
"filename": "openssl-1.0.2u.tar.gz",
"hash": "sha256:ecd0c6ffb493dd06707d38b14bb4d8c2288bb7033735606569d8f90f89669d16",
"urls": ["https://www.openssl.org/source/{filename}"]
}
},
{
"name": "ncurses",
"os": "linux",
"unix": {
"filename": "ncurses-6.2.tar.gz",
"hash": "sha256:30306e0c76e0f9f1f0de987cf1c82a5c21e1ce6568b9227f7da5b71cbea86c9d",
"urls": ["https://invisible-mirror.net/archives/ncurses/{filename}"]
}
},
{
"name": "readline",
"os": "linux",
"unix": {
"filename": "readline-8.0.tar.gz",
"hash": "sha256:e339f51971478d369f8a053a330a190781acb9864cf4c541060f12078948e461",
"urls": ["http://ftp.gnu.org/gnu/readline/{filename}"]
}
},
{
"name": "xz",
"os": "macos,linux",
"unix": {
"filename": "xz-5.2.5.tar.gz",
"hash": "sha256:f6f4910fd033078738bd82bfba4f49219d03b17eb0794eb91efbae419f4aba10",
"urls": ["https://tukaani.org/xz/{filename}"]
}
},
{
"name": "python",
"unix": {
"filename": "Python-3.8.3.tar.xz",
"hash": "sha256:dfab5ec723c218082fe3d5d7ae17ecbdebffa9a1aea4d64aa3a2ecdd2e795864",
"urls": ["https://www.python.org/ftp/python/3.8.3/{filename}"]
}
},
{
"name": "setuptools",
"unix": {
"filename": "setuptools-41.0.1.zip",
"hash": "sha256:a222d126f5471598053c9a77f4b5d4f26eaa1f150ad6e01dcf1a42e185d05613",
"urls": ["pypi"]
}
},
{
"name": "pygments",
"unix": {
"filename": "Pygments-2.6.1.tar.gz",
"hash": "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44",
"urls": ["pypi"]
}
},
{
"name": "cmake",
"os": "macos",
"unix": {
"filename": "cmake-3.6.0.tar.gz",
"hash": "sha1:6b0d2d7d93c2e74f8890c4b8b824deb3b36d24b6",
"urls": ["https://cmake.org/files/v3.6/{filename}"]
}
},
{
"name": "libpng",
"unix": {
"filename": "libpng-1.6.37.tar.xz",
"hash": "sha256:505e70834d35383537b6491e7ae8641f1a4bed1876dbfe361201fc80868d88ca",
"urls": ["http://downloads.sourceforge.net/sourceforge/libpng/{filename}"]
}
},
{
"name": "graphite",
"os": "linux",
"unix": {
"filename": "graphite2-1.3.5.tgz",
"hash": "md5:5b8d22a8bbf031838e31432868c0109c",
"urls": ["http://downloads.sourceforge.net/silgraphite/{filename}"]
}
},
{
"name": "pcre",
"os": "linux",
"unix": {
"filename": "pcre-8.42.tar.bz2",
"hash": "md5:085b6aa253e0f91cae70b3cdbe8c1ac2",
"urls": ["https://ftp.pcre.org/pub/pcre/{filename}"]
}
},
{
"name": "iconv",
"os": "linux",
"unix": {
"filename": "libiconv-1.16.tar.gz",
"hash": "sha256:e6a1b1b589654277ee790cce3734f07876ac4ccfaecbee8afa0b649cf529cc04",
"urls": ["http://ftp.gnu.org/pub/gnu/libiconv/{filename}"]
}
},
{
"name": "glib",
"os": "linux",
"unix": {
"filename": "glib-2.57.1.tar.xz",
"hash": "sha256:d029e7c4536835f1f103472f7510332c28d58b9b7d6cd0e9f45c2653e670d9b4",
"urls": ["http://ftp.gnome.org/pub/GNOME/sources/glib/2.57/{filename}"]
}
},
{
"name": "freetype",
"os": "linux",
"unix": {
"filename": "freetype-2.9.1.tar.bz2",
"hash": "md5:60ef7d8160cd4bf8cb118ee9d65367ca",
"urls": ["https://download.savannah.gnu.org/releases/freetype/{filename}"]
}
},
{
"name": "fontconfig",
"os": "linux",
"unix": {
"filename": "fontconfig-2.12.0.tar.bz2",
"hash": "sha256:b433e4efff1f68fdd8aac221ed1df3ff1e580ffedbada020a703fe64017d8224",
"urls": ["http://www.fontconfig.org/release/{filename}"]
}
},
{
"name": "harfbuzz",
"unix": {
"filename": "harfbuzz-1.7.2.tar.bz2",
"hash": "sha256:a790585e35c1a87f0dcc23580c84b7cc2324e6f67a2946178d278c2a36c790cb",
"urls": ["https://www.freedesktop.org/software/harfbuzz/release/{filename}"]
}
},
{
"name": "libxml2",
"os": "linux",
"unix": {
"filename": "libxml2-2.9.4.tar.gz",
"hash": "sha1:958ae70baf186263a4bd801a81dd5d682aedd1db",
"urls": ["ftp://xmlsoft.org/libxml2/{filename}"]
}
},
{
"name": "wayland",
"os": "linux",
"unix": {
"filename": "wayland-1.18.0.tar.xz",
"hash": "sha256:4675a79f091020817a98fd0484e7208c8762242266967f55a67776936c2e294d",
"urls": ["https://wayland.freedesktop.org/releases/{filename}"]
}
},
{
"name": "wayland-protocols",
"os": "linux",
"unix": {
"filename": "wayland-protocols-1.20.tar.xz",
"hash": "sha256:9782b7a1a863d82d7c92478497d13c758f52e7da4f197aa16443f73de77e4de7",
"urls": ["https://wayland.freedesktop.org/releases/{filename}"]
}
}
]

View File

@@ -46,7 +46,7 @@ particular desktop, but it should work for most major desktop environments.
# Place the kitty.desktop file somewhere it can be found by the OS
cp ~/.local/kitty.app/share/applications/kitty.desktop ~/.local/share/applications
# Update the path to the kitty icon in the kitty.desktop file
sed -i "s/Icon\=kitty/Icon\=\/home\/$USER\/.local\/kitty.app\/share\/icons\/hicolor\/256x256\/apps\/kitty.png/g" ~/.local/share/applications/kitty.desktop
sed -i "s|Icon=kitty|Icon=/home/$USER/.local/kitty.app/share/icons/hicolor/256x256/apps/kitty.png|g" ~/.local/share/applications/kitty.desktop

View File

@@ -4,6 +4,230 @@ Changelog
|kitty| is a feature full, cross-platform, *fast*, GPU based terminal emulator.
To update |kitty|, :doc:`follow the instructions <binary>`.
0.18.3 [2020-08-11]
-------------------
- hints kitten: Allow customizing hint colors (:pull:`2894`)
- Wayland: Fix a typo in the previous release that broke reading mouse cursor size (:iss:`2895`)
- Fix a regression in the previous release that could cause an exception during
startup in rare circumstances (:iss:`2896`)
- Fix image leaving behind a black rectangle when switch away and back to
alternate screen (:iss:`2901`)
- Fix one pixel mis-alignment of rounded corners when either the cell
dimensions or the thickness of the line is an odd number of pixels
(:iss:`2907`)
- Fix a regression that broke specifying OS window size in the session file
(:iss:`2908`)
0.18.2 [2020-07-28]
--------------------
- X11: Improve handling of multiple keyboards. Now pressing a modifier key in
one keyboard and a normal key in another works (:iss:`2362`). Don't rebuild
keymaps on new keyboard events that only change geometry (:iss:`2787`).
Better handling of multiple keyboards with incompatible layouts (:iss:`2726`)
- Improve anti-aliasing of triangular box drawing characters, noticeable on
low-resolution screens (:iss:`2844`)
- Fix ``kitty @ send-text`` not working reliably when using a socket for remote
control (:iss:`2852`)
- Implement support for box drawing rounded-corners characters (:iss:`2240`)
- Allow setting the class for new OS windows in a session file
- When a character from the Unicode Dingbat block is followed by a space, use
the extra space to render a larger version of the character (:iss:`2850`)
- macOS: Fix the LC_CTYPE env var being set to UTF-8 on systems in which the
language and country code do not form a valid locale (:iss:`1233`)
- macOS: Fix :kbd:`cmd+plus` not changing font size (:iss:`2839`)
- Make neighboring window selection in grid and splits layouts more intelligent
(:pull:`2840`)
- Allow passing the current selection to kittens (:iss:`2796`)
- Fix pre-edit text not always being cleared with ibus input (:iss:`2862`)
- Allow setting the :opt:`background_opacity` of new OS windows created via
:option:`kitty --single-instance` using the :option:`kitty --override` command line
argument (:iss:`2806`)
- Fix the CSI J (Erase in display ED) escape code not removing line continued
markers (:iss:`2809`)
- hints kitten: In linenumber mode expand paths that starts with ~
(:iss:`2822`)
- Fix ``launch --location=last`` not working (:iss:`2841`)
- Fix incorrect centering when a PUA or symbol glyph is followed by more than one space
- Have the :opt:`confirm_os_window_close` option also apply when closing tabs
with multiple windows (:iss:`2857`)
- Add support for legacy DECSET codes 47, 1047 and 1048 (:pull:`2871`)
- macOS: no longer render emoji 20% below the baseline. This caused some emoji
to be cut-off and also look misaligned with very high cells (:iss:`2873`)
- macOS: Make the window id of OS windows available in the ``WINDOWID``
environment variable (:pull:`2877`)
- Wayland: Fix a regression in 0.18.0 that could cause crashes related to mouse
cursors in some rare circumstances (:iss:`2810`)
- Fix change in window size that does not change number of cells not being
reported to the kernel (:iss:`2880`)
0.18.1 [2020-06-23]
--------------------
- macOS: Fix for diff kitten not working with python 3.8 (:iss:`2780`)
0.18.0 [2020-06-20]
--------------------
- Allow multiple overlay windows per normal window
- Add an option :opt:`confirm_os_window_close` to ask for confirmation
when closing an OS window with multiple kitty windows.
- Tall and Fat layouts: Add a ``mirrored`` option to put the full size window
on the opposite edge of the screen (:iss:`2654`)
- Tall and Fat layouts: Add mappable actions to increase or decrease the number
of full size windows (:iss:`2688`)
- Allow sending arbitrary signals to the current foreground process in a window
using either a mapping in kitty.conf or via remote control (:iss:`2778`)
- Allow sending the back and forward mouse buttons to terminal applications
(:pull:`2742`)
- **Backwards incompatibility**: The numbers used to encode mouse buttons
for the ``send_mouse_event`` function that can be used in kittens have
been changed (see :ref:`send_mouse_event`).
- Add a new mappable ``quit`` action to quit kitty completely.
- Fix marks using different colors with regexes using only a single color
(:pull:`2663`)
- Linux: Workaround for broken Nvidia drivers for old cards (:iss:`456`)
- Wayland: Fix kitty being killed on some Wayland compositors if a hidden window
has a lot of output (:iss:`2329`)
- BSD: Fix controlling terminal not being established (:pull:`2686`)
- Add support for the CSI REP escape code (:pull:`2702`)
- Wayland: Fix mouse cursor rendering on HiDPI screens (:pull:`2709`)
- X11: Recompile keymaps on XkbNewKeyboardNotify events (:iss:`2726`)
- X11: Reduce startup time by ~25% by only querying GLX for framebuffer
configurations once (:iss:`2754`)
- macOS: Notarize the kitty application bundle (:iss:`2040`)
- Fix the kitty shell launched via a mapping needlessly requiring
:opt:`allow_remote_control` to be turned on.
0.17.4 [2020-05-09]
--------------------
- Allow showing the name of the current layout and the number of windows
in tab titles (:iss:`2634`)
- macOS: Fix a regression in the previous release that caused ligatures to be
not be centered horizontally (:iss:`2591`)
- By default, double clicking no longer considers the : as part of words, see
:opt:`select_by_word_characters` (:iss:`2602`)
- Fix a regression that caused clicking in the padding/margins of windows in
the stack layout to switch the window to the first window (:iss:`2604`)
- macOS: Fix a regression that broke drag and drop (:iss:`2605`)
- Report modifier key state when sending wheel events to the terminal program
- Fix kitty @ send-text not working with text larger than 1024 bytes when using
:option:`kitty --listen-on` (:iss:`2607`)
- Wayland: Fix OS window title not updating for hidden windows (:iss:`2629`)
- Fix :opt:`background_tint` making the window semi-transparent (:iss:`2618`)
0.17.3 [2020-04-23]
--------------------
- Allow individually setting margins and padding for each edge (left, right,
top, bottom). Margins can also be controlled per window via remote control
(:iss:`2546`)
- Fix reverse video not being rendered correctly when using transparency or a
background image (:iss:`2419`)
- Allow mapping arbitrary remote control commands to key presses in
:file:`kitty.conf`
- X11: Fix crash when doing drag and drop from some applications (:iss:`2505`)
- Fix :option:`launch --stdin-add-formatting` not working (:iss:`2512`)
- Update to Unicode 13.0 (:iss:`2513`)
- Render country flags designated by a pair of unicode codepoints
in two cells instead of four.
- diff kitten: New option to control the background color for filler lines in
the margin (:iss:`2518`)
- Fix specifying options for layouts in the startup session file not working
(:iss:`2520`)
- macOS: Fix incorrect horizontal positioning of some full-width East Asian characters
(:iss:`1457`)
- macOS: Render multi-cell PUA characters centered, matching behavior on other
platforms
- Linux: Ignore keys if they are designated as layout/group/mode switch keys
(:iss:`2519`)
- Marks: Fix marks not handling wide characters and tab characters correctly
(:iss:`2534`)
- Add a new :opt:`listen_on` option in kitty.conf to set :option:`kitty --listen-on`
globally. Also allow using environment variables in this option (:iss:`2569`).
- Allow sending mouse events in kittens (:pull:`2538`)
- icat kitten: Fix display of 16-bit depth images (:iss:`2542`)
- Add ncurses specific terminfo definitions for strikethrough (:pull:`2567`)
- Fix a regression in 0.17 that broke displaying graphics over SSH
(:iss:`2568`)
- Fix :option:`--title` not being applied at window creation time (:iss:`2570`)
0.17.2 [2020-03-29]
--------------------

View File

@@ -275,7 +275,7 @@ if you specify a program-to-run you can use the special placeholder
p('\n\n' + as_rst(
global_options_spec, message=cli_msg, usage='command ...', appname='kitty @'))
from kitty.rc.base import cli_params_for
for cmd_name in all_command_names():
for cmd_name in sorted(all_command_names()):
func = command_for_name(cmd_name)
p(f'.. _at_{func.name}:\n')
p('kitty @', func.name + '\n' + '-' * 120)
@@ -326,7 +326,7 @@ def write_remote_control_protocol_docs() -> None: # {{{
p(' ', desc), p()
p(), p()
with open(f'generated/rc.rst', 'w') as f:
with open('generated/rc.rst', 'w') as f:
p = partial(print, file=f)
for name in sorted(all_command_names()):
cmd = command_for_name(name)

View File

@@ -169,7 +169,7 @@ only monospace fonts, since every cell in the grid has to be the same size. If
your font is not listed in ``kitty list-fonts`` it means that it is not
monospace. On Linux you can list all monospace fonts with::
fc-list : family spacing | grep -e spacing=100 -e spacing=90
fc-list : family spacing outline scalable | grep -e spacing=100 -e spacing=90 | grep -e outline=True | grep -e scalable=True
Note that the spacing property is calculated by fontconfig based on actual
glyph widths in the font. If for some reason fontconfig concludes your favorite
@@ -179,7 +179,7 @@ following :file:`~/.config/fontconfig/fonts.conf`::
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<match target="scan">
<match target="font">
<test name="family">
<string>Your Font Family Name</string>
</test>

View File

@@ -30,10 +30,11 @@ alpha-blending and text over graphics.
Some programs that use the kitty graphics protocol:
* `termpdf <https://github.com/dsanson/termpdf>`_ - a terminal PDF/DJVU/CBR viewer
* `termpdf.py <https://github.com/dsanson/termpdf.py>`_ - a terminal PDF/DJVU/CBR viewer
* `ranger <https://github.com/ranger/ranger>`_ - a terminal file manager, with
image previews, see this `PR <https://github.com/ranger/ranger/pull/1077>`_
* :doc:`kitty-diff <kittens/diff>` - a side-by-side terminal diff program with support for images
* `pixcat <https://github.com/mirukana/pixcat>`_ - a third party CLI and python library that wraps the graphics protocol
* `neofetch <https://github.com/dylanaraps/neofetch>`_ - A command line system
information tool
@@ -262,8 +263,8 @@ received. Finally, terminals must not display anything, until the entire sequenc
received and validated.
Detecting available transmission mediums
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Querying support and available transmission mediums
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Since a client has no a-priori knowledge of whether it shares a filesystem/shared memory
with the terminal emulator, it can send an id with the control data, using the ``i`` key
@@ -289,6 +290,27 @@ use the *query action*, set ``a=q``. Then the terminal emulator will try to load
the image and respond with either OK or an error, as above, but it will not
replace an existing image with the same id, nor will it store the image.
While as of May 2020, kitty is the only terminal emulator to support this
graphics protocol, we intend that any terminal emulator that wishes to support
it can. To check if a terminal emulator supports the graphics protocol the best way
is to send the above *query action* followed by a request for the
`primary device attributes <https://vt100.net/docs/vt510-rm/DA1.html>`. If you
get back an answer for the device attributes without getting back an answer for
the *query action* the terminal emulator does not support the graphics
protocol.
This means that terminal emulators that support the graphics protocol, **must**
reply to *query actions* immediately without processing other input. Most
terminal emulators handle input in a FIFO manner, anyway.
So for example, you could send::
<ESC>_Gi=31,s=1,v=1,a=q,t=d,f=24;<NUL><NUL><NUL><ESC>\<ESC>[c
If you get back a response to the graphics query, the terminal emulator supports
the protocol, if you get back a response to the device attributes query without
a response to the graphics query, it does not.
Display images on screen
-----------------------------
@@ -368,10 +390,10 @@ Value of ``d`` Meaning
Some examples::
<ESC>_Ga=d<ESC>\ # delete all visible images
<ESC>_Ga=d,i=10<ESC>\ # delete the image with id=10
<ESC>_Ga=Z,z=-1<ESC>\ # delete the images with z-index -1, also freeing up image data
<ESC>_Ga=P,x=3,y=4<ESC>\ # delete all images that intersect the cell at (3, 4)
<ESC>_Ga=d<ESC>\ # delete all visible images
<ESC>_Ga=d,d=i,i=10<ESC>\ # delete the image with id=10, without freeing data
<ESC>_Ga=d,d=Z,z=-1<ESC>\ # delete the images with z-index -1, also freeing up image data
<ESC>_Ga=d,d=p,x=3,y=4<ESC>\ # delete all images that intersect the cell at (3, 4), without freeing data
Image persistence and storage quotas
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -65,7 +65,7 @@ You can also use your favorite package manager to install the |kitty| package.
`openSUSE <https://build.opensuse.org/package/show/X11:terminals/kitty>`_,
`Arch Linux <https://www.archlinux.org/packages/community/x86_64/kitty/>`_,
`Gentoo <https://packages.gentoo.org/packages/x11-terms/kitty>`_,
`Fedora <https://copr.fedorainfracloud.org/coprs/gagbo/kitty-latest/>`_,
`Fedora <https://src.fedoraproject.org/rpms/kitty>`_,
`Void Linux <https://github.com/void-linux/void-packages/blob/master/srcpkgs/kitty/template>`_,
and `Solus <https://dev.getsol.us/source/kitty/>`_.
@@ -346,6 +346,8 @@ For example:
new_os_window
# set new window size to 80x25 cells
os_window_size 80c 25c
# set the --class for the new OS window
os_window_class mywindow
launch sh
# Make the current window the active (focused) window
focus
@@ -360,11 +362,12 @@ Mouse features
* You can triple click to select a line and then drag to select more lines.
* You can right click to extend a previous selection.
* You can hold down :kbd:`ctrl+alt` and drag with the mouse to select in
columns.
columns (see also :opt:`rectangle_select_modifiers`).
* Selecting text automatically copies it to the primary clipboard (on
platforms with a primary clipboard).
* You can select text with kitty even when a terminal program has grabbed
the mouse by holding down the :kbd:`shift` key.
the mouse by holding down the :kbd:`shift` key (see also
:opt:`terminal_select_modifiers`).
Font control
@@ -438,6 +441,15 @@ The list of Frequently Asked Questions (*FAQ*) is :doc:`available here <faq>`.
.. _completion:
Cool integrations for kitty with other CLI tools
--------------------------------------------------
kitty provides extremely powerful interfaces such as :doc:`remote-control` and
:doc:`kittens/custom` and :doc:`kittens/icat`
that allow it to be integrated with other tools seamlessly. For a list of such
user created integrations, see: :doc:`integrations`.
Completion for kitty
---------------------------------

118
docs/integrations.rst Normal file
View File

@@ -0,0 +1,118 @@
Integrations with other tools
================================
kitty provides extremely powerful interfaces such as :doc:`remote-control` and
:doc:`kittens/custom` and :doc:`kittens/icat`
that allow it to be integrated with other tools seamlessly.
Image and document viewers
----------------------------
Powered by kitty's :doc:`graphics-protocol` there exist many tools for viewing
images and other types of documents directly in your terminal, even over SSH.
`termpdf.py <https://github.com/dsanson/termpdf.py>`_
a terminal PDF/DJVU/CBR viewer
`mdcat <https://github.com/lunaryorn/mdcat>`_
Display markdown files nicely formatted with images in the terminal
`ranger <https://github.com/ranger/ranger>`_
a terminal file manager, with previews of file contents powered by kitty's graphics protocol.
`nnn <https://github.com/jarun/nnn/>`_
another terminal file manager, with previews of file contents powered by kitty's graphics protocol.
`hunter <https://github.com/rabite0/hunter>`_
another terminal file manager, with previews of file contents powered by kitty's graphics protocol.
`koneko <https://github.com/twenty5151/koneko>`_
browse images from the pixiv artist community directly in kitty
System and data visualisation tools
---------------------------------------
`neofetch <https://github.com/dylanaraps/neofetch>`_
A command line system information tool that shows images using kitty's graphics protocol
`matplotlib <https://github.com/jktr/matplotlib-backend-kitty>`_
show matplotlib plots directly in kitty
`KittyTerminalImages.jl <https://github.com/simonschoelly/KittyTerminalImages.jl>`_
show images from Julia directly in kitty
`gnuplot <http://www.gnuplot.info/>`_
a graphing and data visualization tool that can be made to display its
output in kitty with the following bash snippet::
function iplot {
cat <<EOF | gnuplot
set terminal pngcairo enhanced font 'Fira Sans,10'
set autoscale
set samples 1000
set output '|kitty +kitten icat --stdin yes'
set object 1 rectangle from screen 0,0 to screen 1,1 fillcolor rgb"#fdf6e3" behind
plot $@
set output '/dev/null'
EOF
}
Add this to bashrc and then to plot a function, simply do::
iplot 'sin(x*3)*exp(x*.2)'
Editor integration
-----------------------
kitty can be integrated into many different terminal editors to add features
such a split windows, previews, REPLs etc.
`kakoune <https://kakoune.org/>`_
integrates with kitty to use native kitty windows for its windows/panels and REPLs.
`vim-slime <https://github.com/jpalardy/vim-slime#kitty>`_
uses kitty remote control for a Lisp REPL.
`vim-kitty-navigator <https://github.com/knubie/vim-kitty-navigator>`_
allows you to navigate seamlessly between vim and kitty splits using a consistent set of hotkeys.
`vim-test <https://github.com/vim-test/vim-test>`_
allows easily running tests in a terminal window
Scrollback manipulation
-------------------------
`kitty-search <https://github.com/trygveaa/kitty-kitten-search>`_
Live incremental search of the scrollback buffer.
`kitty-grab <https://github.com/yurikhan/kitty_grab>`_
keyboard based text selection for the kitty scrollback buffer.
Miscellaneous
------------------
`kitty-smart-tab <https://github.com/yurikhan/kitty-smart-tab>`_
use keys to either control tabs or pass them onto running applications if
no tabs are present
`kitty-smart-scroll <https://github.com/yurikhan/kitty-smart-scroll>`_
use keys to either scroll or pass them onto running applications if
no scrollback buffer is present
`reload keybindings <https://github.com/kovidgoyal/kitty/issues/1292#issuecomment-582388769>`_
reload key bindings from :file:`kitty.conf` without needing to restart
kitty
`kitti3 <https://github.com/LandingEllipse/kitti3>`_
allow using kitty as a drop-down terminal under the i3 window manager
`weechat-hints <https://github.com/GermainZ/kitty-weechat-hints>`_
URL hints kitten for WeeChat that works without having to use WeeChat's
raw-mode.

View File

@@ -20,24 +20,24 @@ your machine).
.. code-block:: python
def main(args: List[str]) -> str:
# this is the main entry point of the kitten, it will be executed in
# the overlay window when the kitten is launched
answer = input('Enter some text: ')
# whatever this function returns will be available in the
# handle_result() function
return answer
def main(args: List[str]) -> str:
# this is the main entry point of the kitten, it will be executed in
# the overlay window when the kitten is launched
answer = input('Enter some text: ')
# whatever this function returns will be available in the
# handle_result() function
return answer
def handle_result(args: List[str], answer: str, target_window_id: int, boss: kitty.boss.Boss) -> None:
# get the kitty window into which to paste answer
w = boss.window_id_map.get(target_window_id)
if w is not None:
w.paste(answer)
def handle_result(args: List[str], answer: str, target_window_id: int, boss: kitty.boss.Boss) -> None:
# get the kitty window into which to paste answer
w = boss.window_id_map.get(target_window_id)
if w is not None:
w.paste(answer)
Now in :file:`kitty.conf` add the lines::
map ctrl+k kitten mykitten.py
map ctrl+k kitten mykitten.py
Start kitty and press :kbd:`ctrl+k` and you should see the kitten running.
@@ -53,12 +53,13 @@ Passing arguments to kittens
You can pass arguments to kittens by defining them in the map directive in
:file:`kitty.conf`. For example::
map ctrl+k kitten mykitten.py arg1 arg2
map ctrl+k kitten mykitten.py arg1 arg2
These will be available as the ``args`` parameter in the ``main()`` and
``handle_result()`` functions. Note also that the current working directory
of the kitten is set to the working directory of whatever program is
running in the active kitty window.
running in the active kitty window. The special argument ``@selection``
is replaced by the currently selected text in the active kitty window.
Passing the contents of the screen to the kitten
@@ -70,24 +71,25 @@ function, telling kitty what kind of input your kitten would like. For example:
.. code-block:: py
# in main, STDIN is for the kitten process and will contain
# the contents of the screen
def main(args):
return sys.stdin.read()
# in main, STDIN is for the kitten process and will contain
# the contents of the screen
def main(args):
return sys.stdin.read()
# in handle_result, STDIN is for the kitty process itself, rather
# than the kitten process and should not be read from.
from kittens.tui.handler import result_handler
@result_handler(type_of_input='text')
def handle_result(args, stdin_data, target_window_id, boss):
pass
# in handle_result, STDIN is for the kitty process itself, rather
# than the kitten process and should not be read from.
from kittens.tui.handler import result_handler
@result_handler(type_of_input='text')
def handle_result(args, stdin_data, target_window_id, boss):
pass
This will send the plain text of the active window to the kitten's
:file:`STDIN`. For text with formatting escape codes, use ``ansi``
instead. If you want line wrap markers as well, use ``screen-ansi``
or just ``screen``. For the scrollback buffer as well, use
``history``, ``ansi-history`` or ``screen-history``.
``history``, ``ansi-history`` or ``screen-history``. To get
the currently selected text, use ``selection``.
Using kittens to script kitty, without any terminal UI
@@ -104,23 +106,23 @@ Create a file in the kitty config folder, :file:`~/.config/kitty/zoom_toggle.py`
.. code-block:: py
def main(args):
pass
def main(args):
pass
from kittens.tui.handler import result_handler
@result_handler(no_ui=True)
def handle_result(args, answer, target_window_id, boss):
tab = boss.active_tab
if tab is not None:
if tab.current_layout.name == 'stack':
tab.last_used_layout()
else:
tab.goto_layout('stack')
from kittens.tui.handler import result_handler
@result_handler(no_ui=True)
def handle_result(args, answer, target_window_id, boss):
tab = boss.active_tab
if tab is not None:
if tab.current_layout.name == 'stack':
tab.last_used_layout()
else:
tab.goto_layout('stack')
Now in kitty.conf add::
map f11 kitten zoom_toggle.py
map f11 kitten zoom_toggle.py
Pressing :kbd:`F11` will now act as a zoom toggle function. You can get even
more fancy, switching the kitty OS window to fullscreen as well as changing the
@@ -132,6 +134,35 @@ layout, by simply adding the line::
To the ``handle_result()`` function, above.
.. _send_mouse_event:
Sending mouse events
--------------------
If the program running in a window is receiving mouse events you can simulate
those using::
from kitty.fast_data_types import send_mouse_event
send_mouse_event(screen, x, y, button, action, mods)
``screen`` is the ``screen`` attribute of the window you want to send the event
to. ``x`` and ``y`` are the 0-indexed coordinates. ``button`` is a number using
the same numbering as X11 (left: ``1``, middle: ``2``, right: ``3``, scroll up:
``4``, scroll down: ``5``, scroll left: ``6``, scroll right: ``7``, back:
``8``, forward: ``9``). ``action`` is one of ``PRESS``, ``RELEASE``, ``DRAG``
or ``MOVE``. ``mods`` is a bitmask of ``GLFW_MOD_{mod}`` where ``{mod}`` is one
of ``SHIFT``, ``CONTROL`` or ``ALT``. All the mentioned constants are imported
from ``kitty.fast_data_types``.
For example, to send a left click at position x: 2, y: 3 to the active window::
from kitty.fast_data_types import send_mouse_event, GLFW_MOUSE_BUTTON_LEFT, PRESS
send_mouse_event(boss.active_window.screen, 2, 3, GLFW_MOUSE_BUTTON_LEFT, PRESS, 0)
The function will only send the event if the program is receiving events of
that type, and will return ``True`` if it sent the event, and ``False`` if not.
Debugging kittens
--------------------
@@ -157,11 +188,15 @@ Kittens created by kitty users
---------------------------------------------
`vim-kitty-navigator <https://github.com/knubie/vim-kitty-navigator>`_
Allows you to navigate seamlessly between vim and kitty splits using a consistent set of hotkeys.
Allows you to navigate seamlessly between vim and kitty splits using a consistent set of hotkeys.
`smart-scroll <https://github.com/yurikhan/kitty-smart-scroll>`_
Makes the kitty scroll bindings work in full screen applications
Makes the kitty scroll bindings work in full screen applications
`insert password <https://github.com/kovidgoyal/kitty/issues/1222>`_
Insert a password from a CLI password manager, taking care to only do it at
a password prompt.
Insert a password from a CLI password manager, taking care to only do it at
a password prompt.
`weechat-hints <https://github.com/GermainZ/kitty-weechat-hints>`_
URL hints kitten for WeeChat that works without having to use WeeChat's
raw-mode.

View File

@@ -23,7 +23,7 @@ like a path or filename followed by a colon and a line number and open
the file in vim at the specified line number. The patterns and editor
to be used can be modified using options passed to the kitten. For example::
map ctrl+g kitten hints --type=linenum --linenum-action=tab nvim +{line} +{path}
map ctrl+g kitten hints --type=linenum --linenum-action=tab nvim +{line} {path}
will open the selected file in a new tab inside neovim when you press
:kbd:`ctrl+g`.

View File

@@ -21,6 +21,12 @@ Then you can simply use ``icat image.png`` to view images.
`ImageMagick <https://www.imagemagick.org>`_ must be installed for ``icat`` to
work.
.. note::
kitty's image display protocol may not work when used within a terminal
multiplexer such as ``screen`` or ``tmux``, depending on whether the
multiplexer has added support for it or not.
.. program:: kitty +kitten icat

View File

@@ -24,7 +24,7 @@ a leading period and the index for the match if you don't like to use arrow
keys.
You can switch between modes using either the function keys or by pressing
:kbd:`Ctrl+Tab` and :kbd:`Ctrl+Shift+Tab`.
:kbd:`Ctrl+[` and :kbd:`Ctrl+]`.
Command Line Interface

View File

@@ -5,7 +5,7 @@ Launching programs in new windows/tabs
|kitty| has a :code:`launch` action that can be used to run arbitrary programs
in news windows/tabs. It can be mapped to user defined shortcuts in kitty.conf.
in new windows/tabs. It can be mapped to user defined shortcuts in kitty.conf.
It is very powerful and allows sending the contents of
the current window to the launched program, as well as many other options.

View File

@@ -46,10 +46,12 @@ Displays one (or optionally more) full height windows on the left half of the
screen. Remaining windows are tiled vertically on the right half of the screen.
There are options to control how the screen is split horizontally ``bias``
(an integer between ``10`` and ``90``) and options to control how many
full-height windows there are ``full_size`` (a positive integer). The syntax
full-height windows there are ``full_size`` (a positive integer). The
``mirrored`` option when set to ``true`` will cause the short windows to be
on the left side of the screen instead of the right. The syntax
for the options is shown below::
enabled_layouts tall:bias=50;full_size=1
enabled_layouts tall:bias=50;full_size=1;mirrored=false
┌──────────────┬───────────────┐
│ │ │
@@ -65,6 +67,13 @@ for the options is shown below::
│ │ │
└──────────────┴───────────────┘
In addition, you can map keys to increase or decrease the number of full size
windows, for example::
map ctrl+[ layout_action decrease_num_full_size_windows
map ctrl+] layout_action increase_num_full_size_windows
The Fat Layout
----------------
@@ -72,10 +81,12 @@ Displays one (or optionally more) full width windows on the top half of the
screen. Remaining windows are tiled horizontally on the bottom half of the screen.
There are options to control how the screen is split vertically ``bias``
(an integer between ``10`` and ``90``) and options to control how many
full-height windows there are ``full_size`` (a positive integer). The syntax
for the options is shown below::
full-height windows there are ``full_size`` (a positive integer). The
``mirrored`` option when set to ``true`` will cause the narrow windows to be
on the top of the screen instead of the bottom. The syntax for the options is
shown below::
enabled_layouts fat:bias=50;full_size=1
enabled_layouts fat:bias=50;full_size=1;mirrored=false
┌──────────────────────────────┐
│ │
@@ -241,7 +252,7 @@ This will have ``2`` instead of a single tall window, that occupy ``70%``
instead of ``50%`` of available width. ``bias`` can be any number between 10
and 90.
Writing a new layout only requires about a hundred lines of code, so if there
is some layout you want, take a look at `layout.py
<https://github.com/kovidgoyal/kitty/blob/master/kitty/layout.py>`_ and submit
a pull request!
Writing a new layout only requires about two hundred lines of code, so if there
is some layout you want, take a look at one of the existing layouts in the
`layout <https://github.com/kovidgoyal/kitty/tree/master/kitty/layout>`_
package and submit a pull request!

View File

@@ -222,7 +222,7 @@ Here ``!`` is not valid base64 encoded text, so it clears the clipboard.
Further, since it is invalid, it should be ignored by terminal emulators
that do not support this extension, thereby making it safe to use, simply
always send it before starting a new OSC 52 paste, even if you aren't chunking
up large pastes, that way kitty wont concatenate your paste, and it will have
up large pastes, that way kitty won't concatenate your paste, and it will have
no ill-effects in other terminal emulators.
In case you're using software that can't be easily adapted to this

View File

@@ -10,18 +10,18 @@ form::
Where ``<ESC>`` is the byte ``0x1b``. The JSON object has the form::
{
'cmd': "command name",
'version': "kitty version",
'no_response': Optional Boolean,
'payload': <Optional JSON object>,
"cmd": "command name",
"version": <kitty version>,
"no_response": Optional Boolean,
"payload": <Optional JSON object>,
}
The ``version`` above is a string of the form :code:`0.14.2`. If you are developing a
The ``version`` above is an array of the form :code:`[0, 14, 2]`. If you are developing a
standalone client, use the kitty version that you are developing against. Using
a version greater than the version of the kitty instance you are talking to,
will cause a failure.
Set ``no_response`` to True if you don't want a response from kitty.
Set ``no_response`` to ``true`` if you don't want a response from kitty.
The optional payload is a JSON object that is specific to the actual command being sent.
The fields in the object for every command are documented below.

View File

@@ -21,7 +21,7 @@ In order for control to work, :opt:`allow_remote_control` must be enabled in
Now, in the new |kitty| window, enter the command::
kitty @ new-window --title Output --keep-focus cat
kitty @ launch --title Output --keep-focus cat
This will open a new window, running the ``cat`` program that will appear next
to the current window.
@@ -54,7 +54,7 @@ window. Type ``Ctrl+D`` when you are ready to stop.
Now, let's open a new tab::
kitty @ new-window --new-tab --tab-title "My Tab" --keep-focus bash
kitty @ launch --type=tab --tab-title "My Tab" --keep-focus bash
This will open a new tab running the bash shell with the title "My Tab".
We can change the title of the tab with::
@@ -134,6 +134,19 @@ still write to the pipes of any other program on the same computer and
therefore can control |kitty|. It can, however, be useful to block programs
running on other computers (for example, over ssh) or as other users.
Mapping key presses to remote control commands
--------------------------------------------------
If you wish to trigger a remote control command easily with just a keypress,
you can map it in :file:`kitty.conf`. For example::
map F1 remote_control set-spacing margin=30
Then pressing the :kbd:`F1` key will set the active window margins to 30.
The syntax for what follows :code:`remote_control` is exactly the same
as the syntax for what follows :code:`kitty @` above.
Documentation for the remote control protocol
-----------------------------------------------

View File

@@ -137,6 +137,7 @@ static inline void
{parr}
{keys_enum}
enum KEYS key = '{initial_key}';
if (screen->parser_buf[pos] == ';') state = AFTER_VALUE;
while (pos < screen->parser_buf_pos) {{
switch(state) {{

View File

@@ -51,7 +51,8 @@ all_symbols: Set[int] = set()
name_map: Dict[int, str] = {}
word_search_map: DefaultDict[str, Set[int]] = defaultdict(set)
zwj = 0x200d
marks = set(emoji_skin_tone_modifiers) | {zwj}
flag_codepoints = frozenset(range(0x1F1E6, 0x1F1E6 + 26))
marks = set(emoji_skin_tone_modifiers) | {zwj} | flag_codepoints
not_assigned = set(range(0, sys.maxunicode))
@@ -103,36 +104,88 @@ def parse_ucd() -> None:
word_search_map['lamda'] |= word_search_map['lambda']
def split_two(line: str) -> Tuple[Set[int], str]:
spec, rest = line.split(';', 1)
spec, rest = spec.strip(), rest.strip().split(' ', 1)[0].strip()
def parse_range_spec(spec: str) -> Set[int]:
spec = spec.strip()
if '..' in spec:
chars_ = tuple(map(lambda x: int(x, 16), filter(None, spec.split('.'))))
chars = set(range(chars_[0], chars_[1] + 1))
else:
chars = {int(spec, 16)}
return chars, rest
return chars
def split_two(line: str) -> Tuple[Set[int], str]:
spec, rest = line.split(';', 1)
spec, rest = spec.strip(), rest.strip().split(' ', 1)[0].strip()
return parse_range_spec(spec), rest
all_emoji: Set[int] = set()
emoji_categories: Dict[str, Set[int]] = {}
emoji_presentation_bases: Set[int] = set()
narrow_emoji: Set[int] = set()
wide_emoji: Set[int] = set()
flags: Dict[int, List[int]] = {}
def parse_basic_emoji(spec: str) -> None:
parts = list(filter(None, spec.split()))
has_emoji_presentation = len(parts) < 2
chars = parse_range_spec(parts[0])
all_emoji.update(chars)
emoji_presentation_bases.update(chars)
(wide_emoji if has_emoji_presentation else narrow_emoji).update(chars)
def parse_keycap_sequence(spec: str) -> None:
base, fe0f, cc = list(filter(None, spec.split()))
chars = parse_range_spec(base)
all_emoji.update(chars)
emoji_presentation_bases.update(chars)
narrow_emoji.update(chars)
def parse_flag_emoji_sequence(spec: str) -> None:
a, b = list(filter(None, spec.split()))
left, right = int(a, 16), int(b, 16)
chars = {left, right}
all_emoji.update(chars)
wide_emoji.update(chars)
emoji_presentation_bases.update(chars)
flags.setdefault(left, []).append(right)
def parse_emoji_tag_sequence(spec: str) -> None:
a = int(spec.split()[0], 16)
all_emoji.add(a)
wide_emoji.add(a)
emoji_presentation_bases.add(a)
def parse_emoji_modifier_sequence(spec: str) -> None:
a, b = list(filter(None, spec.split()))
char, mod = int(a, 16), int(b, 16)
mod
all_emoji.add(char)
wide_emoji.add(char)
emoji_presentation_bases.add(char)
def parse_emoji() -> None:
for line in get_data('emoji-data.txt', 'emoji'):
chars, rest = split_two(line)
s = emoji_categories.setdefault(rest, set())
s.update(chars)
all_emoji.update(chars)
for line in get_data('emoji-variation-sequences.txt', 'emoji'):
parts = line.split()
base, var = parts[0], parts[1]
if base.startswith('#'):
for line in get_data('emoji-sequences.txt', 'emoji'):
parts = [x.strip() for x in line.split(';')]
if len(parts) < 2:
continue
if var.upper() == 'FE0F':
ibase = int(base, 16)
emoji_presentation_bases.add(ibase)
data, etype = parts[:2]
if etype == 'Basic_Emoji':
parse_basic_emoji(data)
elif etype == 'Emoji_Keycap_Sequence':
parse_keycap_sequence(data)
elif etype == 'RGI_Emoji_Flag_Sequence':
parse_flag_emoji_sequence(data)
elif etype == 'RGI_Emoji_Tag_Sequence':
parse_emoji_tag_sequence(data)
elif etype == 'RGI_Emoji_Modifier_Sequence':
parse_emoji_modifier_sequence(data)
doublewidth: Set[int] = set()
@@ -204,15 +257,6 @@ def gen_emoji() -> None:
p('\t}')
p('\treturn false;\n}')
p('static inline bool\nis_emoji_modifier(char_type code) {')
p('\tswitch(code) {')
for spec in get_ranges(list(emoji_categories['Emoji_Modifier'])):
write_case(spec, p)
p('\t\t\treturn true;')
p('\t\tdefault: return false;')
p('\t}')
p('\treturn false;\n}')
p('static inline bool\nis_symbol(char_type code) {')
p('\tswitch(code) {')
for spec in get_ranges(list(all_symbols)):
@@ -230,7 +274,9 @@ def category_test(
comment: str,
use_static: bool = False,
extra_chars: Union[FrozenSet[int], Set[int]] = frozenset(),
exclude: Union[Set[int], FrozenSet[int]] = frozenset()
exclude: Union[Set[int], FrozenSet[int]] = frozenset(),
least_check_return: Optional[str] = None,
ascii_range: Optional[str] = None
) -> None:
static = 'static inline ' if use_static else ''
chars: Set[int] = set()
@@ -240,10 +286,15 @@ def category_test(
chars -= exclude
p(f'{static}bool\n{name}(char_type code) {{')
p(f'\t// {comment} ({len(chars)} codepoints)' + ' {{' '{')
if least_check_return is not None:
least = min(chars)
p(f'\tif (LIKELY(code < {least})) return {least_check_return};')
if ascii_range is not None:
p(f'\tif (LIKELY(0x20 <= code && code <= 0x7e)) return {ascii_range};')
p('\tswitch(code) {')
for spec in get_ranges(list(chars)):
write_case(spec, p)
p(f'\t\t\treturn true;')
p('\t\t\treturn true;')
p('\t} // }}}\n')
p('\treturn false;\n}\n')
@@ -293,11 +344,15 @@ def gen_ucd() -> None:
{c for c in class_maps if c.startswith('M')},
'M category (marks)',
# See https://github.com/harfbuzz/harfbuzz/issues/169
extra_chars=emoji_skin_tone_modifiers | {zwj}
extra_chars=emoji_skin_tone_modifiers | {zwj},
least_check_return='false'
)
category_test(
'is_ignored_char', p, 'Cc Cf Cs'.split(),
'Control characters and non-characters', extra_chars=non_characters, exclude={zwj})
'Control characters and non-characters',
extra_chars=non_characters, exclude={zwj},
ascii_range='false'
)
category_test('is_word_char', p, {c for c in class_maps if c[0] in 'LN'}, 'L and N categories')
category_test('is_CZ_category', p, cz, 'C and Z categories')
category_test('is_P_category', p, {c for c in class_maps if c[0] == 'P'}, 'P category (punctuation)')
@@ -377,8 +432,8 @@ def gen_names() -> None:
p('}; // }}}\n')
# The trie
p(f'typedef struct {{ uint32_t children_offset; uint32_t match_offset; }} word_trie;\n')
all_trie_nodes: List['TrieNode'] = []
p('typedef struct { uint32_t children_offset; uint32_t match_offset; } word_trie;\n')
all_trie_nodes: List['TrieNode'] = [] # noqa
class TrieNode:
@@ -427,7 +482,7 @@ def gen_names() -> None:
def gen_wcwidth() -> None:
seen: Set[int] = set()
def add(p: Callable, comment: str, chars_: Set[int], ret: int) -> None:
def add(p: Callable, comment: str, chars_: Union[Set[int], FrozenSet[int]], ret: int) -> None:
chars = chars_ - seen
seen.update(chars)
p(f'\t\t// {comment} ({len(chars)} codepoints)' + ' {{' '{')
@@ -438,16 +493,18 @@ def gen_wcwidth() -> None:
with create_header('kitty/wcwidth-std.h') as p:
p('static int\nwcwidth_std(int32_t code) {')
p('\tif (LIKELY(0x20 <= code && code <= 0x7e)) return 1;')
p('\tswitch(code) {')
non_printing = class_maps['Cc'] | class_maps['Cf'] | class_maps['Cs']
add(p, 'Flags', flag_codepoints, 2)
add(p, 'Marks', marks | {0}, 0)
add(p, 'Non-printing characters', non_printing, -1)
add(p, 'Private use', class_maps['Co'], -3)
add(p, 'Text Presentation', emoji_categories['Emoji'] - emoji_categories['Emoji_Presentation'], 1)
add(p, 'Text Presentation', narrow_emoji, 1)
add(p, 'East Asian ambiguous width', ambiguous, -2)
add(p, 'East Asian double width', doublewidth, 2)
add(p, 'Emoji Presentation', emoji_categories['Emoji_Presentation'], 2)
add(p, 'Emoji Presentation', wide_emoji, 2)
add(p, 'Not assigned in the unicode character database', not_assigned, -4)

18
glfw/backend_utils.c vendored
View File

@@ -332,3 +332,21 @@ pollForEvents(EventLoopData *eld, monotonic_t timeout, watch_callback_func displ
}
return read_ok;
}
// Duplicate a UTF-8 encoded string
// but cut it so that it has at most max_length bytes plus the null byte.
// This does not take combining characters into account.
GLFWAPI char* utf_8_strndup(const char* source, size_t max_length) {
if (!source) return NULL;
size_t length = strnlen(source, max_length);
if (length >= max_length) {
for (length = max_length; length > 0; length--) {
if ((source[length] & 0xC0) != 0x80) break;
}
}
char* result = malloc(length + 1);
memcpy(result, source, length);
result[length] = 0;
return result;
}

View File

@@ -95,3 +95,4 @@ unsigned dispatchTimers(EventLoopData *eld);
void finalizePollData(EventLoopData *eld);
bool initPollData(EventLoopData *eld, int display_fd);
void wakeupEventLoop(EventLoopData *eld);
char* utf_8_strndup(const char* source, size_t max_length);

View File

@@ -424,11 +424,7 @@ display_reconfigured(CGDirectDisplayID display UNUSED, CGDisplayChangeSummaryFla
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
(void)sender;
_GLFWwindow* window;
for (window = _glfw.windowListHead; window; window = window->next)
_glfwInputWindowCloseRequest(window);
if (_glfw.callbacks.application_close) _glfw.callbacks.application_close(0);
return NSTerminateCancel;
}
@@ -673,8 +669,6 @@ int _glfwPlatformInit(void)
if (!initializeTIS())
return false;
_glfwInitJoysticksNS();
_glfwPollMonitorsNS();
return true;
@@ -727,7 +721,6 @@ void _glfwPlatformTerminate(void)
free(_glfw.ns.clipboardString);
_glfwTerminateNSGL();
_glfwTerminateJoysticksNS();
} // autoreleasepool
}

View File

@@ -44,7 +44,3 @@ typedef struct _GLFWjoystickNS
CFMutableArrayRef hats;
} _GLFWjoystickNS;
void _glfwInitJoysticksNS(void);
void _glfwTerminateJoysticksNS(void);

View File

@@ -233,6 +233,19 @@ static void matchCallback(void* context UNUSED,
break;
}
}
else if (page == kHIDPage_Simulation)
{
switch (usage)
{
case kHIDUsage_Sim_Accelerator:
case kHIDUsage_Sim_Brake:
case kHIDUsage_Sim_Throttle:
case kHIDUsage_Sim_Rudder:
case kHIDUsage_Sim_Steering:
target = axes;
break;
}
}
else if (page == kHIDPage_Button || page == kHIDPage_Consumer)
target = buttons;
@@ -291,12 +304,10 @@ static void removeCallback(void* context UNUSED,
//////////////////////////////////////////////////////////////////////////
////// GLFW internal API //////
////// GLFW platform API //////
//////////////////////////////////////////////////////////////////////////
// Initialize joystick interface
//
void _glfwInitJoysticksNS(void)
bool _glfwPlatformInitJoysticks(void)
{
CFMutableArrayRef matching;
const long usages[] =
@@ -315,7 +326,7 @@ void _glfwInitJoysticksNS(void)
if (!matching)
{
_glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create array");
return;
return false;
}
for (size_t i = 0; i < sizeof(usages) / sizeof(long); i++)
@@ -370,26 +381,24 @@ void _glfwInitJoysticksNS(void)
// Execute the run loop once in order to register any initially-attached
// joysticks
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
return true;
}
// Close all opened joystick handles
//
void _glfwTerminateJoysticksNS(void)
void _glfwPlatformTerminateJoysticks(void)
{
int jid;
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
closeJoystick(_glfw.joysticks + jid);
CFRelease(_glfw.ns.hidManager);
_glfw.ns.hidManager = NULL;
if (_glfw.ns.hidManager)
{
CFRelease(_glfw.ns.hidManager);
_glfw.ns.hidManager = NULL;
}
}
//////////////////////////////////////////////////////////////////////////
////// GLFW platform API //////
//////////////////////////////////////////////////////////////////////////
int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode)
{
if (mode & _GLFW_POLL_AXES)

View File

@@ -236,26 +236,71 @@ static bool refreshMonitorScreen(_GLFWmonitor* monitor)
return false;
}
// Returns a fallback refresh rate for when Core Graphics says it is zero
// Returns the display refresh rate queried from the I/O registry
//
static double getFallbackRefreshRate(_GLFWmonitor* monitor)
static double getFallbackRefreshRate(CGDirectDisplayID displayID)
{
CGDisplayModeRef mode = CGDisplayCopyDisplayMode(monitor->ns.displayID);
double refreshRate = CGDisplayModeGetRefreshRate(mode);
CGDisplayModeRelease(mode);
double refreshRate = 60.0;
if (refreshRate == 0.0)
io_iterator_t it;
io_service_t service;
if (IOServiceGetMatchingServices(kIOMasterPortDefault,
IOServiceMatching("IOFramebuffer"),
&it) != 0)
{
CVDisplayLinkRef link = NULL;
CVDisplayLinkCreateWithCGDisplay(monitor->ns.displayID, &link);
const CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link);
if (!(time.flags & kCVTimeIsIndefinite))
refreshRate = (int) (time.timeScale / (double) time.timeValue);
CVDisplayLinkRelease(link);
return refreshRate;
}
while ((service = IOIteratorNext(it)) != 0)
{
const CFNumberRef indexRef =
IORegistryEntryCreateCFProperty(service,
CFSTR("IOFramebufferOpenGLIndex"),
kCFAllocatorDefault,
kNilOptions);
if (!indexRef)
continue;
uint32_t index = 0;
CFNumberGetValue(indexRef, kCFNumberIntType, &index);
CFRelease(indexRef);
if (CGOpenGLDisplayMaskToDisplayID(1 << index) != displayID)
continue;
const CFNumberRef clockRef =
IORegistryEntryCreateCFProperty(service,
CFSTR("IOFBCurrentPixelClock"),
kCFAllocatorDefault,
kNilOptions);
const CFNumberRef countRef =
IORegistryEntryCreateCFProperty(service,
CFSTR("IOFBCurrentPixelCount"),
kCFAllocatorDefault,
kNilOptions);
uint32_t clock = 0, count = 0;
if (clockRef)
{
CFNumberGetValue(clockRef, kCFNumberIntType, &clock);
CFRelease(clockRef);
}
if (countRef)
{
CFNumberGetValue(countRef, kCFNumberIntType, &count);
CFRelease(countRef);
}
if (clock > 0 && count > 0)
refreshRate = clock / (double) count;
break;
}
IOObjectRelease(it);
return refreshRate;
}
@@ -351,11 +396,15 @@ void _glfwPollMonitorsNS(void)
_GLFWmonitor* monitor = _glfwAllocMonitor(name, (int)size.width, (int)size.height);
monitor->ns.displayID = displays[i];
monitor->ns.unitNumber = unitNumber;
monitor->ns.fallbackRefreshRate = getFallbackRefreshRate(monitor);
createDisplayLink(monitor->ns.displayID);
free(name);
CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displays[i]);
if (CGDisplayModeGetRefreshRate(mode) == 0.0)
monitor->ns.fallbackRefreshRate = getFallbackRefreshRate(displays[i]);
CGDisplayModeRelease(mode);
_glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST);
}

View File

@@ -96,16 +96,11 @@ typedef VkResult (APIENTRY *PFN_vkCreateMetalSurfaceEXT)(VkInstance,const VkMeta
#include "posix_thread.h"
#include "cocoa_joystick.h"
#include "nsgl_context.h"
#include "egl_context.h"
#include "osmesa_context.h"
#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)
#define _glfw_dlclose(handle) dlclose(handle)
#define _glfw_dlsym(handle, name) dlsym(handle, name)
#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->ns.view)
#define _GLFW_EGL_NATIVE_DISPLAY EGL_DEFAULT_DISPLAY
#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNS ns
#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryNS ns
#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerNS ns

View File

@@ -1133,7 +1133,7 @@ is_ascii_control_char(char x) {
break;
}
_glfwInputScroll(window, deltaX, deltaY, flags);
_glfwInputScroll(window, deltaX, deltaY, flags, translateFlags([event modifierFlags]));
}
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
@@ -1460,6 +1460,11 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
}
else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
{
// EGL implementation on macOS use CALayer* EGLNativeWindowType so we
// need to get the layer for EGL window surface creation.
[window->ns.view setWantsLayer:YES];
window->ns.layer = [window->ns.view layer];
if (!_glfwInitEGL())
return false;
if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
@@ -1522,7 +1527,8 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window UNUSED, const char* title)
void _glfwPlatformSetWindowIcon(_GLFWwindow* window UNUSED,
int count UNUSED, const GLFWimage* images UNUSED)
{
// Regular windows do not have icons
_glfwInputError(GLFW_FEATURE_UNAVAILABLE,
"Cocoa: Regular windows do not have icons on macOS");
}
void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
@@ -1848,6 +1854,11 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, bool enabled)
[window->ns.object setLevel:NSNormalWindowLevel];
}
void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, bool enabled)
{
[window->ns.object setIgnoresMouseEvents:enabled];
}
float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
{
return (float) [window->ns.object alphaValue];
@@ -1858,6 +1869,17 @@ void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
[window->ns.object setAlphaValue:opacity];
}
void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window UNUSED, bool enabled UNUSED)
{
_glfwInputError(GLFW_FEATURE_UNIMPLEMENTED,
"Cocoa: Raw mouse motion not yet implemented");
}
bool _glfwPlatformRawMouseMotionSupported(void)
{
return false;
}
void
_glfwDispatchRenderFrame(CGDirectDisplayID displayID) {
_GLFWwindow *w = _glfw.windowListHead;
@@ -2088,6 +2110,47 @@ const char* _glfwPlatformGetClipboardString(void)
return _glfw.ns.clipboardString;
}
EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs)
{
if (_glfw.egl.ANGLE_platform_angle)
{
int type = 0;
if (_glfw.egl.ANGLE_platform_angle_opengl)
{
if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_OPENGL)
type = EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE;
}
if (_glfw.egl.ANGLE_platform_angle_metal)
{
if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_METAL)
type = EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE;
}
if (type)
{
*attribs = calloc(3, sizeof(EGLint));
(*attribs)[0] = EGL_PLATFORM_ANGLE_TYPE_ANGLE;
(*attribs)[1] = type;
(*attribs)[2] = EGL_NONE;
return EGL_PLATFORM_ANGLE_ANGLE;
}
}
return 0;
}
EGLNativeDisplayType _glfwPlatformGetEGLNativeDisplay(void)
{
return EGL_DEFAULT_DISPLAY;
}
EGLNativeWindowType _glfwPlatformGetEGLNativeWindow(_GLFWwindow* window)
{
return window->ns.layer;
}
void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
{
if (_glfw.vk.KHR_surface && _glfw.vk.EXT_metal_surface)

168
glfw/context.c vendored
View File

@@ -173,174 +173,6 @@ bool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
return true;
}
// Chooses the framebuffer config that best matches the desired one
//
const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired,
const _GLFWfbconfig* alternatives,
unsigned int count)
{
unsigned int i;
unsigned int missing, leastMissing = UINT_MAX;
unsigned int colorDiff, leastColorDiff = UINT_MAX;
unsigned int extraDiff, leastExtraDiff = UINT_MAX;
const _GLFWfbconfig* current;
const _GLFWfbconfig* closest = NULL;
for (i = 0; i < count; i++)
{
current = alternatives + i;
if (desired->stereo > 0 && current->stereo == 0)
{
// Stereo is a hard constraint
continue;
}
if (desired->doublebuffer != current->doublebuffer)
{
// Double buffering is a hard constraint
continue;
}
// Count number of missing buffers
{
missing = 0;
if (desired->alphaBits > 0 && current->alphaBits == 0)
missing++;
if (desired->depthBits > 0 && current->depthBits == 0)
missing++;
if (desired->stencilBits > 0 && current->stencilBits == 0)
missing++;
if (desired->auxBuffers > 0 &&
current->auxBuffers < desired->auxBuffers)
{
missing += desired->auxBuffers - current->auxBuffers;
}
if (desired->samples > 0 && current->samples == 0)
{
// Technically, several multisampling buffers could be
// involved, but that's a lower level implementation detail and
// not important to us here, so we count them as one
missing++;
}
if (desired->transparent != current->transparent)
missing++;
}
// These polynomials make many small channel size differences matter
// less than one large channel size difference
// Calculate color channel size difference value
{
colorDiff = 0;
if (desired->redBits != GLFW_DONT_CARE)
{
colorDiff += (desired->redBits - current->redBits) *
(desired->redBits - current->redBits);
}
if (desired->greenBits != GLFW_DONT_CARE)
{
colorDiff += (desired->greenBits - current->greenBits) *
(desired->greenBits - current->greenBits);
}
if (desired->blueBits != GLFW_DONT_CARE)
{
colorDiff += (desired->blueBits - current->blueBits) *
(desired->blueBits - current->blueBits);
}
}
// Calculate non-color channel size difference value
{
extraDiff = 0;
if (desired->alphaBits != GLFW_DONT_CARE)
{
extraDiff += (desired->alphaBits - current->alphaBits) *
(desired->alphaBits - current->alphaBits);
}
if (desired->depthBits != GLFW_DONT_CARE)
{
extraDiff += (desired->depthBits - current->depthBits) *
(desired->depthBits - current->depthBits);
}
if (desired->stencilBits != GLFW_DONT_CARE)
{
extraDiff += (desired->stencilBits - current->stencilBits) *
(desired->stencilBits - current->stencilBits);
}
if (desired->accumRedBits != GLFW_DONT_CARE)
{
extraDiff += (desired->accumRedBits - current->accumRedBits) *
(desired->accumRedBits - current->accumRedBits);
}
if (desired->accumGreenBits != GLFW_DONT_CARE)
{
extraDiff += (desired->accumGreenBits - current->accumGreenBits) *
(desired->accumGreenBits - current->accumGreenBits);
}
if (desired->accumBlueBits != GLFW_DONT_CARE)
{
extraDiff += (desired->accumBlueBits - current->accumBlueBits) *
(desired->accumBlueBits - current->accumBlueBits);
}
if (desired->accumAlphaBits != GLFW_DONT_CARE)
{
extraDiff += (desired->accumAlphaBits - current->accumAlphaBits) *
(desired->accumAlphaBits - current->accumAlphaBits);
}
if (desired->samples != GLFW_DONT_CARE)
{
extraDiff += (desired->samples - current->samples) *
(desired->samples - current->samples);
}
if (desired->sRGB && !current->sRGB)
extraDiff++;
}
// Figure out if the current one is better than the best one found so far
// Least number of missing buffers is the most important heuristic,
// then color buffer size match and lastly size match for other buffers
if (missing < leastMissing)
closest = current;
else if (missing == leastMissing)
{
if ((colorDiff < leastColorDiff) ||
(colorDiff == leastColorDiff && extraDiff < leastExtraDiff))
{
closest = current;
}
}
if (current == closest)
{
leastMissing = missing;
leastColorDiff = colorDiff;
leastExtraDiff = extraDiff;
}
}
return closest;
}
// Retrieves the attributes of the current context
//
bool _glfwRefreshContextAttribs(_GLFWwindow* window,

182
glfw/egl_context.c vendored
View File

@@ -76,6 +76,7 @@ static const char* getEGLErrorString(EGLint error)
}
}
#ifdef _GLFW_X11
// Returns the specified attribute of the specified EGLConfig
//
static int getEGLConfigAttrib(EGLConfig config, int attrib)
@@ -84,6 +85,7 @@ static int getEGLConfigAttrib(EGLConfig config, int attrib)
eglGetConfigAttrib(_glfw.egl.display, config, attrib, &value);
return value;
}
#endif
// Return the EGLConfig most closely matching the specified hints
//
@@ -91,39 +93,44 @@ static bool chooseEGLConfig(const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* desired,
EGLConfig* result)
{
EGLConfig* nativeConfigs;
_GLFWfbconfig* usableConfigs;
const _GLFWfbconfig* closest;
int i, nativeCount, usableCount;
EGLConfig configs[512];
int i = 0, nativeCount = 0, ans_idx = 0;
EGLint attributes[64];
#define ATTR(k, v) { attributes[i++] = k; attributes[i++] = v; }
ATTR(EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER);
ATTR(EGL_SURFACE_TYPE, EGL_WINDOW_BIT);
if (ctxconfig->client == GLFW_OPENGL_ES_API) {
if (ctxconfig->major == 1) ATTR(EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT)
else ATTR(EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT);
}
else if (ctxconfig->client == GLFW_OPENGL_API) ATTR(EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT);
if (desired->samples > 0) ATTR(EGL_SAMPLES, desired->samples);
if (desired->depthBits > 0) ATTR(EGL_DEPTH_SIZE, desired->depthBits);
if (desired->stencilBits > 0) ATTR(EGL_STENCIL_SIZE, desired->stencilBits);
if (desired->redBits > 0) ATTR(EGL_RED_SIZE, desired->redBits);
if (desired->greenBits > 0) ATTR(EGL_GREEN_SIZE, desired->greenBits);
if (desired->blueBits > 0) ATTR(EGL_BLUE_SIZE, desired->blueBits);
if (desired->alphaBits > 0) ATTR(EGL_ALPHA_SIZE, desired->alphaBits);
ATTR(EGL_NONE, EGL_NONE);
#undef ATTR
if (!eglChooseConfig(_glfw.egl.display, attributes, configs, sizeof(configs)/sizeof(configs[0]), &nativeCount)) {
_glfwInputError(GLFW_API_UNAVAILABLE, "EGL: eglChooseConfig failed");
return false;
}
eglGetConfigs(_glfw.egl.display, NULL, 0, &nativeCount);
if (!nativeCount)
{
_glfwInputError(GLFW_API_UNAVAILABLE, "EGL: No EGLConfigs returned");
return false;
}
nativeConfigs = calloc(nativeCount, sizeof(EGLConfig));
eglGetConfigs(_glfw.egl.display, nativeConfigs, nativeCount, &nativeCount);
usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig));
usableCount = 0;
for (i = 0; i < nativeCount; i++)
{
const EGLConfig n = nativeConfigs[i];
_GLFWfbconfig* u = usableConfigs + usableCount;
// Only consider RGB(A) EGLConfigs
if (getEGLConfigAttrib(n, EGL_COLOR_BUFFER_TYPE) != EGL_RGB_BUFFER)
continue;
// Only consider window EGLConfigs
if (!(getEGLConfigAttrib(n, EGL_SURFACE_TYPE) & EGL_WINDOW_BIT))
continue;
#if defined(_GLFW_X11)
{
const EGLConfig n = configs[i];
XVisualInfo vi = {0};
// Only consider EGLConfigs with associated Visuals
@@ -138,55 +145,18 @@ static bool chooseEGLConfig(const _GLFWctxconfig* ctxconfig,
XGetVisualInfo(_glfw.x11.display, VisualIDMask, &vi, &count);
if (vis)
{
u->transparent = _glfwIsVisualTransparentX11(vis[0].visual);
bool transparent = _glfwIsVisualTransparentX11(vis[0].visual);
XFree(vis);
if (!transparent) continue;
}
}
}
#endif // _GLFW_X11
if (ctxconfig->client == GLFW_OPENGL_ES_API)
{
if (ctxconfig->major == 1)
{
if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES_BIT))
continue;
}
else
{
if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES2_BIT))
continue;
}
}
else if (ctxconfig->client == GLFW_OPENGL_API)
{
if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_BIT))
continue;
}
u->redBits = getEGLConfigAttrib(n, EGL_RED_SIZE);
u->greenBits = getEGLConfigAttrib(n, EGL_GREEN_SIZE);
u->blueBits = getEGLConfigAttrib(n, EGL_BLUE_SIZE);
u->alphaBits = getEGLConfigAttrib(n, EGL_ALPHA_SIZE);
u->depthBits = getEGLConfigAttrib(n, EGL_DEPTH_SIZE);
u->stencilBits = getEGLConfigAttrib(n, EGL_STENCIL_SIZE);
u->samples = getEGLConfigAttrib(n, EGL_SAMPLES);
u->doublebuffer = true;
u->handle = (uintptr_t) n;
usableCount++;
ans_idx = i;
break;
}
closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount);
if (closest)
*result = (EGLConfig) closest->handle;
free(nativeConfigs);
free(usableConfigs);
return closest != NULL;
*result = configs[ans_idx];
return true;
}
static void makeContextCurrentEGL(_GLFWwindow* window)
@@ -303,6 +273,8 @@ static void destroyContextEGL(_GLFWwindow* window)
bool _glfwInitEGL(void)
{
int i;
EGLint* attribs = NULL;
const char* extensions;
const char* sonames[] =
{
#if defined(_GLFW_EGL_LIBRARY)
@@ -340,6 +312,7 @@ bool _glfwInitEGL(void)
glfw_dlsym(_glfw.egl.GetConfigAttrib, _glfw.egl.handle, "eglGetConfigAttrib");
glfw_dlsym(_glfw.egl.GetConfigs, _glfw.egl.handle, "eglGetConfigs");
glfw_dlsym(_glfw.egl.ChooseConfig, _glfw.egl.handle, "eglChooseConfig");
glfw_dlsym(_glfw.egl.GetDisplay, _glfw.egl.handle, "eglGetDisplay");
glfw_dlsym(_glfw.egl.GetError, _glfw.egl.handle, "eglGetError");
glfw_dlsym(_glfw.egl.Initialize, _glfw.egl.handle, "eglInitialize");
@@ -357,6 +330,7 @@ bool _glfwInitEGL(void)
if (!_glfw.egl.GetConfigAttrib ||
!_glfw.egl.GetConfigs ||
!_glfw.egl.ChooseConfig ||
!_glfw.egl.GetDisplay ||
!_glfw.egl.GetError ||
!_glfw.egl.Initialize ||
@@ -379,7 +353,51 @@ bool _glfwInitEGL(void)
return false;
}
_glfw.egl.display = eglGetDisplay(_GLFW_EGL_NATIVE_DISPLAY);
extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
if (extensions && eglGetError() == EGL_SUCCESS)
_glfw.egl.EXT_client_extensions = true;
if (_glfw.egl.EXT_client_extensions)
{
_glfw.egl.EXT_platform_base =
_glfwStringInExtensionString("EGL_EXT_platform_base", extensions);
_glfw.egl.EXT_platform_x11 =
_glfwStringInExtensionString("EGL_EXT_platform_x11", extensions);
_glfw.egl.EXT_platform_wayland =
_glfwStringInExtensionString("EGL_EXT_platform_wayland", extensions);
_glfw.egl.ANGLE_platform_angle =
_glfwStringInExtensionString("EGL_ANGLE_platform_angle", extensions);
_glfw.egl.ANGLE_platform_angle_opengl =
_glfwStringInExtensionString("EGL_ANGLE_platform_angle_opengl", extensions);
_glfw.egl.ANGLE_platform_angle_d3d =
_glfwStringInExtensionString("EGL_ANGLE_platform_angle_d3d", extensions);
_glfw.egl.ANGLE_platform_angle_vulkan =
_glfwStringInExtensionString("EGL_ANGLE_platform_angle_vulkan", extensions);
_glfw.egl.ANGLE_platform_angle_metal =
_glfwStringInExtensionString("EGL_ANGLE_platform_angle_metal", extensions);
}
if (_glfw.egl.EXT_platform_base)
{
_glfw.egl.GetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)
eglGetProcAddress("eglGetPlatformDisplayEXT");
_glfw.egl.CreatePlatformWindowSurfaceEXT = (PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)
eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT");
}
_glfw.egl.platform = _glfwPlatformGetEGLPlatform(&attribs);
if (_glfw.egl.platform)
{
_glfw.egl.display =
eglGetPlatformDisplayEXT(_glfw.egl.platform,
_glfwPlatformGetEGLNativeDisplay(),
attribs);
}
else
_glfw.egl.display = eglGetDisplay(_glfwPlatformGetEGLNativeDisplay());
free(attribs);
if (_glfw.egl.display == EGL_NO_DISPLAY)
{
_glfwInputError(GLFW_API_UNAVAILABLE,
@@ -447,6 +465,7 @@ bool _glfwCreateContextEGL(_GLFWwindow* window,
EGLint attribs[40];
EGLConfig config;
EGLContext share = NULL;
EGLNativeWindowType native;
int index = 0;
if (!_glfw.egl.display)
@@ -572,23 +591,30 @@ bool _glfwCreateContextEGL(_GLFWwindow* window,
}
// Set up attributes for surface creation
index = 0;
if (fbconfig->sRGB)
{
int index = 0;
if (fbconfig->sRGB)
{
if (_glfw.egl.KHR_gl_colorspace)
setAttrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR);
}
setAttrib(EGL_NONE, EGL_NONE);
if (_glfw.egl.KHR_gl_colorspace)
setAttrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR);
}
setAttrib(EGL_NONE, EGL_NONE);
native = _glfwPlatformGetEGLNativeWindow(window);
// HACK: ANGLE does not implement eglCreatePlatformWindowSurfaceEXT
// despite reporting EGL_EXT_platform_base
if (_glfw.egl.platform && _glfw.egl.platform != EGL_PLATFORM_ANGLE_ANGLE)
{
window->context.egl.surface =
eglCreatePlatformWindowSurfaceEXT(_glfw.egl.display, config, native, attribs);
}
else
{
window->context.egl.surface =
eglCreateWindowSurface(_glfw.egl.display, config, native, attribs);
}
window->context.egl.surface =
eglCreateWindowSurface(_glfw.egl.display,
config,
_GLFW_EGL_NATIVE_WINDOW,
attribs);
if (window->context.egl.surface == EGL_NO_SURFACE)
{
_glfwInputError(GLFW_PLATFORM_ERROR,

57
glfw/egl_context.h vendored
View File

@@ -25,26 +25,10 @@
//
//========================================================================
#if defined(_GLFW_USE_EGLPLATFORM_H)
#include <EGL/eglplatform.h>
#elif defined(_GLFW_WIN32)
#if defined(_GLFW_WIN32)
#define EGLAPIENTRY __stdcall
typedef HDC EGLNativeDisplayType;
typedef HWND EGLNativeWindowType;
#elif defined(_GLFW_COCOA)
#define EGLAPIENTRY
typedef void* EGLNativeDisplayType;
typedef id EGLNativeWindowType;
#elif defined(_GLFW_X11)
#define EGLAPIENTRY
typedef Display* EGLNativeDisplayType;
typedef Window EGLNativeWindowType;
#elif defined(_GLFW_WAYLAND)
#define EGLAPIENTRY
typedef struct wl_display* EGLNativeDisplayType;
typedef struct wl_egl_window* EGLNativeWindowType;
#else
#error "No supported EGL platform selected"
#define EGLAPIENTRY
#endif
#define EGL_SUCCESS 0x3000
@@ -108,6 +92,17 @@ typedef struct wl_egl_window* EGLNativeWindowType;
#define EGL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x2097
#define EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR 0
#define EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x2098
#define EGL_PLATFORM_X11_EXT 0x31d5
#define EGL_PLATFORM_WAYLAND_EXT 0x31d8
#define EGL_PLATFORM_ANGLE_ANGLE 0x3202
#define EGL_PLATFORM_ANGLE_TYPE_ANGLE 0x3203
#define EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE 0x320d
#define EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE 0x320e
#define EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE 0x3207
#define EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE 0x3208
#define EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE 0x3450
#define EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE 0x3489
#define EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE 0x348f
typedef int EGLint;
typedef unsigned int EGLBoolean;
@@ -117,9 +112,13 @@ typedef void* EGLContext;
typedef void* EGLDisplay;
typedef void* EGLSurface;
typedef void* EGLNativeDisplayType;
typedef void* EGLNativeWindowType;
// EGL function pointer typedefs
typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigAttrib)(EGLDisplay,EGLConfig,EGLint,EGLint*);
typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigs)(EGLDisplay,EGLConfig*,EGLint,EGLint*);
typedef EGLBoolean (EGLAPIENTRY * PFN_eglChooseConfig)(EGLDisplay,EGLint const*,EGLConfig*,EGLint,EGLint*);
typedef EGLDisplay (EGLAPIENTRY * PFN_eglGetDisplay)(EGLNativeDisplayType);
typedef EGLint (EGLAPIENTRY * PFN_eglGetError)(void);
typedef EGLBoolean (EGLAPIENTRY * PFN_eglInitialize)(EGLDisplay,EGLint*,EGLint*);
@@ -136,6 +135,7 @@ typedef const char* (EGLAPIENTRY * PFN_eglQueryString)(EGLDisplay,EGLint);
typedef GLFWglproc (EGLAPIENTRY * PFN_eglGetProcAddress)(const char*);
#define eglGetConfigAttrib _glfw.egl.GetConfigAttrib
#define eglGetConfigs _glfw.egl.GetConfigs
#define eglChooseConfig _glfw.egl.ChooseConfig
#define eglGetDisplay _glfw.egl.GetDisplay
#define eglGetError _glfw.egl.GetError
#define eglInitialize _glfw.egl.Initialize
@@ -151,9 +151,10 @@ typedef GLFWglproc (EGLAPIENTRY * PFN_eglGetProcAddress)(const char*);
#define eglQueryString _glfw.egl.QueryString
#define eglGetProcAddress _glfw.egl.GetProcAddress
#define _GLFW_EGL_CONTEXT_STATE _GLFWcontextEGL egl
#define _GLFW_EGL_LIBRARY_CONTEXT_STATE _GLFWlibraryEGL egl
typedef EGLDisplay (EGLAPIENTRY * PFNEGLGETPLATFORMDISPLAYEXTPROC)(EGLenum,void*,const EGLint*);
typedef EGLSurface (EGLAPIENTRY * PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)(EGLDisplay,EGLConfig,void*,const EGLint*);
#define eglGetPlatformDisplayEXT _glfw.egl.GetPlatformDisplayEXT
#define eglCreatePlatformWindowSurfaceEXT _glfw.egl.CreatePlatformWindowSurfaceEXT
// EGL-specific per-context data
//
@@ -171,6 +172,7 @@ typedef struct _GLFWcontextEGL
//
typedef struct _GLFWlibraryEGL
{
EGLenum platform;
EGLDisplay display;
EGLint major, minor;
bool prefix;
@@ -180,11 +182,21 @@ typedef struct _GLFWlibraryEGL
bool KHR_gl_colorspace;
bool KHR_get_all_proc_addresses;
bool KHR_context_flush_control;
bool EXT_client_extensions;
bool EXT_platform_base;
bool EXT_platform_x11;
bool EXT_platform_wayland;
bool ANGLE_platform_angle;
bool ANGLE_platform_angle_opengl;
bool ANGLE_platform_angle_d3d;
bool ANGLE_platform_angle_vulkan;
bool ANGLE_platform_angle_metal;
void* handle;
PFN_eglGetConfigAttrib GetConfigAttrib;
PFN_eglGetConfigs GetConfigs;
PFN_eglChooseConfig ChooseConfig;
PFN_eglGetDisplay GetDisplay;
PFN_eglGetError GetError;
PFN_eglInitialize Initialize;
@@ -200,6 +212,9 @@ typedef struct _GLFWlibraryEGL
PFN_eglQueryString QueryString;
PFN_eglGetProcAddress GetProcAddress;
PFNEGLGETPLATFORMDISPLAYEXTPROC GetPlatformDisplayEXT;
PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC CreatePlatformWindowSurfaceEXT;
} _GLFWlibraryEGL;

View File

@@ -6,7 +6,7 @@ import json
import os
import re
import sys
from typing import Callable, List, Optional, Tuple
from typing import Callable, Dict, List, Optional, Tuple
_plat = sys.platform.lower()
is_linux = 'linux' in _plat
@@ -19,6 +19,7 @@ class Env:
cppflags: List[str] = []
cflags: List[str] = []
ldflags: List[str] = []
library_paths: Dict[str, List[str]] = {}
ldpaths: List[str] = []
ccver: Tuple[int, int]
@@ -32,13 +33,13 @@ class Env:
def __init__(
self, cc: str = '', cppflags: List[str] = [], cflags: List[str] = [], ldflags: List[str] = [],
ldpaths: Optional[List[str]] = None, ccver: Tuple[int, int] = (0, 0)
library_paths: Dict[str, List[str]] = {}, ldpaths: Optional[List[str]] = None, ccver: Tuple[int, int] = (0, 0)
):
self.cc, self.cppflags, self.cflags, self.ldflags, self.ldpaths = cc, cppflags, cflags, ldflags, [] if ldpaths is None else ldpaths
self.ccver = ccver
self.cc, self.cppflags, self.cflags, self.ldflags, self.library_paths = cc, cppflags, cflags, ldflags, library_paths
self.ldpaths, self.ccver = [] if ldpaths is None else ldpaths, ccver
def copy(self) -> 'Env':
ans = Env(self.cc, list(self.cppflags), list(self.cflags), list(self.ldflags), list(self.ldpaths), self.ccver)
ans = Env(self.cc, list(self.cppflags), list(self.cflags), list(self.ldflags), dict(self.library_paths), list(self.ldpaths), self.ccver)
ans.all_headers = list(self.all_headers)
ans.sources = list(self.sources)
ans.wayland_packagedir = self.wayland_packagedir
@@ -55,7 +56,7 @@ def wayland_protocol_file_name(base: str, ext: str = 'c') -> str:
def init_env(env: Env, pkg_config: Callable, at_least_version: Callable, test_compile: Callable, module: str = 'x11') -> Env:
ans = env.copy()
ans.cflags.append('-fpic')
ans.cflags.append('-fPIC')
ans.cppflags.append('-D_GLFW_' + module.upper())
ans.cppflags.append('-D_GLFW_BUILD_DLL')
@@ -95,7 +96,7 @@ def init_env(env: Env, pkg_config: Callable, at_least_version: Callable, test_co
for p in ans.wayland_protocols:
ans.sources.append(wayland_protocol_file_name(p))
ans.all_headers.append(wayland_protocol_file_name(p, 'h'))
for dep in 'wayland-egl wayland-client wayland-cursor xkbcommon dbus-1'.split():
for dep in 'wayland-client wayland-cursor xkbcommon dbus-1'.split():
ans.cflags.extend(pkg_config(dep, '--cflags-only-I'))
ans.ldpaths.extend(pkg_config(dep, '--libs'))
has_memfd_create = test_compile(env.cc, '-Werror', src='''#define _GNU_SOURCE

220
glfw/glfw3.h vendored
View File

@@ -195,7 +195,38 @@ extern "C" {
#endif /*__APPLE__*/
#elif !defined(GLFW_INCLUDE_NONE)
#elif defined(GLFW_INCLUDE_GLU)
#if defined(__APPLE__)
#if defined(GLFW_INCLUDE_GLU)
#include <OpenGL/glu.h>
#endif
#else /*__APPLE__*/
#if defined(GLFW_INCLUDE_GLU)
#include <GL/glu.h>
#endif
#endif /*__APPLE__*/
#elif !defined(GLFW_INCLUDE_NONE) && \
!defined(__gl_h_) && \
!defined(__gles1_gl_h_) && \
!defined(__gles2_gl2_h_) && \
!defined(__gles2_gl3_h_) && \
!defined(__gles2_gl31_h_) && \
!defined(__gles2_gl32_h_) && \
!defined(__gl_glcorearb_h_) && \
!defined(__gl2_h_) /*legacy*/ && \
!defined(__gl3_h_) /*legacy*/ && \
!defined(__gl31_h_) /*legacy*/ && \
!defined(__gl32_h_) /*legacy*/ && \
!defined(__glcorearb_h_) /*legacy*/ && \
!defined(__GL_H__) /*non-standard*/ && \
!defined(__gltypes_h_) /*non-standard*/ && \
!defined(__glee_h_) /*non-standard*/
#if defined(__APPLE__)
@@ -203,9 +234,6 @@ extern "C" {
#define GL_GLEXT_LEGACY
#endif
#include <OpenGL/gl.h>
#if defined(GLFW_INCLUDE_GLU)
#include <OpenGL/glu.h>
#endif
#else /*__APPLE__*/
@@ -213,9 +241,6 @@ extern "C" {
#if defined(GLFW_INCLUDE_GLEXT)
#include <GL/glext.h>
#endif
#if defined(GLFW_INCLUDE_GLU)
#include <GL/glu.h>
#endif
#endif /*__APPLE__*/
@@ -806,6 +831,33 @@ extern "C" {
* @analysis Application programmer error. Fix the offending call.
*/
#define GLFW_NO_WINDOW_CONTEXT 0x0001000A
/*! @brief The requested feature is not provided by the platform.
*
* The requested feature is not provided by the platform, so GLFW is unable to
* implement it. The documentation for each function notes if it could emit
* this error.
*
* @analysis Platform or platform version limitation. The error can be ignored
* unless the feature is critical to the application.
*
* @par
* A function call that emits this error has no effect other than the error and
* updating any existing out parameters.
*/
#define GLFW_FEATURE_UNAVAILABLE 0x0001000C
/*! @brief The requested feature is not implemented for the platform.
*
* The requested feature has not yet been implemented in GLFW for this platform.
*
* @analysis An incomplete implementation of GLFW for this platform, hopefully
* fixed in a future release. The error can be ignored unless the feature is
* critical to the application.
*
* @par
* A function call that emits this error has no effect other than the error and
* updating any existing out parameters.
*/
#define GLFW_FEATURE_UNIMPLEMENTED 0x0001000D
/*! @} */
/*! @addtogroup window
@@ -880,11 +932,19 @@ extern "C" {
* [window attribute](@ref GLFW_FOCUS_ON_SHOW_attrib).
*/
#define GLFW_FOCUS_ON_SHOW 0x0002000C
/*! @brief Mouse input transparency window hint and attribute
*
* Mouse input transparency [window hint](@ref GLFW_MOUSE_PASSTHROUGH_hint) or
* [window attribute](@ref GLFW_MOUSE_PASSTHROUGH_attrib).
*/
#define GLFW_MOUSE_PASSTHROUGH 0x0002000D
/*! @brief Occlusion window attribute
*
* Occlusion [window attribute](@ref GLFW_OCCLUDED_attrib).
*/
#define GLFW_OCCLUDED 0x0002000D
#define GLFW_OCCLUDED 0x0002000E
/*! @brief Framebuffer bit depth hint.
*
* Framebuffer bit depth [hint](@ref GLFW_RED_BITS).
@@ -1002,12 +1062,17 @@ extern "C" {
* and [attribute](@ref GLFW_OPENGL_FORWARD_COMPAT_attrib).
*/
#define GLFW_OPENGL_FORWARD_COMPAT 0x00022006
/*! @brief OpenGL debug context hint and attribute.
/*! @brief Debug mode context hint and attribute.
*
* OpenGL debug context [hint](@ref GLFW_OPENGL_DEBUG_CONTEXT_hint) and
* [attribute](@ref GLFW_OPENGL_DEBUG_CONTEXT_attrib).
* Debug mode context [hint](@ref GLFW_CONTEXT_DEBUG_hint) and
* [attribute](@ref GLFW_CONTEXT_DEBUG_attrib).
*/
#define GLFW_OPENGL_DEBUG_CONTEXT 0x00022007
#define GLFW_CONTEXT_DEBUG 0x00022007
/*! @brief Legacy name for compatibility.
*
* This is an alias for compatibility with earlier versions.
*/
#define GLFW_OPENGL_DEBUG_CONTEXT GLFW_CONTEXT_DEBUG
/*! @brief OpenGL profile hint and attribute.
*
* OpenGL profile [hint](@ref GLFW_OPENGL_PROFILE_hint) and
@@ -1076,6 +1141,7 @@ extern "C" {
#define GLFW_STICKY_KEYS 0x00033002
#define GLFW_STICKY_MOUSE_BUTTONS 0x00033003
#define GLFW_LOCK_KEY_MODS 0x00033004
#define GLFW_RAW_MOUSE_MOTION 0x00033005
#define GLFW_CURSOR_NORMAL 0x00034001
#define GLFW_CURSOR_HIDDEN 0x00034002
@@ -1089,6 +1155,14 @@ extern "C" {
#define GLFW_EGL_CONTEXT_API 0x00036002
#define GLFW_OSMESA_CONTEXT_API 0x00036003
#define GLFW_ANGLE_PLATFORM_TYPE_NONE 0x00037001
#define GLFW_ANGLE_PLATFORM_TYPE_OPENGL 0x00037002
#define GLFW_ANGLE_PLATFORM_TYPE_OPENGLES 0x00037003
#define GLFW_ANGLE_PLATFORM_TYPE_D3D9 0x00037004
#define GLFW_ANGLE_PLATFORM_TYPE_D3D11 0x00037005
#define GLFW_ANGLE_PLATFORM_TYPE_VULKAN 0x00037007
#define GLFW_ANGLE_PLATFORM_TYPE_METAL 0x00037008
/*! @defgroup shapes Standard cursor shapes
* @brief Standard system cursor shapes.
*
@@ -1122,8 +1196,12 @@ typedef enum {
* Joystick hat buttons [init hint](@ref GLFW_JOYSTICK_HAT_BUTTONS).
*/
#define GLFW_JOYSTICK_HAT_BUTTONS 0x00050001
#define GLFW_DEBUG_KEYBOARD 0x00050002
#define GLFW_ENABLE_JOYSTICKS 0x00050003
/*! @brief ANGLE rendering backend init hint.
*
* ANGLE rendering backend [init hint](@ref GLFW_ANGLE_PLATFORM_TYPE_hint).
*/
#define GLFW_ANGLE_PLATFORM_TYPE 0x00050002
#define GLFW_DEBUG_KEYBOARD 0x00050003
/*! @brief macOS specific init hint.
*
* macOS specific [init hint](@ref GLFW_COCOA_CHDIR_RESOURCES_hint).
@@ -1319,6 +1397,22 @@ typedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int);
*/
typedef void (* GLFWwindowclosefun)(GLFWwindow*);
/*! @brief The function pointer type for application close callbacks.
*
* This is the function pointer type for application close callbacks. A application
* close callback function has the following signature:
* @code
* void function_name(int flags)
* @endcode
*
* @param[in] flags 0 for a user requested application quit, 1 if a fatal error occurred and application should quit ASAP
*
* @sa @ref glfwSetApplicationCloseCallback
*
* @ingroup window
*/
typedef void (* GLFWapplicationclosefun)(int);
/*! @brief The function pointer type for window content refresh callbacks.
*
* This is the function pointer type for window content refresh callbacks.
@@ -1550,6 +1644,7 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int);
* value 2 for stationary momentum scrolling, value 3 for momentum scrolling
* in progress, value 4 for momentum scrolling ended, value 5 for momentum
* scrolling cancelled and value 6 if scrolling may begin soon.
* @param[int] mods The keyboard modifiers
*
* @sa @ref scrolling
* @sa @ref glfwSetScrollCallback
@@ -1559,7 +1654,7 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int);
*
* @ingroup input
*/
typedef void (* GLFWscrollfun)(GLFWwindow*,double,double,int);
typedef void (* GLFWscrollfun)(GLFWwindow*,double,double,int,int);
/*! @brief The function pointer type for key callbacks.
*
@@ -1835,6 +1930,8 @@ GLFWAPI void glfwRemoveTimer(unsigned long long);
* call this function, as it is called by @ref glfwInit before it returns
* failure.
*
* This function has no effect if GLFW is not initialized.
*
* @errors Possible errors include @ref GLFW_PLATFORM_ERROR.
*
* @remark This function may be called before @ref glfwInit.
@@ -2832,21 +2929,21 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title);
* @param[in] images The images to create the icon from. This is ignored if
* count is zero.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
* GLFW_PLATFORM_ERROR and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).
*
* @pointer_lifetime The specified image data is copied before this function
* returns.
*
* @remark @macos The GLFW window has no icon, as it is not a document
* window, so this function does nothing. The dock icon will be the same as
* @remark @macos Regular windows do not have icons on macOS. This function
* will emit @ref GLFW_FEATURE_UNAVAILABLE. The dock icon will be the same as
* the application bundle's icon. For more information on bundles, see the
* [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/)
* in the Mac Developer Library.
*
* @remark @wayland There is no existing protocol to change an icon, the
* window will thus inherit the one defined in the application's desktop file.
* This function always emits @ref GLFW_PLATFORM_ERROR.
* This function will emit @ref GLFW_FEATURE_UNAVAILABLE.
*
* @thread_safety This function must only be called from the main thread.
*
@@ -2872,12 +2969,12 @@ GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* i
* @param[out] ypos Where to store the y-coordinate of the upper-left corner of
* the content area, or `NULL`.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
* GLFW_PLATFORM_ERROR and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).
*
* @remark @wayland There is no way for an application to retrieve the global
* position of its windows, this function will always emit @ref
* GLFW_PLATFORM_ERROR.
* position of its windows. This function will emit @ref
* GLFW_FEATURE_UNAVAILABLE.
*
* @thread_safety This function must only be called from the main thread.
*
@@ -2906,12 +3003,12 @@ GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos);
* @param[in] xpos The x-coordinate of the upper-left corner of the content area.
* @param[in] ypos The y-coordinate of the upper-left corner of the content area.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
* GLFW_PLATFORM_ERROR and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).
*
* @remark @wayland There is no way for an application to set the global
* position of its windows, this function will always emit @ref
* GLFW_PLATFORM_ERROR.
* position of its windows. This function will emit @ref
* GLFW_FEATURE_UNAVAILABLE.
*
* @thread_safety This function must only be called from the main thread.
*
@@ -3289,8 +3386,11 @@ GLFWAPI float glfwGetWindowOpacity(GLFWwindow* window);
* @param[in] window The window to set the opacity for.
* @param[in] opacity The desired opacity of the specified window.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
* GLFW_PLATFORM_ERROR and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).
*
* @remark @wayland There is no way to set an opacity factor for a window.
* This function will emit @ref GLFW_FEATURE_UNAVAILABLE.
*
* @thread_safety This function must only be called from the main thread.
*
@@ -3457,11 +3557,11 @@ GLFWAPI void glfwHideWindow(GLFWwindow* window);
*
* @param[in] window The window to give input focus.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
* GLFW_PLATFORM_ERROR and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).
*
* @remark @wayland It is not possible for an application to bring its windows
* to front, this function will always emit @ref GLFW_PLATFORM_ERROR.
* @remark @wayland It is not possible for an application to set the input
* focus. This function will emit @ref GLFW_FEATURE_UNAVAILABLE.
*
* @thread_safety This function must only be called from the main thread.
*
@@ -3648,6 +3748,7 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib);
* [GLFW_FLOATING](@ref GLFW_FLOATING_attrib),
* [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib) and
* [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_attrib).
* [GLFW_MOUSE_PASSTHROUGH](@ref GLFW_MOUSE_PASSTHROUGH_attrib)
*
* Some of these attributes are ignored for full screen windows. The new
* value will take effect if the window is later made windowed.
@@ -3826,6 +3927,7 @@ GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwind
* @ingroup window
*/
GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwindowclosefun callback);
GLFWAPI GLFWapplicationclosefun glfwSetApplicationCloseCallback(GLFWapplicationclosefun callback);
/*! @brief Sets the refresh callback for the specified window.
*
@@ -4075,11 +4177,13 @@ GLFWAPI void glfwPostEmptyEvent(void);
*
* This function returns the value of an input option for the specified window.
* The mode must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS,
* @ref GLFW_STICKY_MOUSE_BUTTONS or @ref GLFW_LOCK_KEY_MODS.
* @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or
* @ref GLFW_RAW_MOUSE_MOTION.
*
* @param[in] window The window to query.
* @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`,
* `GLFW_STICKY_MOUSE_BUTTONS` or `GLFW_LOCK_KEY_MODS`.
* `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or
* `GLFW_RAW_MOUSE_MOTION`.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_INVALID_ENUM.
@@ -4098,7 +4202,8 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode);
*
* This function sets an input mode option for the specified window. The mode
* must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS,
* @ref GLFW_STICKY_MOUSE_BUTTONS or @ref GLFW_LOCK_KEY_MODS.
* @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or
* @ref GLFW_RAW_MOUSE_MOTION.
*
* If the mode is `GLFW_CURSOR`, the value must be one of the following cursor
* modes:
@@ -4130,13 +4235,21 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode);
* GLFW_MOD_CAPS_LOCK bit set when the event was generated with Caps Lock on,
* and the @ref GLFW_MOD_NUM_LOCK bit when Num Lock was on.
*
* If the mode is `GLFW_RAW_MOUSE_MOTION`, the value must be either `true`
* to enable raw (unscaled and unaccelerated) mouse motion when the cursor is
* disabled, or `false` to disable it. If raw motion is not supported,
* attempting to set this will emit @ref GLFW_FEATURE_UNAVAILABLE. Call @ref
* glfwRawMouseMotionSupported to check for support.
*
* @param[in] window The window whose input mode to set.
* @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`,
* `GLFW_STICKY_MOUSE_BUTTONS` or `GLFW_LOCK_KEY_MODS`.
* `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or
* `GLFW_RAW_MOUSE_MOTION`.
* @param[in] value The new value of the specified input mode.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
* GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
* GLFW_INVALID_ENUM, @ref GLFW_PLATFORM_ERROR and @ref
* GLFW_FEATURE_UNAVAILABLE (see above).
*
* @thread_safety This function must only be called from the main thread.
*
@@ -4148,6 +4261,35 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode);
*/
GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value);
/*! @brief Returns whether raw mouse motion is supported.
*
* This function returns whether raw mouse motion is supported on the current
* system. This status does not change after GLFW has been initialized so you
* only need to check this once. If you attempt to enable raw motion on
* a system that does not support it, @ref GLFW_PLATFORM_ERROR will be emitted.
*
* Raw mouse motion is closer to the actual motion of the mouse across
* a surface. It is not affected by the scaling and acceleration applied to
* the motion of the desktop cursor. That processing is suitable for a cursor
* while raw motion is better for controlling for example a 3D camera. Because
* of this, raw mouse motion is only provided when the cursor is disabled.
*
* @return `true` if raw mouse motion is supported on the current machine,
* or `false` otherwise.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref raw_mouse_motion
* @sa @ref glfwSetInputMode
*
* @since Added in version 3.3.
*
* @ingroup input
*/
GLFWAPI int glfwRawMouseMotionSupported(void);
/*! @brief Returns the layout-specific name of the specified printable key.
*
* This function returns the name of the specified printable key, encoded as

111
glfw/glx_context.c vendored
View File

@@ -47,99 +47,87 @@ static int getGLXFBConfigAttrib(GLXFBConfig fbconfig, int attrib)
return value;
}
static GLXFBConfig*
choose_fb_config(const _GLFWfbconfig* desired, bool trust_window_bit, int *nelements) {
int attrib_list[64];
int pos = 0;
#define ATTR(x, y) { attrib_list[pos++] = x; attrib_list[pos++] = y; }
ATTR(GLX_DOUBLEBUFFER, desired->doublebuffer ? True : False);
if (desired->stereo > 0) ATTR(GLX_STEREO, desired->stereo ? True : False);
if (desired->auxBuffers > 0) ATTR(GLX_AUX_BUFFERS, desired->auxBuffers);
if (_glfw.glx.ARB_multisample && desired->samples > 0) ATTR(GLX_SAMPLES, desired->samples);
if (desired->depthBits != GLFW_DONT_CARE) ATTR(GLX_DEPTH_SIZE, desired->depthBits);
if (desired->stencilBits != GLFW_DONT_CARE) ATTR(GLX_STENCIL_SIZE, desired->stencilBits);
if (desired->redBits != GLFW_DONT_CARE) ATTR(GLX_RED_SIZE, desired->redBits);
if (desired->greenBits != GLFW_DONT_CARE) ATTR(GLX_GREEN_SIZE, desired->greenBits);
if (desired->blueBits != GLFW_DONT_CARE) ATTR(GLX_BLUE_SIZE, desired->blueBits);
if (desired->alphaBits != GLFW_DONT_CARE) ATTR(GLX_ALPHA_SIZE, desired->alphaBits);
if (desired->accumRedBits != GLFW_DONT_CARE) ATTR(GLX_ACCUM_RED_SIZE, desired->accumRedBits);
if (desired->accumGreenBits != GLFW_DONT_CARE) ATTR(GLX_ACCUM_GREEN_SIZE, desired->accumGreenBits);
if (desired->accumBlueBits != GLFW_DONT_CARE) ATTR(GLX_ACCUM_BLUE_SIZE, desired->accumBlueBits);
if (desired->accumAlphaBits != GLFW_DONT_CARE) ATTR(GLX_ACCUM_ALPHA_SIZE, desired->accumAlphaBits);
if (!trust_window_bit) ATTR(GLX_DRAWABLE_TYPE, 0);
ATTR(None, None);
return glXChooseFBConfig(_glfw.x11.display, _glfw.x11.screen, attrib_list, nelements);
#undef ATTR
}
// Return the GLXFBConfig most closely matching the specified hints
//
static bool chooseGLXFBConfig(const _GLFWfbconfig* desired,
GLXFBConfig* result)
{
GLXFBConfig* nativeConfigs;
_GLFWfbconfig* usableConfigs;
const _GLFWfbconfig* closest;
int i, nativeCount, usableCount;
int i, nativeCount, ans_idx = 0;
const char* vendor;
bool trustWindowBit = true;
static _GLFWfbconfig prev_desired = {0};
static GLXFBConfig prev_result = 0;
if (prev_result != 0 && memcmp(&prev_desired, desired, sizeof(_GLFWfbconfig)) == 0) {
*result = prev_result;
return true;
}
prev_desired = *desired;
// HACK: This is a (hopefully temporary) workaround for Chromium
// (VirtualBox GL) not setting the window bit on any GLXFBConfigs
vendor = glXGetClientString(_glfw.x11.display, GLX_VENDOR);
if (vendor && strcmp(vendor, "Chromium") == 0)
trustWindowBit = false;
nativeConfigs =
glXGetFBConfigs(_glfw.x11.display, _glfw.x11.screen, &nativeCount);
nativeConfigs = choose_fb_config(desired, trustWindowBit, &nativeCount);
if (!nativeConfigs || !nativeCount)
{
_glfwInputError(GLFW_API_UNAVAILABLE, "GLX: No GLXFBConfigs returned");
return false;
}
usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig));
usableCount = 0;
for (i = 0; i < nativeCount; i++)
{
const GLXFBConfig n = nativeConfigs[i];
_GLFWfbconfig* u = usableConfigs + usableCount;
// Only consider RGBA GLXFBConfigs
if (!(getGLXFBConfigAttrib(n, GLX_RENDER_TYPE) & GLX_RGBA_BIT))
continue;
// Only consider window GLXFBConfigs
if (!(getGLXFBConfigAttrib(n, GLX_DRAWABLE_TYPE) & GLX_WINDOW_BIT))
{
if (trustWindowBit)
continue;
}
if (desired->transparent)
{
bool transparency_matches = true, srgb_matches = true;
if (desired->transparent) {
transparency_matches = false;
XVisualInfo* vi = glXGetVisualFromFBConfig(_glfw.x11.display, n);
if (vi)
{
u->transparent = _glfwIsVisualTransparentX11(vi->visual);
XFree(vi);
}
if (vi && _glfwIsVisualTransparentX11(vi->visual)) transparency_matches = true;
}
u->redBits = getGLXFBConfigAttrib(n, GLX_RED_SIZE);
u->greenBits = getGLXFBConfigAttrib(n, GLX_GREEN_SIZE);
u->blueBits = getGLXFBConfigAttrib(n, GLX_BLUE_SIZE);
if (desired->sRGB && (_glfw.glx.ARB_framebuffer_sRGB || _glfw.glx.EXT_framebuffer_sRGB)) {
srgb_matches = getGLXFBConfigAttrib(n, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB) ? true : false;
}
u->alphaBits = getGLXFBConfigAttrib(n, GLX_ALPHA_SIZE);
u->depthBits = getGLXFBConfigAttrib(n, GLX_DEPTH_SIZE);
u->stencilBits = getGLXFBConfigAttrib(n, GLX_STENCIL_SIZE);
if (transparency_matches && srgb_matches) {
ans_idx = i; break;
}
u->accumRedBits = getGLXFBConfigAttrib(n, GLX_ACCUM_RED_SIZE);
u->accumGreenBits = getGLXFBConfigAttrib(n, GLX_ACCUM_GREEN_SIZE);
u->accumBlueBits = getGLXFBConfigAttrib(n, GLX_ACCUM_BLUE_SIZE);
u->accumAlphaBits = getGLXFBConfigAttrib(n, GLX_ACCUM_ALPHA_SIZE);
u->auxBuffers = getGLXFBConfigAttrib(n, GLX_AUX_BUFFERS);
if (getGLXFBConfigAttrib(n, GLX_STEREO))
u->stereo = true;
if (getGLXFBConfigAttrib(n, GLX_DOUBLEBUFFER))
u->doublebuffer = true;
if (_glfw.glx.ARB_multisample)
u->samples = getGLXFBConfigAttrib(n, GLX_SAMPLES);
if (_glfw.glx.ARB_framebuffer_sRGB || _glfw.glx.EXT_framebuffer_sRGB)
u->sRGB = getGLXFBConfigAttrib(n, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB);
u->handle = (uintptr_t) n;
usableCount++;
}
closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount);
if (closest)
*result = (GLXFBConfig) closest->handle;
*result = nativeConfigs[ans_idx];
prev_result = nativeConfigs[ans_idx];
XFree(nativeConfigs);
free(usableConfigs);
return closest != NULL;
return true;
}
// Create the OpenGL context using legacy API
@@ -287,6 +275,7 @@ bool _glfwInitGLX(void)
glfw_dlsym(_glfw.glx.GetFBConfigs, _glfw.glx.handle, "glXGetFBConfigs");
glfw_dlsym(_glfw.glx.GetFBConfigAttrib, _glfw.glx.handle, "glXGetFBConfigAttrib");
glfw_dlsym(_glfw.glx.ChooseFBConfig, _glfw.glx.handle, "glXChooseFBConfig");
glfw_dlsym(_glfw.glx.GetClientString, _glfw.glx.handle, "glXGetClientString");
glfw_dlsym(_glfw.glx.QueryExtension, _glfw.glx.handle, "glXQueryExtension");
glfw_dlsym(_glfw.glx.QueryVersion, _glfw.glx.handle, "glXQueryVersion");

3
glfw/glx_context.h vendored
View File

@@ -73,6 +73,7 @@ typedef struct __GLXcontext* GLXContext;
typedef void (*__GLXextproc)(void);
typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*);
typedef GLXFBConfig* (*PFNGLXCHOOSEFBCONFIGPROC)(Display*,int,const int*,int*);
typedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int);
typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*);
typedef Bool (*PFNGLXQUERYVERSIONPROC)(Display*,int*,int*);
@@ -95,6 +96,7 @@ typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLX
// libGL.so function pointer typedefs
#define glXGetFBConfigs _glfw.glx.GetFBConfigs
#define glXGetFBConfigAttrib _glfw.glx.GetFBConfigAttrib
#define glXChooseFBConfig _glfw.glx.ChooseFBConfig
#define glXGetClientString _glfw.glx.GetClientString
#define glXQueryExtension _glfw.glx.QueryExtension
#define glXQueryVersion _glfw.glx.QueryVersion
@@ -134,6 +136,7 @@ typedef struct _GLFWlibraryGLX
// GLX 1.3 functions
PFNGLXGETFBCONFIGSPROC GetFBConfigs;
PFNGLXGETFBCONFIGATTRIBPROC GetFBConfigAttrib;
PFNGLXCHOOSEFBCONFIGPROC ChooseFBConfig;
PFNGLXGETCLIENTSTRINGPROC GetClientString;
PFNGLXQUERYEXTENSIONPROC QueryExtension;
PFNGLXQUERYVERSIONPROC QueryVersion;

2
glfw/ibus_glfw.c vendored
View File

@@ -269,7 +269,7 @@ setup_connection(_GLFWIBUSData *ibus) {
ibus->conn = NULL;
}
debug("Connecting to IBUS daemon @ %s for IME input management\n", ibus->address);
ibus->conn = glfw_dbus_connect_to(ibus->address, "Failed to connect to the IBUS daemon, with error", "ibus", false);
ibus->conn = glfw_dbus_connect_to(ibus->address, "Failed to connect to the IBUS daemon, with error", "ibus", true);
if (!ibus->conn) return false;
free((void*)ibus->input_ctx_path); ibus->input_ctx_path = NULL;
if (!glfw_dbus_call_method_with_reply(

20
glfw/init.c vendored
View File

@@ -53,8 +53,8 @@ static GLFWerrorfun _glfwErrorCallback;
static _GLFWinitconfig _glfwInitHints =
{
true, // hat buttons
GLFW_ANGLE_PLATFORM_TYPE_NONE, // ANGLE backend
false, // debug keyboard
true, // enable joystick
{
true, // macOS menu bar
true // macOS bundle chdir
@@ -92,6 +92,7 @@ static void terminate(void)
_glfw.mappingCount = 0;
_glfwTerminateVulkan();
_glfwPlatformTerminateJoysticks();
_glfwPlatformTerminate();
_glfw.initialized = false;
@@ -168,6 +169,10 @@ void _glfwInputError(int code, const char* format, ...)
strcpy(description, "The requested format is unavailable");
else if (code == GLFW_NO_WINDOW_CONTEXT)
strcpy(description, "The specified window has no context");
else if (code == GLFW_FEATURE_UNAVAILABLE)
strcpy(description, "The requested feature cannot be implemented for this platform");
else if (code == GLFW_FEATURE_UNIMPLEMENTED)
strcpy(description, "The requested feature has not yet been implemented for this platform");
else
strcpy(description, "ERROR: UNKNOWN GLFW ERROR");
}
@@ -271,12 +276,12 @@ GLFWAPI void glfwInitHint(int hint, int value)
{
switch (hint)
{
case GLFW_ENABLE_JOYSTICKS:
_glfwInitHints.enableJoysticks = value;
return;
case GLFW_JOYSTICK_HAT_BUTTONS:
_glfwInitHints.hatButtons = value;
return;
case GLFW_ANGLE_PLATFORM_TYPE:
_glfwInitHints.angleType = value;
return;
case GLFW_DEBUG_KEYBOARD:
_glfwInitHints.debugKeyboard = value;
return;
@@ -363,3 +368,10 @@ GLFWAPI void glfwUpdateTimer(unsigned long long timer_id, monotonic_t interval,
GLFWAPI void glfwRemoveTimer(unsigned long long timer_id) {
_glfwPlatformRemoveTimer(timer_id);
}
GLFWAPI GLFWapplicationclosefun glfwSetApplicationCloseCallback(GLFWapplicationclosefun cbfun)
{
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
_GLFW_SWAP_POINTERS(_glfw.callbacks.application_close, cbfun);
return cbfun;
}

89
glfw/input.c vendored
View File

@@ -44,6 +44,22 @@
#define _GLFW_JOYSTICK_BUTTON 2
#define _GLFW_JOYSTICK_HATBIT 3
// Initializes the platform joystick API if it has not been already
//
static bool initJoysticks(void)
{
if (!_glfw.joysticksInitialized)
{
if (!_glfwPlatformInitJoysticks())
{
_glfwPlatformTerminateJoysticks();
return false;
}
}
return _glfw.joysticksInitialized = true;
}
// Finds a mapping based on joystick GUID
//
static _GLFWmapping* findMapping(const char* guid)
@@ -300,10 +316,10 @@ void _glfwInputKeyboard(_GLFWwindow* window, GLFWkeyevent* ev)
// Notifies shared code of a scroll event
//
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset, int flags)
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset, int flags, int mods)
{
if (window->callbacks.scroll)
window->callbacks.scroll((GLFWwindow*) window, xoffset, yoffset, flags);
window->callbacks.scroll((GLFWwindow*) window, xoffset, yoffset, flags, mods);
}
// Notifies shared code of a mouse button click event
@@ -675,6 +691,8 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode)
return window->stickyMouseButtons;
case GLFW_LOCK_KEY_MODS:
return window->lockKeyMods;
case GLFW_RAW_MOUSE_MOTION:
return window->rawMouseMotion;
}
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode);
@@ -754,10 +772,32 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
{
window->lockKeyMods = value ? true : false;
}
else if (mode == GLFW_RAW_MOUSE_MOTION)
{
if (!_glfwPlatformRawMouseMotionSupported())
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Raw mouse motion is not supported on this system");
return;
}
value = value ? true : false;
if (window->rawMouseMotion == value)
return;
window->rawMouseMotion = value;
_glfwPlatformSetRawMouseMotion(window, value);
}
else
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode);
}
GLFWAPI int glfwRawMouseMotionSupported(void)
{
_GLFW_REQUIRE_INIT_OR_RETURN(false);
return _glfwPlatformRawMouseMotionSupported();
}
GLFWAPI const char* glfwGetKeyName(int key, int native_key)
{
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
@@ -1077,6 +1117,9 @@ GLFWAPI int glfwJoystickPresent(int jid)
return false;
}
if (!initJoysticks())
return false;
js = _glfw.joysticks + jid;
if (!js->present)
return false;
@@ -1102,6 +1145,9 @@ GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count)
return NULL;
}
if (!initJoysticks())
return NULL;
js = _glfw.joysticks + jid;
if (!js->present)
return NULL;
@@ -1131,6 +1177,9 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count)
return NULL;
}
if (!initJoysticks())
return NULL;
js = _glfw.joysticks + jid;
if (!js->present)
return NULL;
@@ -1164,6 +1213,9 @@ GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count)
return NULL;
}
if (!initJoysticks())
return NULL;
js = _glfw.joysticks + jid;
if (!js->present)
return NULL;
@@ -1190,6 +1242,9 @@ GLFWAPI const char* glfwGetJoystickName(int jid)
return NULL;
}
if (!initJoysticks())
return NULL;
js = _glfw.joysticks + jid;
if (!js->present)
return NULL;
@@ -1215,6 +1270,9 @@ GLFWAPI const char* glfwGetJoystickGUID(int jid)
return NULL;
}
if (!initJoysticks())
return NULL;
js = _glfw.joysticks + jid;
if (!js->present)
return NULL;
@@ -1260,6 +1318,10 @@ GLFWAPI void* glfwGetJoystickUserPointer(int jid)
GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun)
{
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
if (!initJoysticks())
return NULL;
_GLFW_SWAP_POINTERS(_glfw.callbacks.joystick, cbfun);
return cbfun;
}
@@ -1339,6 +1401,9 @@ GLFWAPI int glfwJoystickIsGamepad(int jid)
return false;
}
if (!initJoysticks())
return false;
js = _glfw.joysticks + jid;
if (!js->present)
return false;
@@ -1364,6 +1429,9 @@ GLFWAPI const char* glfwGetGamepadName(int jid)
return NULL;
}
if (!initJoysticks())
return NULL;
js = _glfw.joysticks + jid;
if (!js->present)
return NULL;
@@ -1396,6 +1464,9 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state)
return false;
}
if (!initJoysticks())
return false;
js = _glfw.joysticks + jid;
if (!js->present)
return false;
@@ -1412,8 +1483,18 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state)
if (e->type == _GLFW_JOYSTICK_AXIS)
{
const float value = js->axes[e->index] * e->axisScale + e->axisOffset;
if (value > 0.f)
state->buttons[i] = GLFW_PRESS;
// HACK: This should be baked into the value transform
// TODO: Bake into transform when implementing output modifiers
if (e->axisOffset < 0 || (e->axisOffset == 0 && e->axisScale > 0))
{
if (value >= 0.f)
state->buttons[i] = GLFW_PRESS;
}
else
{
if (value <= 0.f)
state->buttons[i] = GLFW_PRESS;
}
}
else if (e->type == _GLFW_JOYSTICK_HATBIT)
{

33
glfw/internal.h vendored
View File

@@ -198,6 +198,9 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void);
#error "No supported window creation API selected"
#endif
#include "egl_context.h"
#include "osmesa_context.h"
#define remove_i_from_array(array, i, count) { \
(count)--; \
if ((i) < (count)) { \
@@ -271,8 +274,8 @@ struct _GLFWerror
struct _GLFWinitconfig
{
bool hatButtons;
int angleType;
bool debugKeyboard;
bool enableJoysticks;
struct {
bool menubar;
bool chdir;
@@ -299,6 +302,7 @@ struct _GLFWwndconfig
bool maximized;
bool centerCursor;
bool focusOnShow;
bool mousePassthrough;
bool scaleToMonitor;
struct {
bool retina;
@@ -392,9 +396,9 @@ struct _GLFWcontext
// This is defined in the context API's context.h
_GLFW_PLATFORM_CONTEXT_STATE
// This is defined in egl_context.h
_GLFW_EGL_CONTEXT_STATE;
_GLFWcontextEGL egl;
// This is defined in osmesa_context.h
_GLFW_OSMESA_CONTEXT_STATE;
_GLFWcontextOSMesa osmesa;
};
// Window and context structure
@@ -409,6 +413,7 @@ struct _GLFWwindow
bool autoIconify;
bool floating;
bool focusOnShow;
bool mousePassthrough;
bool shouldClose;
void* userPointer;
GLFWid id;
@@ -429,6 +434,7 @@ struct _GLFWwindow
char keys[GLFW_KEY_LAST + 1];
// Virtual cursor position when cursor is disabled
double virtualCursorPosX, virtualCursorPosY;
bool rawMouseMotion;
_GLFWcontext context;
@@ -568,6 +574,7 @@ struct _GLFWlibrary
_GLFWmonitor** monitors;
int monitorCount;
bool joysticksInitialized;
_GLFWjoystick joysticks[GLFW_JOYSTICK_LAST + 1];
_GLFWmapping* mappings;
int mappingCount;
@@ -601,8 +608,10 @@ struct _GLFWlibrary
struct {
GLFWmonitorfun monitor;
GLFWjoystickfun joystick;
GLFWapplicationclosefun application_close;
} callbacks;
// This is defined in the window API's platform.h
_GLFW_PLATFORM_LIBRARY_WINDOW_STATE;
// This is defined in the context API's context.h
@@ -610,9 +619,9 @@ struct _GLFWlibrary
// This is defined in the platform's joystick.h
_GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE
// This is defined in egl_context.h
_GLFW_EGL_LIBRARY_CONTEXT_STATE;
_GLFWlibraryEGL egl;
// This is defined in osmesa_context.h
_GLFW_OSMESA_LIBRARY_CONTEXT_STATE;
_GLFWlibraryOSMesa osmesa;
};
// Global state shared between compilation units of GLFW
@@ -631,6 +640,8 @@ const char* _glfwPlatformGetVersionString(void);
void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos);
void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos);
void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode);
void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, bool enabled);
bool _glfwPlatformRawMouseMotionSupported(void);
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
const GLFWimage* image, int xhot, int yhot, int count);
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, GLFWCursorShape shape);
@@ -657,6 +668,8 @@ void _glfwPlatformSetPrimarySelectionString(const char* string);
const char* _glfwPlatformGetPrimarySelectionString(void);
#endif
bool _glfwPlatformInitJoysticks(void);
void _glfwPlatformTerminateJoysticks(void);
int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode);
void _glfwPlatformUpdateGamepadGUID(char* guid);
@@ -708,6 +721,7 @@ float _glfwPlatformGetWindowOpacity(_GLFWwindow* window);
void _glfwPlatformSetWindowResizable(_GLFWwindow* window, bool enabled);
void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, bool enabled);
void _glfwPlatformSetWindowFloating(_GLFWwindow* window, bool enabled);
void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, bool enabled);
void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity);
void _glfwPlatformUpdateIMEState(_GLFWwindow *w, int which, int a, int b, int c, int d);
@@ -716,6 +730,10 @@ void _glfwPlatformWaitEvents(void);
void _glfwPlatformWaitEventsTimeout(monotonic_t timeout);
void _glfwPlatformPostEmptyEvent(void);
EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs);
EGLNativeDisplayType _glfwPlatformGetEGLNativeDisplay(void);
EGLNativeWindowType _glfwPlatformGetEGLNativeWindow(_GLFWwindow* window);
void _glfwPlatformGetRequiredInstanceExtensions(char** extensions);
int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
VkPhysicalDevice device,
@@ -755,7 +773,7 @@ void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor);
void _glfwInitializeKeyEvent(GLFWkeyevent *ev, int key, int native_key, int action, int mods);
void _glfwInputKeyboard(_GLFWwindow *window, GLFWkeyevent *ev);
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset, int flags);
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset, int flags, int mods);
void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods);
void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos);
void _glfwInputCursorEnter(_GLFWwindow* window, bool entered);
@@ -789,9 +807,6 @@ void _glfwDebug(const char* format, ...);
//////////////////////////////////////////////////////////////////////////
bool _glfwStringInExtensionString(const char* string, const char* extensions);
const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired,
const _GLFWfbconfig* alternatives,
unsigned int count);
bool _glfwRefreshContextAttribs(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig);
bool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig);

98
glfw/linux_joystick.c vendored
View File

@@ -264,9 +264,50 @@ static int compareJoysticks(const void* fp, const void* sp)
////// GLFW internal API //////
//////////////////////////////////////////////////////////////////////////
// Initialize joystick interface
//
bool _glfwInitJoysticksLinux(void)
void _glfwDetectJoystickConnectionLinux(void)
{
if (_glfw.linjs.inotify <= 0)
return;
ssize_t offset = 0;
char buffer[16384];
const ssize_t size = read(_glfw.linjs.inotify, buffer, sizeof(buffer));
while (size > offset)
{
regmatch_t match;
const struct inotify_event* e = (struct inotify_event*) (buffer + offset);
offset += sizeof(struct inotify_event) + e->len;
if (regexec(&_glfw.linjs.regex, e->name, 1, &match, 0) != 0)
continue;
char path[PATH_MAX];
snprintf(path, sizeof(path), "/dev/input/%s", e->name);
if (e->mask & (IN_CREATE | IN_ATTRIB))
openJoystickDevice(path);
else if (e->mask & IN_DELETE)
{
for (int jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{
if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0)
{
closeJoystick(_glfw.joysticks + jid);
break;
}
}
}
}
}
//////////////////////////////////////////////////////////////////////////
////// GLFW platform API //////
//////////////////////////////////////////////////////////////////////////
bool _glfwPlatformInitJoysticks(void)
{
const char* dirname = "/dev/input";
@@ -320,9 +361,7 @@ bool _glfwInitJoysticksLinux(void)
return true;
}
// Close all opened joystick handles
//
void _glfwTerminateJoysticksLinux(void)
void _glfwPlatformTerminateJoysticks(void)
{
int jid;
@@ -333,61 +372,16 @@ void _glfwTerminateJoysticksLinux(void)
closeJoystick(js);
}
regfree(&_glfw.linjs.regex);
if (_glfw.linjs.inotify > 0)
{
if (_glfw.linjs.watch > 0)
inotify_rm_watch(_glfw.linjs.inotify, _glfw.linjs.watch);
close(_glfw.linjs.inotify);
regfree(&_glfw.linjs.regex);
}
}
void _glfwDetectJoystickConnectionLinux(void)
{
if (_glfw.linjs.inotify <= 0)
return;
ssize_t offset = 0;
char buffer[16384];
const ssize_t size = read(_glfw.linjs.inotify, buffer, sizeof(buffer));
while (size > offset)
{
regmatch_t match;
const struct inotify_event* e = (struct inotify_event*) (buffer + offset);
offset += sizeof(struct inotify_event) + e->len;
if (regexec(&_glfw.linjs.regex, e->name, 1, &match, 0) != 0)
continue;
char path[PATH_MAX];
snprintf(path, sizeof(path), "/dev/input/%s", e->name);
if (e->mask & (IN_CREATE | IN_ATTRIB))
openJoystickDevice(path);
else if (e->mask & IN_DELETE)
{
for (int jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{
if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0)
{
closeJoystick(_glfw.joysticks + jid);
break;
}
}
}
}
}
//////////////////////////////////////////////////////////////////////////
////// GLFW platform API //////
//////////////////////////////////////////////////////////////////////////
int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode UNUSED)
{
// Read all queued events (non-blocking)

View File

@@ -55,7 +55,4 @@ typedef struct _GLFWlibraryLinux
bool dropped;
} _GLFWlibraryLinux;
bool _glfwInitJoysticksLinux(void);
void _glfwTerminateJoysticksLinux(void);
void _glfwDetectJoystickConnectionLinux(void);

5
glfw/null_init.c vendored
View File

@@ -29,6 +29,8 @@
#include "internal.h"
#include <stdlib.h>
//////////////////////////////////////////////////////////////////////////
////// GLFW platform API //////
@@ -36,11 +38,14 @@
int _glfwPlatformInit(void)
{
_glfwPollMonitorsNull();
return true;
}
void _glfwPlatformTerminate(void)
{
free(_glfw.null.clipboardString);
_glfwTerminateOSMesa();
}

View File

@@ -33,6 +33,15 @@
////// GLFW platform API //////
//////////////////////////////////////////////////////////////////////////
bool _glfwPlatformInitJoysticks(void)
{
return true;
}
void _glfwPlatformTerminateJoysticks(void)
{
}
int _glfwPlatformPollJoystick(_GLFWjoystick* js UNUSED, int mode UNUSED)
{
return false;

96
glfw/null_monitor.c vendored
View File

@@ -29,13 +29,45 @@
#include "internal.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
// The the sole (fake) video mode of our (sole) fake monitor
//
static GLFWvidmode getVideoMode(void)
{
GLFWvidmode mode;
mode.width = 1920;
mode.height = 1080;
mode.redBits = 8;
mode.greenBits = 8;
mode.blueBits = 8;
mode.refreshRate = 60;
return mode;
}
//////////////////////////////////////////////////////////////////////////
////// GLFW internal API //////
//////////////////////////////////////////////////////////////////////////
void _glfwPollMonitorsNull(void)
{
const float dpi = 141.f;
const GLFWvidmode mode = getVideoMode();
_GLFWmonitor* monitor = _glfwAllocMonitor("Null SuperNoop 0",
(int) (mode.width * 25.4f / dpi),
(int) (mode.height * 25.4f / dpi));
_glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_FIRST);
}
//////////////////////////////////////////////////////////////////////////
////// GLFW platform API //////
//////////////////////////////////////////////////////////////////////////
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor UNUSED)
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
{
_glfwFreeGammaArrays(&monitor->null.ramp);
}
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor UNUSED, int* xpos UNUSED, int* ypos UNUSED)
@@ -52,26 +84,72 @@ void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor UNUSED,
}
void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor UNUSED,
int* xpos UNUSED, int* ypos UNUSED,
int* width UNUSED, int* height UNUSED)
int* xpos, int* ypos,
int* width, int* height)
{
const GLFWvidmode mode = getVideoMode();
if (xpos)
*xpos = 0;
if (ypos)
*ypos = 10;
if (width)
*width = mode.width;
if (height)
*height = mode.height - 10;
}
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor UNUSED, int* found UNUSED)
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor UNUSED, int* found)
{
return NULL;
GLFWvidmode* mode = calloc(1, sizeof(GLFWvidmode));
*mode = getVideoMode();
*found = 1;
return mode;
}
void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor UNUSED, GLFWvidmode* mode UNUSED)
void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor UNUSED, GLFWvidmode* mode)
{
*mode = getVideoMode();
}
bool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor UNUSED, GLFWgammaramp* ramp UNUSED)
bool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
{
return false;
if (!monitor->null.ramp.size)
{
_glfwAllocGammaArrays(&monitor->null.ramp, 256);
for (unsigned int i = 0; i < monitor->null.ramp.size; i++)
{
const float gamma = 2.2f;
float value;
value = i / (float) (monitor->null.ramp.size - 1);
value = powf(value, 1.f / gamma) * 65535.f + 0.5f;
value = _glfw_fminf(value, 65535.f);
monitor->null.ramp.red[i] = (unsigned short) value;
monitor->null.ramp.green[i] = (unsigned short) value;
monitor->null.ramp.blue[i] = (unsigned short) value;
}
}
_glfwAllocGammaArrays(ramp, monitor->null.ramp.size);
memcpy(ramp->red, monitor->null.ramp.red, sizeof(short) * ramp->size);
memcpy(ramp->green, monitor->null.ramp.green, sizeof(short) * ramp->size);
memcpy(ramp->blue, monitor->null.ramp.blue, sizeof(short) * ramp->size);
return true;
}
void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor UNUSED, const GLFWgammaramp* ramp UNUSED)
void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
{
if (monitor->null.ramp.size != ramp->size)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Null: Gamma ramp size must match current ramp size");
return;
}
memcpy(monitor->null.ramp.red, ramp->red, sizeof(short) * ramp->size);
memcpy(monitor->null.ramp.green, ramp->green, sizeof(short) * ramp->size);
memcpy(monitor->null.ramp.blue, ramp->blue, sizeof(short) * ramp->size);
}

42
glfw/null_platform.h vendored
View File

@@ -27,17 +27,14 @@
#include <dlfcn.h>
#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNull null
#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNull null
#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryNull null
#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorNull null
#define _GLFW_PLATFORM_CONTEXT_STATE
#define _GLFW_PLATFORM_MONITOR_STATE
#define _GLFW_PLATFORM_CURSOR_STATE
#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE
#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE
#define _GLFW_EGL_CONTEXT_STATE
#define _GLFW_EGL_LIBRARY_CONTEXT_STATE
#include "osmesa_context.h"
#include "posix_time.h"
#include "posix_thread.h"
#include "null_joystick.h"
@@ -56,7 +53,36 @@
//
typedef struct _GLFWwindowNull
{
int width;
int height;
int xpos;
int ypos;
int width;
int height;
char* title;
bool visible;
bool iconified;
bool maximized;
bool resizable;
bool decorated;
bool floating;
bool transparent;
float opacity;
} _GLFWwindowNull;
// Null-specific per-monitor data
//
typedef struct _GLFWmonitorNull
{
GLFWgammaramp ramp;
} _GLFWmonitorNull;
// Null-specific global data
//
typedef struct _GLFWlibraryNull
{
int xcursor;
int ycursor;
char* clipboardString;
_GLFWwindow* focusedWindow;
} _GLFWlibraryNull;
void _glfwPollMonitorsNull(void);

494
glfw/null_window.c vendored
View File

@@ -30,12 +30,71 @@
#include "internal.h"
#include "../kitty/monotonic.h"
#include <stdlib.h>
static void applySizeLimits(_GLFWwindow* window, int* width, int* height)
{
if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE)
{
const float ratio = (float) window->numer / (float) window->denom;
*height = (int) (*width / ratio);
}
if (window->minwidth != GLFW_DONT_CARE && *width < window->minwidth)
*width = window->minwidth;
else if (window->maxwidth != GLFW_DONT_CARE && *width > window->maxwidth)
*width = window->maxwidth;
if (window->minheight != GLFW_DONT_CARE && *height < window->minheight)
*height = window->minheight;
else if (window->maxheight != GLFW_DONT_CARE && *height > window->maxheight)
*height = window->maxheight;
}
static void fitToMonitor(_GLFWwindow* window)
{
GLFWvidmode mode;
_glfwPlatformGetVideoMode(window->monitor, &mode);
_glfwPlatformGetMonitorPos(window->monitor,
&window->null.xpos,
&window->null.ypos);
window->null.width = mode.width;
window->null.height = mode.height;
}
static void acquireMonitor(_GLFWwindow* window)
{
_glfwInputMonitorWindow(window->monitor, window);
}
static void releaseMonitor(_GLFWwindow* window)
{
if (window->monitor->window != window)
return;
_glfwInputMonitorWindow(window->monitor, NULL);
}
static int createNativeWindow(_GLFWwindow* window,
const _GLFWwndconfig* wndconfig)
const _GLFWwndconfig* wndconfig,
const _GLFWfbconfig* fbconfig)
{
window->null.width = wndconfig->width;
window->null.height = wndconfig->height;
if (window->monitor)
fitToMonitor(window);
else
{
window->null.xpos = 17;
window->null.ypos = 17;
window->null.width = wndconfig->width;
window->null.height = wndconfig->height;
}
window->null.visible = wndconfig->visible;
window->null.decorated = wndconfig->decorated;
window->null.maximized = wndconfig->maximized;
window->null.floating = wndconfig->floating;
window->null.transparent = fbconfig->transparent;
window->null.opacity = 1.f;
return true;
}
@@ -50,7 +109,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig)
{
if (!createNativeWindow(window, wndconfig))
if (!createNativeWindow(window, wndconfig, fbconfig))
return false;
if (ctxconfig->client != GLFW_NO_API)
@@ -70,11 +129,24 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
}
}
if (window->monitor)
{
_glfwPlatformShowWindow(window);
_glfwPlatformFocusWindow(window);
acquireMonitor(window);
}
return true;
}
void _glfwPlatformDestroyWindow(_GLFWwindow* window)
{
if (window->monitor)
releaseMonitor(window);
if (_glfw.null.focusedWindow == window)
_glfw.null.focusedWindow = NULL;
if (window->context.destroy)
window->context.destroy(window);
}
@@ -88,20 +160,60 @@ void _glfwPlatformSetWindowIcon(_GLFWwindow* window UNUSED, int count UNUSED,
{
}
void _glfwPlatformSetWindowMonitor(_GLFWwindow* window UNUSED,
_GLFWmonitor* monitor UNUSED,
int xpos UNUSED, int ypos UNUSED,
int width UNUSED, int height UNUSED,
void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
_GLFWmonitor* monitor,
int xpos, int ypos,
int width, int height,
int refreshRate UNUSED)
{
if (window->monitor == monitor)
{
if (!monitor)
{
_glfwPlatformSetWindowPos(window, xpos, ypos);
_glfwPlatformSetWindowSize(window, width, height);
}
return;
}
if (window->monitor)
releaseMonitor(window);
_glfwInputWindowMonitor(window, monitor);
if (window->monitor)
{
window->null.visible = true;
acquireMonitor(window);
fitToMonitor(window);
}
else
{
_glfwPlatformSetWindowPos(window, xpos, ypos);
_glfwPlatformSetWindowSize(window, width, height);
}
}
void _glfwPlatformGetWindowPos(_GLFWwindow* window UNUSED, int* xpos UNUSED, int* ypos UNUSED)
void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
{
if (xpos)
*xpos = window->null.xpos;
if (ypos)
*ypos = window->null.ypos;
}
void _glfwPlatformSetWindowPos(_GLFWwindow* window UNUSED, int xpos UNUSED, int ypos UNUSED)
void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)
{
if (window->monitor)
return;
if (window->null.xpos != xpos || window->null.ypos != ypos)
{
window->null.xpos = xpos;
window->null.ypos = ypos;
_glfwInputWindowPos(window, xpos, ypos);
}
}
void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
@@ -114,18 +226,34 @@ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
{
window->null.width = width;
window->null.height = height;
if (window->monitor)
return;
if (window->null.width != width || window->null.height != height)
{
window->null.width = width;
window->null.height = height;
_glfwInputWindowSize(window, width, height);
_glfwInputFramebufferSize(window, width, height);
}
}
void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window UNUSED,
void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
int minwidth UNUSED, int minheight UNUSED,
int maxwidth UNUSED, int maxheight UNUSED)
{
int width = window->null.width;
int height = window->null.height;
applySizeLimits(window, &width, &height);
_glfwPlatformSetWindowSize(window, width, height);
}
void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window UNUSED, int n UNUSED, int d UNUSED)
void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int n UNUSED, int d UNUSED)
{
int width = window->null.width;
int height = window->null.height;
applySizeLimits(window, &width, &height);
_glfwPlatformSetWindowSize(window, width, height);
}
void _glfwPlatformSetWindowSizeIncrements(_GLFWwindow* window UNUSED, int widthincr UNUSED, int heightincr UNUSED)
@@ -140,10 +268,17 @@ void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* heigh
*height = window->null.height;
}
void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window UNUSED,
int* left UNUSED, int* top UNUSED,
int* right UNUSED, int* bottom UNUSED)
void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
int* left, int* top,
int* right, int* bottom)
{
if (window->null.decorated && !window->monitor)
{
*left = 1;
*top = 10;
*right = 1;
*bottom = 1;
}
}
void _glfwPlatformGetWindowContentScale(_GLFWwindow* window UNUSED,
@@ -160,59 +295,111 @@ monotonic_t _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window UNUSED)
return ms_to_monotonic_t(500ll);
}
void _glfwPlatformIconifyWindow(_GLFWwindow* window UNUSED)
void _glfwPlatformIconifyWindow(_GLFWwindow* window)
{
if (_glfw.null.focusedWindow == window)
{
_glfw.null.focusedWindow = NULL;
_glfwInputWindowFocus(window, false);
}
if (!window->null.iconified)
{
window->null.iconified = true;
_glfwInputWindowIconify(window, true);
if (window->monitor)
releaseMonitor(window);
}
}
void _glfwPlatformRestoreWindow(_GLFWwindow* window)
{
if (window->null.iconified)
{
window->null.iconified = false;
_glfwInputWindowIconify(window, false);
if (window->monitor)
acquireMonitor(window);
}
else if (window->null.maximized)
{
window->null.maximized = false;
_glfwInputWindowMaximize(window, false);
}
}
void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
{
if (!window->null.maximized)
{
window->null.maximized = true;
_glfwInputWindowMaximize(window, true);
}
}
int _glfwPlatformWindowMaximized(_GLFWwindow* window)
{
return window->null.maximized;
}
int _glfwPlatformWindowHovered(_GLFWwindow* window)
{
return _glfw.null.xcursor >= window->null.xpos &&
_glfw.null.ycursor >= window->null.ypos &&
_glfw.null.xcursor <= window->null.xpos + window->null.width - 1 &&
_glfw.null.ycursor <= window->null.ypos + window->null.height - 1;
}
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
{
return window->null.transparent;
}
void _glfwPlatformSetWindowResizable(_GLFWwindow* window, bool enabled)
{
window->null.resizable = enabled;
}
void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, bool enabled)
{
window->null.decorated = enabled;
}
void _glfwPlatformSetWindowFloating(_GLFWwindow* window, bool enabled)
{
window->null.floating = enabled;
}
void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window UNUSED, bool enabled UNUSED)
{
}
void _glfwPlatformRestoreWindow(_GLFWwindow* window UNUSED)
float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
{
return window->null.opacity;
}
void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
{
window->null.opacity = opacity;
}
void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window UNUSED, bool enabled UNUSED)
{
}
void _glfwPlatformMaximizeWindow(_GLFWwindow* window UNUSED)
bool _glfwPlatformRawMouseMotionSupported(void)
{
return true;
}
int _glfwPlatformWindowMaximized(_GLFWwindow* window UNUSED)
void _glfwPlatformShowWindow(_GLFWwindow* window)
{
return false;
window->null.visible = true;
}
int _glfwPlatformWindowHovered(_GLFWwindow* window UNUSED)
{
return false;
}
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window UNUSED)
{
return false;
}
void _glfwPlatformSetWindowResizable(_GLFWwindow* window UNUSED, bool enabled UNUSED)
{
}
void _glfwPlatformSetWindowDecorated(_GLFWwindow* window UNUSED, bool enabled UNUSED)
{
}
void _glfwPlatformSetWindowFloating(_GLFWwindow* window UNUSED, bool enabled UNUSED)
{
}
float _glfwPlatformGetWindowOpacity(_GLFWwindow* window UNUSED)
{
return 1.f;
}
void _glfwPlatformSetWindowOpacity(_GLFWwindow* window UNUSED, float opacity UNUSED)
{
}
void _glfwPlatformShowWindow(_GLFWwindow* window UNUSED)
{
}
void _glfwPlatformRequestWindowAttention(_GLFWwindow* window UNUSED)
{
}
@@ -222,21 +409,41 @@ int _glfwPlatformWindowBell(_GLFWwindow* window UNUSED)
return false;
}
void _glfwPlatformUnhideWindow(_GLFWwindow* window UNUSED)
void _glfwPlatformHideWindow(_GLFWwindow* window)
{
if (_glfw.null.focusedWindow == window)
{
_glfw.null.focusedWindow = NULL;
_glfwInputWindowFocus(window, false);
}
window->null.visible = false;
}
void _glfwPlatformHideWindow(_GLFWwindow* window UNUSED)
void _glfwPlatformFocusWindow(_GLFWwindow* window)
{
if (_glfw.null.focusedWindow == window)
return;
if (!window->null.visible)
return;
_GLFWwindow* previous = _glfw.null.focusedWindow;
_glfw.null.focusedWindow = window;
if (previous)
{
_glfwInputWindowFocus(previous, false);
if (previous->monitor && previous->autoIconify)
_glfwPlatformIconifyWindow(previous);
}
_glfwInputWindowFocus(window, true);
}
void _glfwPlatformFocusWindow(_GLFWwindow* window UNUSED)
int _glfwPlatformWindowFocused(_GLFWwindow* window)
{
}
int _glfwPlatformWindowFocused(_GLFWwindow* window UNUSED)
{
return false;
return _glfw.null.focusedWindow == window;
}
int _glfwPlatformWindowOccluded(_GLFWwindow* window UNUSED)
@@ -244,14 +451,14 @@ int _glfwPlatformWindowOccluded(_GLFWwindow* window UNUSED)
return false;
}
int _glfwPlatformWindowIconified(_GLFWwindow* window UNUSED)
int _glfwPlatformWindowIconified(_GLFWwindow* window)
{
return false;
return window->null.iconified;
}
int _glfwPlatformWindowVisible(_GLFWwindow* window UNUSED)
int _glfwPlatformWindowVisible(_GLFWwindow* window)
{
return false;
return window->null.visible;
}
void _glfwPlatformPollEvents(void)
@@ -270,12 +477,18 @@ void _glfwPlatformPostEmptyEvent(void)
{
}
void _glfwPlatformGetCursorPos(_GLFWwindow* window UNUSED, double* xpos UNUSED, double* ypos UNUSED)
void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
{
if (xpos)
*xpos = _glfw.null.xcursor - window->null.xpos;
if (ypos)
*ypos = _glfw.null.ycursor - window->null.ypos;
}
void _glfwPlatformSetCursorPos(_GLFWwindow* window UNUSED, double x UNUSED, double y UNUSED)
void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
{
_glfw.null.xcursor = window->null.xpos + (int) x;
_glfw.null.ycursor = window->null.ypos + (int) y;
}
void _glfwPlatformSetCursorMode(_GLFWwindow* window UNUSED, int mode UNUSED)
@@ -302,23 +515,142 @@ void _glfwPlatformSetCursor(_GLFWwindow* window UNUSED, _GLFWcursor* cursor UNUS
{
}
void _glfwPlatformSetClipboardString(const char* string UNUSED)
void _glfwPlatformSetClipboardString(const char* string)
{
char* copy = _glfw_strdup(string);
free(_glfw.null.clipboardString);
_glfw.null.clipboardString = copy;
}
const char* _glfwPlatformGetClipboardString(void)
{
return _glfw.null.clipboardString;
}
const char* _glfwPlatformGetNativeKeyName(int native_key)
{
switch (scancode)
{
case GLFW_KEY_APOSTROPHE:
return "'";
case GLFW_KEY_COMMA:
return ",";
case GLFW_KEY_MINUS:
case GLFW_KEY_KP_SUBTRACT:
return "-";
case GLFW_KEY_PERIOD:
case GLFW_KEY_KP_DECIMAL:
return ".";
case GLFW_KEY_SLASH:
case GLFW_KEY_KP_DIVIDE:
return "/";
case GLFW_KEY_SEMICOLON:
return ";";
case GLFW_KEY_EQUAL:
case GLFW_KEY_KP_EQUAL:
return "=";
case GLFW_KEY_LEFT_BRACKET:
return "[";
case GLFW_KEY_RIGHT_BRACKET:
return "]";
case GLFW_KEY_KP_MULTIPLY:
return "*";
case GLFW_KEY_KP_ADD:
return "+";
case GLFW_KEY_BACKSLASH:
case GLFW_KEY_WORLD_1:
case GLFW_KEY_WORLD_2:
return "\\";
case GLFW_KEY_0:
case GLFW_KEY_KP_0:
return "0";
case GLFW_KEY_1:
case GLFW_KEY_KP_1:
return "1";
case GLFW_KEY_2:
case GLFW_KEY_KP_2:
return "2";
case GLFW_KEY_3:
case GLFW_KEY_KP_3:
return "3";
case GLFW_KEY_4:
case GLFW_KEY_KP_4:
return "4";
case GLFW_KEY_5:
case GLFW_KEY_KP_5:
return "5";
case GLFW_KEY_6:
case GLFW_KEY_KP_6:
return "6";
case GLFW_KEY_7:
case GLFW_KEY_KP_7:
return "7";
case GLFW_KEY_8:
case GLFW_KEY_KP_8:
return "8";
case GLFW_KEY_9:
case GLFW_KEY_KP_9:
return "9";
case GLFW_KEY_A:
return "a";
case GLFW_KEY_B:
return "b";
case GLFW_KEY_C:
return "c";
case GLFW_KEY_D:
return "d";
case GLFW_KEY_E:
return "e";
case GLFW_KEY_F:
return "f";
case GLFW_KEY_G:
return "g";
case GLFW_KEY_H:
return "h";
case GLFW_KEY_I:
return "i";
case GLFW_KEY_J:
return "j";
case GLFW_KEY_K:
return "k";
case GLFW_KEY_L:
return "l";
case GLFW_KEY_M:
return "m";
case GLFW_KEY_N:
return "n";
case GLFW_KEY_O:
return "o";
case GLFW_KEY_P:
return "p";
case GLFW_KEY_Q:
return "q";
case GLFW_KEY_R:
return "r";
case GLFW_KEY_S:
return "s";
case GLFW_KEY_T:
return "t";
case GLFW_KEY_U:
return "u";
case GLFW_KEY_V:
return "v";
case GLFW_KEY_W:
return "w";
case GLFW_KEY_X:
return "x";
case GLFW_KEY_Y:
return "y";
case GLFW_KEY_Z:
return "z";
}
return NULL;
}
const char* _glfwPlatformGetNativeKeyName(int native_key UNUSED)
int _glfwPlatformGetNativeKeyForKey(int key)
{
return "";
}
int _glfwPlatformGetNativeKeyForKey(int key UNUSED)
{
return -1;
return key;
}
void _glfwPlatformGetRequiredInstanceExtensions(char** extensions UNUSED)
@@ -338,6 +670,6 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance UNUSED,
VkSurfaceKHR* surface UNUSED)
{
// This seems like the most appropriate error to return here
return VK_ERROR_INITIALIZATION_FAILED;
return VK_ERROR_EXTENSION_NOT_PRESENT;
}

View File

@@ -54,10 +54,6 @@ typedef GLFWglproc (GLAPIENTRY * PFN_OSMesaGetProcAddress)(const char*);
#define OSMesaGetDepthBuffer _glfw.osmesa.GetDepthBuffer
#define OSMesaGetProcAddress _glfw.osmesa.GetProcAddress
#define _GLFW_OSMESA_CONTEXT_STATE _GLFWcontextOSMesa osmesa
#define _GLFW_OSMESA_LIBRARY_CONTEXT_STATE _GLFWlibraryOSMesa osmesa
// OSMesa-specific per-context data
//
typedef struct _GLFWcontextOSMesa

View File

@@ -54,6 +54,7 @@
"headers": [
"wl_platform.h",
"posix_thread.h",
"wl_cursors.h",
"xkb_glfw.h",
"dbus_glfw.h",
"ibus_glfw.h",
@@ -78,6 +79,7 @@
"wl_init.c",
"wl_monitor.c",
"wl_window.c",
"wl_cursors.c",
"posix_thread.c",
"xkb_glfw.c",
"dbus_glfw.c",

35
glfw/window.c vendored
View File

@@ -240,13 +240,14 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height,
window->videoMode.blueBits = fbconfig.blueBits;
window->videoMode.refreshRate = _glfw.hints.refreshRate;
window->monitor = (_GLFWmonitor*) monitor;
window->resizable = wndconfig.resizable;
window->decorated = wndconfig.decorated;
window->autoIconify = wndconfig.autoIconify;
window->floating = wndconfig.floating;
window->focusOnShow = wndconfig.focusOnShow;
window->cursorMode = GLFW_CURSOR_NORMAL;
window->monitor = (_GLFWmonitor*) monitor;
window->resizable = wndconfig.resizable;
window->decorated = wndconfig.decorated;
window->autoIconify = wndconfig.autoIconify;
window->floating = wndconfig.floating;
window->focusOnShow = wndconfig.focusOnShow;
window->mousePassthrough = wndconfig.mousePassthrough;
window->cursorMode = GLFW_CURSOR_NORMAL;
window->minwidth = GLFW_DONT_CARE;
window->minheight = GLFW_DONT_CARE;
@@ -273,6 +274,9 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height,
}
}
if (wndconfig.mousePassthrough)
_glfwPlatformSetWindowMousePassthrough(window, true);
if (window->monitor)
{
if (wndconfig.centerCursor)
@@ -422,6 +426,9 @@ GLFWAPI void glfwWindowHint(int hint, int value)
case GLFW_FOCUS_ON_SHOW:
_glfw.hints.window.focusOnShow = value ? true : false;
return;
case GLFW_MOUSE_PASSTHROUGH:
_glfw.hints.window.mousePassthrough = value ? true : false;
return;
case GLFW_CLIENT_API:
_glfw.hints.context.client = value;
return;
@@ -440,7 +447,7 @@ GLFWAPI void glfwWindowHint(int hint, int value)
case GLFW_OPENGL_FORWARD_COMPAT:
_glfw.hints.context.forward = value ? true : false;
return;
case GLFW_OPENGL_DEBUG_CONTEXT:
case GLFW_CONTEXT_DEBUG:
_glfw.hints.context.debug = value ? true : false;
return;
case GLFW_CONTEXT_NO_ERROR:
@@ -905,6 +912,8 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib)
return _glfwPlatformWindowHovered(window);
case GLFW_FOCUS_ON_SHOW:
return window->focusOnShow;
case GLFW_MOUSE_PASSTHROUGH:
return window->mousePassthrough;
case GLFW_TRANSPARENT_FRAMEBUFFER:
return _glfwPlatformFramebufferTransparent(window);
case GLFW_OCCLUDED:
@@ -931,7 +940,7 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib)
return window->context.robustness;
case GLFW_OPENGL_FORWARD_COMPAT:
return window->context.forward;
case GLFW_OPENGL_DEBUG_CONTEXT:
case GLFW_CONTEXT_DEBUG:
return window->context.debug;
case GLFW_OPENGL_PROFILE:
return window->context.profile;
@@ -985,6 +994,14 @@ GLFWAPI void glfwSetWindowAttrib(GLFWwindow* handle, int attrib, int value)
}
else if (attrib == GLFW_FOCUS_ON_SHOW)
window->focusOnShow = value;
else if (attrib == GLFW_MOUSE_PASSTHROUGH)
{
if (window->mousePassthrough == value)
return;
window->mousePassthrough = value;
_glfwPlatformSetWindowMousePassthrough(window, value);
}
else
_glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib);
}

65
glfw/wl_cursors.c vendored Normal file
View File

@@ -0,0 +1,65 @@
// Future devs supporting whatever Wayland protocol stabilizes for cursor selection: see _themeAdd.
#include "internal.h"
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
static int
pixels_from_scale(int scale) {
static bool queried_env = false;
static int factor = 32;
if (!queried_env) {
const char *env = getenv("XCURSOR_SIZE");
if (env) {
const int retval = atoi(env);
if (retval > 0 && retval < 2048) factor = retval;
}
queried_env = true;
}
return factor * scale;
}
struct wl_cursor_theme*
glfw_wlc_theme_for_scale(int scale) {
GLFWWLCursorThemes *t = &_glfw.wl.cursor_themes;
for (size_t i = 0; i < t->count; i++) {
if (t->themes[i].scale == scale) return t->themes[i].theme;
}
if (t->count >= t->capacity) {
t->themes = realloc(t->themes, sizeof(GLFWWLCursorTheme) * (t->count + 16));
if (!t->themes) {
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Out of memory allocating space for cursor themes");
return NULL;
}
t->capacity = t->count + 16;
}
struct wl_cursor_theme *ans = wl_cursor_theme_load(getenv("XCURSOR_THEME"), pixels_from_scale(scale), _glfw.wl.shm);
if (!ans) {
_glfwInputError(
GLFW_PLATFORM_ERROR, "Wayland: wl_cursor_theme_load failed at scale: %d pixels: %d",
scale, pixels_from_scale(scale)
);
return NULL;
}
GLFWWLCursorTheme *theme = t->themes + t->count++;
theme->scale = scale;
theme->theme = ans;
return ans;
}
void
glfw_wlc_destroy(void) {
GLFWWLCursorThemes *t = &_glfw.wl.cursor_themes;
for (size_t i = 0; i < t->count; i++) {
wl_cursor_theme_destroy(t->themes[i].theme);
}
free(t->themes);
t->themes = NULL; t->capacity = 0; t->count = 0;
}

18
glfw/wl_cursors.h vendored Normal file
View File

@@ -0,0 +1,18 @@
// Declarations for a HiDPI-aware cursor theme manager.
#include <wayland-cursor.h>
typedef struct {
struct wl_cursor_theme *theme;
int scale;
} GLFWWLCursorTheme;
typedef struct {
GLFWWLCursorTheme *themes;
size_t count, capacity;
} GLFWWLCursorThemes;
struct wl_cursor_theme* glfw_wlc_theme_for_scale(int scale);
void glfw_wlc_destroy(void);

77
glfw/wl_init.c vendored
View File

@@ -113,7 +113,7 @@ static void pointerHandleEnter(void* data UNUSED,
}
window->wl.decorations.focus = focus;
_glfw.wl.pointerSerial = serial;
_glfw.wl.serial = serial;
_glfw.wl.pointerFocus = window;
window->wl.hovered = true;
@@ -134,20 +134,23 @@ static void pointerHandleLeave(void* data UNUSED,
window->wl.hovered = false;
_glfw.wl.pointerSerial = serial;
_glfw.wl.serial = serial;
_glfw.wl.pointerFocus = NULL;
_glfwInputCursorEnter(window, false);
_glfw.wl.cursorPreviousShape = GLFW_INVALID_CURSOR;
}
static void setCursor(GLFWCursorShape shape)
static void setCursor(GLFWCursorShape shape, _GLFWwindow* window)
{
struct wl_buffer* buffer;
struct wl_cursor* cursor;
struct wl_cursor_image* image;
struct wl_surface* surface = _glfw.wl.cursorSurface;
const int scale = window->wl.scale;
cursor = _glfwLoadCursor(shape);
struct wl_cursor_theme *theme = glfw_wlc_theme_for_scale(scale);
if (!theme) return;
cursor = _glfwLoadCursor(shape, theme);
if (!cursor) return;
// TODO: handle animated cursors too.
image = cursor->images[0];
@@ -158,10 +161,11 @@ static void setCursor(GLFWCursorShape shape)
buffer = wl_cursor_image_get_buffer(image);
if (!buffer)
return;
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial,
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial,
surface,
image->hotspot_x,
image->hotspot_y);
image->hotspot_x / scale,
image->hotspot_y / scale);
wl_surface_set_buffer_scale(surface, scale);
wl_surface_attach(surface, buffer, 0, 0);
wl_surface_damage(surface, 0, 0,
image->width, image->height);
@@ -225,7 +229,7 @@ static void pointerHandleMotion(void* data UNUSED,
assert(0);
}
if (_glfw.wl.cursorPreviousShape != cursorShape)
setCursor(cursorShape);
setCursor(cursorShape, window);
}
static void pointerHandleButton(void* data UNUSED,
@@ -301,7 +305,7 @@ static void pointerHandleButton(void* data UNUSED,
if (window->wl.decorations.focus != mainWindow)
return;
_glfw.wl.pointerSerial = serial;
_glfw.wl.serial = serial;
/* Makes left, right and middle 0, 1 and 2. Overall order follows evdev
* codes. */
@@ -330,11 +334,11 @@ static void pointerHandleAxis(void* data UNUSED,
axis == WL_POINTER_AXIS_VERTICAL_SCROLL);
if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
x = wl_fixed_to_double(value) * -1;
x = -wl_fixed_to_double(value);
else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
y = wl_fixed_to_double(value) * -1;
y = -wl_fixed_to_double(value);
_glfwInputScroll(window, x, y, 1);
_glfwInputScroll(window, x, y, 1, _glfw.wl.xkb.states.modifiers);
}
static const struct wl_pointer_listener pointerListener = {
@@ -372,7 +376,7 @@ static void keyboardHandleKeymap(void* data UNUSED,
static void keyboardHandleEnter(void* data UNUSED,
struct wl_keyboard* keyboard UNUSED,
uint32_t serial UNUSED,
uint32_t serial,
struct wl_surface* surface,
struct wl_array* keys)
{
@@ -388,6 +392,7 @@ static void keyboardHandleEnter(void* data UNUSED,
return;
}
_glfw.wl.serial = serial;
_glfw.wl.keyboardFocus = window;
_glfwInputWindowFocus(window, true);
uint32_t* key;
@@ -403,7 +408,7 @@ static void keyboardHandleEnter(void* data UNUSED,
static void keyboardHandleLeave(void* data UNUSED,
struct wl_keyboard* keyboard UNUSED,
uint32_t serial UNUSED,
uint32_t serial,
struct wl_surface* surface UNUSED)
{
_GLFWwindow* window = _glfw.wl.keyboardFocus;
@@ -411,6 +416,7 @@ static void keyboardHandleLeave(void* data UNUSED,
if (!window)
return;
_glfw.wl.serial = serial;
_glfw.wl.keyboardFocus = NULL;
_glfwInputWindowFocus(window, false);
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, 0);
@@ -427,7 +433,7 @@ dispatchPendingKeyRepeats(id_type timer_id UNUSED, void *data UNUSED) {
static void keyboardHandleKey(void* data UNUSED,
struct wl_keyboard* keyboard UNUSED,
uint32_t serial UNUSED,
uint32_t serial,
uint32_t time UNUSED,
uint32_t key,
uint32_t state)
@@ -436,6 +442,8 @@ static void keyboardHandleKey(void* data UNUSED,
if (!window)
return;
int action = state == WL_KEYBOARD_KEY_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE;
_glfw.wl.serial = serial;
glfw_xkb_handle_key_event(window, &_glfw.wl.xkb, key, action);
bool repeatable = false;
_glfw.wl.keyRepeatInfo.key = 0;
@@ -454,12 +462,13 @@ static void keyboardHandleKey(void* data UNUSED,
static void keyboardHandleModifiers(void* data UNUSED,
struct wl_keyboard* keyboard UNUSED,
uint32_t serial UNUSED,
uint32_t serial,
uint32_t modsDepressed,
uint32_t modsLatched,
uint32_t modsLocked,
uint32_t group)
{
_glfw.wl.serial = serial;
glfw_xkb_update_modifiers(&_glfw.wl.xkb, modsDepressed, modsLatched, modsLocked, 0, 0, group);
}
@@ -759,13 +768,6 @@ int _glfwPlatformInit(void)
// Sync so we got all initial output events
wl_display_roundtrip(_glfw.wl.display);
#ifdef __linux__
if (_glfw.hints.init.enableJoysticks) {
if (!_glfwInitJoysticksLinux())
return false;
}
#endif
if (!_glfw.wl.wmBase)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
@@ -775,35 +777,21 @@ int _glfwPlatformInit(void)
if (_glfw.wl.shm)
{
const char *cursorTheme = getenv("XCURSOR_THEME"), *cursorSizeStr = getenv("XCURSOR_SIZE");
char *cursorSizeEnd;
int cursorSize = 32;
if (cursorSizeStr)
{
errno = 0;
long cursorSizeLong = strtol(cursorSizeStr, &cursorSizeEnd, 10);
if (!*cursorSizeEnd && !errno && cursorSizeLong > 0 && cursorSizeLong <= INT_MAX)
cursorSize = (int)cursorSizeLong;
}
_glfw.wl.cursorTheme = wl_cursor_theme_load(cursorTheme, cursorSize, _glfw.wl.shm);
if (!_glfw.wl.cursorTheme)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Unable to load default cursor theme");
return false;
}
_glfw.wl.cursorSurface =
wl_compositor_create_surface(_glfw.wl.compositor);
}
else
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Failed to find Wayland SHM");
return false;
}
return true;
}
void _glfwPlatformTerminate(void)
{
#ifdef __linux__
_glfwTerminateJoysticksLinux();
#endif
_glfwTerminateEGL();
if (_glfw.wl.egl.handle)
{
@@ -814,8 +802,7 @@ void _glfwPlatformTerminate(void)
glfw_xkb_release(&_glfw.wl.xkb);
glfw_dbus_terminate(&_glfw.wl.dbus);
if (_glfw.wl.cursorTheme)
wl_cursor_theme_destroy(_glfw.wl.cursorTheme);
glfw_wlc_destroy();
if (_glfw.wl.cursor.handle)
{
_glfw_dlclose(_glfw.wl.cursor.handle);

4
glfw/wl_monitor.c vendored
View File

@@ -202,7 +202,7 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode)
bool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor UNUSED, GLFWgammaramp* ramp UNUSED)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
_glfwInputError(GLFW_FEATURE_UNAVAILABLE,
"Wayland: Gamma ramp access is not available");
return false;
}
@@ -210,7 +210,7 @@ bool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor UNUSED, GLFWgammaramp* ramp
void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor UNUSED,
const GLFWgammaramp* ramp UNUSED)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
_glfwInputError(GLFW_FEATURE_UNAVAILABLE,
"Wayland: Gamma ramp access is not available");
}

33
glfw/wl_platform.h vendored
View File

@@ -50,8 +50,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
#endif
#include "backend_utils.h"
#include "xkb_glfw.h"
#include "egl_context.h"
#include "osmesa_context.h"
#include "wl_cursors.h"
#include "wayland-xdg-shell-client-protocol.h"
#include "wayland-viewporter-client-protocol.h"
@@ -65,9 +64,6 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
#define _glfw_dlclose(handle) dlclose(handle)
#define _glfw_dlsym(handle, name) dlsym(handle, name)
#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->wl.native)
#define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.wl.display)
#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowWayland wl
#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryWayland wl
#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorWayland wl
@@ -76,18 +72,6 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
#define _GLFW_PLATFORM_CONTEXT_STATE
#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE
struct wl_cursor_image {
uint32_t width;
uint32_t height;
uint32_t hotspot_x;
uint32_t hotspot_y;
uint32_t delay;
};
struct wl_cursor {
unsigned int image_count;
struct wl_cursor_image** images;
char* name;
};
typedef struct wl_cursor_theme* (* PFN_wl_cursor_theme_load)(const char*, int, struct wl_shm*);
typedef void (* PFN_wl_cursor_theme_destroy)(struct wl_cursor_theme*);
typedef struct wl_cursor* (* PFN_wl_cursor_theme_get_cursor)(struct wl_cursor_theme*, const char*);
@@ -236,10 +220,9 @@ typedef struct _GLFWlibraryWayland
int compositorVersion;
int seatVersion;
struct wl_cursor_theme* cursorTheme;
struct wl_surface* cursorSurface;
GLFWCursorShape cursorPreviousShape;
uint32_t pointerSerial;
uint32_t serial;
int32_t keyboardRepeatRate;
monotonic_t keyboardRepeatDelay;
@@ -278,6 +261,7 @@ typedef struct _GLFWlibraryWayland
size_t dataOffersCounter;
_GLFWWaylandDataOffer dataOffers[8];
char* primarySelectionString;
GLFWWLCursorThemes cursor_themes;
} _GLFWlibraryWayland;
// Wayland-specific per-monitor data
@@ -291,8 +275,7 @@ typedef struct _GLFWmonitorWayland
int x;
int y;
int scale;
} _GLFWmonitorWayland;
} _GLFWmonitorWayland;
// Wayland-specific per-cursor data
//
@@ -302,7 +285,11 @@ typedef struct _GLFWcursorWayland
struct wl_buffer* buffer;
int width, height;
int xhot, yhot;
int currentImage;
unsigned int currentImage;
/** The scale of the cursor, or 0 if the cursor should be loaded late, or -1 if the cursor variable itself is unused. */
int scale;
/** Cursor shape stored to allow late cursor loading in setCursorImage. */
GLFWCursorShape shape;
} _GLFWcursorWayland;
@@ -310,5 +297,5 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version);
void _glfwSetupWaylandDataDevice(void);
void _glfwSetupWaylandPrimarySelectionDevice(void);
void animateCursorImage(id_type timer_id, void *data);
struct wl_cursor* _glfwLoadCursor(GLFWCursorShape);
struct wl_cursor* _glfwLoadCursor(GLFWCursorShape, struct wl_cursor_theme*);
void destroy_data_offer(_GLFWWaylandDataOffer*);

313
glfw/wl_window.c vendored
View File

@@ -42,7 +42,66 @@
#include <sys/mman.h>
static void
setCursorImage(_GLFWwindow* window)
{
_GLFWcursorWayland defaultCursor = {.shape = GLFW_ARROW_CURSOR};
_GLFWcursorWayland* cursorWayland = window->cursor ? &window->cursor->wl : &defaultCursor;
struct wl_cursor_image* image = NULL;
struct wl_buffer* buffer = NULL;
struct wl_surface* surface = _glfw.wl.cursorSurface;
const int scale = window->wl.scale;
if (cursorWayland->scale < 0) {
buffer = cursorWayland->buffer;
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, 0);
} else
{
if (cursorWayland->scale != scale) {
struct wl_cursor *newCursor = NULL;
struct wl_cursor_theme *theme = glfw_wlc_theme_for_scale(scale);
if (theme) newCursor = _glfwLoadCursor(cursorWayland->shape, theme);
if (newCursor != NULL) {
cursorWayland->cursor = newCursor;
cursorWayland->scale = scale;
cursorWayland->currentImage = 0;
} else {
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: late cursor load failed; proceeding with existing cursor");
}
}
if (!cursorWayland->cursor || !cursorWayland->cursor->image_count)
return;
if (cursorWayland->currentImage >= cursorWayland->cursor->image_count) cursorWayland->currentImage = 0;
image = cursorWayland->cursor->images[cursorWayland->currentImage];
if (!image) image = cursorWayland->cursor->images[0];
if (!image) return;
buffer = wl_cursor_image_get_buffer(image);
if (image->delay && window->cursor) {
changeTimerInterval(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, ms_to_monotonic_t(image->delay));
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, 1);
} else {
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, 0);
}
if (!buffer)
return;
cursorWayland->width = image->width;
cursorWayland->height = image->height;
cursorWayland->xhot = image->hotspot_x;
cursorWayland->yhot = image->hotspot_y;
}
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial,
surface,
cursorWayland->xhot / scale,
cursorWayland->yhot / scale);
wl_surface_set_buffer_scale(surface, scale);
wl_surface_attach(surface, buffer, 0, 0);
wl_surface_damage(surface, 0, 0,
cursorWayland->width, cursorWayland->height);
wl_surface_commit(surface);
}
static bool checkScaleChange(_GLFWwindow* window)
@@ -73,6 +132,7 @@ static bool checkScaleChange(_GLFWwindow* window)
{
window->wl.scale = scale;
wl_surface_set_buffer_scale(window->wl.surface, scale);
setCursorImage(window);
return true;
}
if (window->wl.monitorsCount > 0 && !window->wl.initial_scale_notified) {
@@ -388,9 +448,9 @@ static const struct zxdg_toplevel_decoration_v1_listener xdgDecorationListener =
xdgDecorationHandleConfigure,
};
static void handleEnter(void *data,
struct wl_surface *surface UNUSED,
struct wl_output *output)
static void surfaceHandleEnter(void *data,
struct wl_surface *surface UNUSED,
struct wl_output *output)
{
_GLFWwindow* window = data;
_GLFWmonitor* monitor = wl_output_get_user_data(output);
@@ -411,9 +471,9 @@ static void handleEnter(void *data,
}
}
static void handleLeave(void *data,
struct wl_surface *surface UNUSED,
struct wl_output *output)
static void surfaceHandleLeave(void *data,
struct wl_surface *surface UNUSED,
struct wl_output *output)
{
_GLFWwindow* window = data;
_GLFWmonitor* monitor = wl_output_get_user_data(output);
@@ -436,8 +496,8 @@ static void handleLeave(void *data,
}
static const struct wl_surface_listener surfaceListener = {
handleEnter,
handleLeave
surfaceHandleEnter,
surfaceHandleLeave
};
static void setIdleInhibitor(_GLFWwindow* window, bool enable)
@@ -679,56 +739,16 @@ static bool createXdgSurface(_GLFWwindow* window)
return true;
}
static void
setCursorImage(_GLFWcursorWayland* cursorWayland)
{
struct wl_cursor_image* image;
struct wl_buffer* buffer;
struct wl_surface* surface = _glfw.wl.cursorSurface;
if (!cursorWayland->cursor) {
buffer = cursorWayland->buffer;
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, 0);
} else
{
image = cursorWayland->cursor->images[cursorWayland->currentImage];
buffer = wl_cursor_image_get_buffer(image);
if (image->delay) {
changeTimerInterval(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, ms_to_monotonic_t(image->delay));
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, 1);
} else {
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, 0);
}
if (!buffer)
return;
cursorWayland->width = image->width;
cursorWayland->height = image->height;
cursorWayland->xhot = image->hotspot_x;
cursorWayland->yhot = image->hotspot_y;
}
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial,
surface,
cursorWayland->xhot,
cursorWayland->yhot);
wl_surface_attach(surface, buffer, 0, 0);
wl_surface_damage(surface, 0, 0,
cursorWayland->width, cursorWayland->height);
wl_surface_commit(surface);
}
static void
incrementCursorImage(_GLFWwindow* window)
{
if (window && window->wl.decorations.focus == mainWindow) {
if (window && window->wl.decorations.focus == mainWindow && window->cursorMode != GLFW_CURSOR_HIDDEN) {
_GLFWcursor* cursor = window->wl.currentCursor;
if (cursor && cursor->wl.cursor)
{
cursor->wl.currentImage += 1;
cursor->wl.currentImage %= cursor->wl.cursor->image_count;
setCursorImage(&cursor->wl);
setCursorImage(window);
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, cursor->wl.cursor->image_count > 1);
return;
}
@@ -743,12 +763,19 @@ animateCursorImage(id_type timer_id UNUSED, void *data UNUSED) {
static void
abortOnFatalError(int last_error) {
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: fatal display error: %s", strerror(last_error));
_GLFWwindow* window = _glfw.windowListHead;
while (window)
{
_glfwInputWindowCloseRequest(window);
window = window->next;
static bool abort_called = false;
if (!abort_called) {
abort_called = true;
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: fatal display error: %s", strerror(last_error));
if (_glfw.callbacks.application_close) _glfw.callbacks.application_close(1);
else {
_GLFWwindow* window = _glfw.windowListHead;
while (window)
{
_glfwInputWindowCloseRequest(window);
window = window->next;
}
}
}
// ensure the tick callback is called
_glfw.wl.eventLoopData.wakeup_data_read = true;
@@ -804,24 +831,25 @@ handleEvents(monotonic_t timeout)
}
static struct wl_cursor*
try_cursor_names(int arg_count, ...) {
try_cursor_names(struct wl_cursor_theme* theme, int arg_count, ...) {
struct wl_cursor* ans = NULL;
va_list ap;
va_start(ap, arg_count);
for (int i = 0; i < arg_count && !ans; i++) {
const char *name = va_arg(ap, const char *);
ans = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, name);
ans = wl_cursor_theme_get_cursor(theme, name);
}
va_end(ap);
return ans;
}
struct wl_cursor* _glfwLoadCursor(GLFWCursorShape shape)
struct wl_cursor* _glfwLoadCursor(GLFWCursorShape shape, struct wl_cursor_theme* theme)
{
static bool warnings[GLFW_INVALID_CURSOR] = {0};
if (!theme) return NULL;
#define NUMARGS(...) (sizeof((const char*[]){__VA_ARGS__})/sizeof(const char*))
#define C(name, ...) case name: { \
ans = try_cursor_names(NUMARGS(__VA_ARGS__), __VA_ARGS__); \
ans = try_cursor_names(theme, NUMARGS(__VA_ARGS__), __VA_ARGS__); \
if (!ans && !warnings[name]) {\
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Could not find standard cursor: %s", #name); \
warnings[name] = true; \
@@ -831,7 +859,7 @@ struct wl_cursor* _glfwLoadCursor(GLFWCursorShape shape)
struct wl_cursor* ans = NULL;
switch (shape)
{
C(GLFW_ARROW_CURSOR, "arrow", "left_ptr", "default")
C(GLFW_ARROW_CURSOR, "left_ptr", "arrow", "default")
C(GLFW_IBEAM_CURSOR, "xterm", "ibeam", "text")
C(GLFW_CROSSHAIR_CURSOR, "crosshair", "cross")
C(GLFW_HAND_CURSOR, "hand2", "grab", "grabbing", "closedhand")
@@ -901,6 +929,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
}
window->wl.currentCursor = NULL;
// Don't set window->wl.cursorTheme to NULL here.
window->wl.monitors = calloc(1, sizeof(_GLFWmonitor*));
window->wl.monitorsCount = 0;
@@ -929,6 +958,7 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
window->context.destroy(window);
destroyDecorations(window);
if (window->wl.xdg.decoration)
zxdg_toplevel_decoration_v1_destroy(window->wl.xdg.decoration);
@@ -957,12 +987,10 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
{
if (window->wl.title)
free(window->wl.title);
window->wl.title = _glfw_strdup(title);
// Wayland cannot handle requests larger than ~8200 bytes. Sending
// one causes an abort(). Since titles this large are meaningless anyway
// ensure they do not happen. One should really truncate ensuring valid UTF-8
// but I cant be bothered.
if (title && strnlen(title, 2048) >= 2048) window->wl.title[2048] = 0;
// ensure they do not happen.
window->wl.title = utf_8_strndup(title, 2048);
if (window->wl.xdg.toplevel)
xdg_toplevel_set_title(window->wl.xdg.toplevel, window->wl.title);
}
@@ -970,8 +998,8 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
void _glfwPlatformSetWindowIcon(_GLFWwindow* window UNUSED,
int count UNUSED, const GLFWimage* images UNUSED)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Setting window icon not supported");
_glfwInputError(GLFW_FEATURE_UNAVAILABLE,
"Wayland: The platform does not support setting the window icon");
}
void _glfwPlatformGetWindowPos(_GLFWwindow* window UNUSED, int* xpos UNUSED, int* ypos UNUSED)
@@ -979,16 +1007,16 @@ void _glfwPlatformGetWindowPos(_GLFWwindow* window UNUSED, int* xpos UNUSED, int
// A Wayland client is not aware of its position, so just warn and leave it
// as (0, 0)
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Window position retrieval not supported");
_glfwInputError(GLFW_FEATURE_UNAVAILABLE,
"Wayland: The platform does not provide the window position");
}
void _glfwPlatformSetWindowPos(_GLFWwindow* window UNUSED, int xpos UNUSED, int ypos UNUSED)
{
// A Wayland client can not set its position, so just warn
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Window position setting not supported");
_glfwInputError(GLFW_FEATURE_UNAVAILABLE,
"Wayland: The platform does not support setting the window position");
}
void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
@@ -1029,6 +1057,8 @@ void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window UNUSED,
{
// TODO: find out how to trigger a resize.
// The actual limits are checked in the xdg_toplevel::configure handler.
_glfwInputError(GLFW_FEATURE_UNIMPLEMENTED,
"Wayland: Window aspect ratio not yet implemented");
}
void _glfwPlatformSetWindowSizeIncrements(_GLFWwindow* window UNUSED,
@@ -1042,8 +1072,10 @@ void _glfwPlatformGetFramebufferSize(_GLFWwindow* window,
int* width, int* height)
{
_glfwPlatformGetWindowSize(window, width, height);
*width *= window->wl.scale;
*height *= window->wl.scale;
if (width)
*width *= window->wl.scale;
if (height)
*height *= window->wl.scale;
}
void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
@@ -1133,7 +1165,7 @@ void _glfwPlatformRequestWindowAttention(_GLFWwindow* window UNUSED)
// TODO
static bool notified = false;
if (!notified) {
_glfwInputError(GLFW_PLATFORM_ERROR,
_glfwInputError(GLFW_FEATURE_UNIMPLEMENTED,
"Wayland: Window attention request not implemented yet");
notified = true;
}
@@ -1154,8 +1186,8 @@ int _glfwPlatformWindowBell(_GLFWwindow* window UNUSED)
void _glfwPlatformFocusWindow(_GLFWwindow* window UNUSED)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Focusing a window requires user interaction");
_glfwInputError(GLFW_FEATURE_UNAVAILABLE,
"Wayland: The platform does not support setting the input focus");
}
void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
@@ -1208,7 +1240,7 @@ int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
void _glfwPlatformSetWindowResizable(_GLFWwindow* window UNUSED, bool enabled UNUSED)
{
// TODO
_glfwInputError(GLFW_PLATFORM_ERROR,
_glfwInputError(GLFW_FEATURE_UNIMPLEMENTED,
"Wayland: Window attribute setting not implemented yet");
}
@@ -1226,10 +1258,23 @@ void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, bool enabled)
void _glfwPlatformSetWindowFloating(_GLFWwindow* window UNUSED, bool enabled UNUSED)
{
// TODO
_glfwInputError(GLFW_PLATFORM_ERROR,
_glfwInputError(GLFW_FEATURE_UNIMPLEMENTED,
"Wayland: Window attribute setting not implemented yet");
}
void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, bool enabled)
{
if (enabled)
{
struct wl_region* region = wl_compositor_create_region(_glfw.wl.compositor);
wl_surface_set_input_region(window->wl.surface, region);
wl_region_destroy(region);
}
else
wl_surface_set_input_region(window->wl.surface, 0);
wl_surface_commit(window->wl.surface);
}
float _glfwPlatformGetWindowOpacity(_GLFWwindow* window UNUSED)
{
return 1.f;
@@ -1237,6 +1282,18 @@ float _glfwPlatformGetWindowOpacity(_GLFWwindow* window UNUSED)
void _glfwPlatformSetWindowOpacity(_GLFWwindow* window UNUSED, float opacity UNUSED)
{
_glfwInputError(GLFW_FEATURE_UNAVAILABLE,
"Wayland: The platform does not support setting the window opacity");
}
void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window UNUSED, bool enabled UNUSED)
{
// This is handled in relativePointerHandleRelativeMotion
}
bool _glfwPlatformRawMouseMotionSupported(void)
{
return true;
}
void _glfwPlatformPollEvents(void)
@@ -1309,17 +1366,19 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
cursor->wl.height = image->height;
cursor->wl.xhot = xhot;
cursor->wl.yhot = yhot;
cursor->wl.scale = -1;
cursor->wl.shape = GLFW_INVALID_CURSOR;
return true;
}
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, GLFWCursorShape shape)
{
struct wl_cursor* standardCursor;
standardCursor = _glfwLoadCursor(shape);
if (!standardCursor) return false;
cursor->wl.cursor = standardCursor;
// Don't actually load the cursor at this point,
// because there's not enough info to be properly HiDPI aware.
cursor->wl.cursor = NULL;
cursor->wl.currentImage = 0;
cursor->wl.scale = 0;
cursor->wl.shape = shape;
return true;
}
@@ -1333,31 +1392,42 @@ void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
wl_buffer_destroy(cursor->wl.buffer);
}
static void handleRelativeMotion(void* data,
struct zwp_relative_pointer_v1* pointer UNUSED,
uint32_t timeHi UNUSED,
uint32_t timeLo UNUSED,
wl_fixed_t dx UNUSED,
wl_fixed_t dy UNUSED,
wl_fixed_t dxUnaccel,
wl_fixed_t dyUnaccel)
static void relativePointerHandleRelativeMotion(void* data,
struct zwp_relative_pointer_v1* pointer UNUSED,
uint32_t timeHi UNUSED,
uint32_t timeLo UNUSED,
wl_fixed_t dx,
wl_fixed_t dy,
wl_fixed_t dxUnaccel,
wl_fixed_t dyUnaccel)
{
_GLFWwindow* window = data;
double xpos = window->virtualCursorPosX;
double ypos = window->virtualCursorPosY;
if (window->cursorMode != GLFW_CURSOR_DISABLED)
return;
_glfwInputCursorPos(window,
window->virtualCursorPosX + wl_fixed_to_double(dxUnaccel),
window->virtualCursorPosY + wl_fixed_to_double(dyUnaccel));
if (window->rawMouseMotion)
{
xpos += wl_fixed_to_double(dxUnaccel);
ypos += wl_fixed_to_double(dyUnaccel);
}
else
{
xpos += wl_fixed_to_double(dx);
ypos += wl_fixed_to_double(dy);
}
_glfwInputCursorPos(window, xpos, ypos);
}
static const struct zwp_relative_pointer_v1_listener relativePointerListener = {
handleRelativeMotion
relativePointerHandleRelativeMotion
};
static void handleLocked(void* data UNUSED,
struct zwp_locked_pointer_v1* lockedPointer UNUSED)
static void lockedPointerHandleLocked(void* data UNUSED,
struct zwp_locked_pointer_v1* lockedPointer UNUSED)
{
}
@@ -1377,14 +1447,14 @@ static void unlockPointer(_GLFWwindow* window)
static void lockPointer(_GLFWwindow* window UNUSED);
static void handleUnlocked(void* data UNUSED,
struct zwp_locked_pointer_v1* lockedPointer UNUSED)
static void lockedPointerHandleUnlocked(void* data UNUSED,
struct zwp_locked_pointer_v1* lockedPointer UNUSED)
{
}
static const struct zwp_locked_pointer_v1_listener lockedPointerListener = {
handleLocked,
handleUnlocked
lockedPointerHandleLocked,
lockedPointerHandleUnlocked
};
static void lockPointer(_GLFWwindow* window)
@@ -1421,7 +1491,7 @@ static void lockPointer(_GLFWwindow* window)
window->wl.pointerLock.relativePointer = relativePointer;
window->wl.pointerLock.lockedPointer = lockedPointer;
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial,
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial,
NULL, 0, 0);
}
@@ -1432,8 +1502,6 @@ static bool isPointerLocked(_GLFWwindow* window)
void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
{
struct wl_cursor* defaultCursor;
if (!_glfw.wl.pointer)
return;
@@ -1450,21 +1518,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
if (window->cursorMode == GLFW_CURSOR_NORMAL)
{
if (cursor)
setCursorImage(&cursor->wl);
else
{
defaultCursor = _glfwLoadCursor(GLFW_ARROW_CURSOR);
if (!defaultCursor) return;
_GLFWcursorWayland cursorWayland = {
defaultCursor,
NULL,
0, 0,
0, 0,
0
};
setCursorImage(&cursorWayland);
}
setCursorImage(window);
}
else if (window->cursorMode == GLFW_CURSOR_DISABLED)
{
@@ -1473,8 +1527,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
}
else if (window->cursorMode == GLFW_CURSOR_HIDDEN)
{
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial,
NULL, 0, 0);
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, NULL, 0, 0);
}
}
@@ -1967,6 +2020,24 @@ const char* _glfwPlatformGetPrimarySelectionString(void)
return NULL;
}
EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs UNUSED)
{
if (_glfw.egl.EXT_platform_base && _glfw.egl.EXT_platform_wayland)
return EGL_PLATFORM_WAYLAND_EXT;
else
return 0;
}
EGLNativeDisplayType _glfwPlatformGetEGLNativeDisplay(void)
{
return _glfw.wl.display;
}
EGLNativeWindowType _glfwPlatformGetEGLNativeWindow(_GLFWwindow* window)
{
return window->wl.native;
}
void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
{
if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_wayland_surface)

92
glfw/x11_init.c vendored
View File

@@ -42,11 +42,11 @@
#include <unistd.h>
// Check whether the specified atom is supported
// Return the atom ID only if it is listed in the specified array
//
static Atom getSupportedAtom(Atom* supportedAtoms,
unsigned long atomCount,
const char* atomName)
static Atom getAtomIfSupported(Atom* supportedAtoms,
unsigned long atomCount,
const char* atomName)
{
const Atom atom = XInternAtom(_glfw.x11.display, atomName, False);
@@ -64,9 +64,8 @@ static Atom getSupportedAtom(Atom* supportedAtoms,
static void detectEWMH(void)
{
// First we read the _NET_SUPPORTING_WM_CHECK property on the root window
Window* windowFromRoot = NULL;
// Then we look for the _NET_SUPPORTING_WM_CHECK property of the root window
Window* windowFromRoot = NULL;
if (!_glfwGetWindowPropertyX11(_glfw.x11.root,
_glfw.x11.NET_SUPPORTING_WM_CHECK,
XA_WINDOW,
@@ -93,6 +92,7 @@ static void detectEWMH(void)
_glfwReleaseErrorHandlerX11();
// If the property exists, it should contain the XID of the window
if (*windowFromRoot != *windowFromChild)
{
XFree(windowFromRoot);
@@ -103,49 +103,50 @@ static void detectEWMH(void)
XFree(windowFromRoot);
XFree(windowFromChild);
// We are now fairly sure that an EWMH-compliant window manager is running
// We are now fairly sure that an EWMH-compliant WM is currently running
// We can now start querying the WM about what features it supports by
// looking in the _NET_SUPPORTED property on the root window
// It should contain a list of supported EWMH protocol and state atoms
Atom* supportedAtoms = NULL;
const unsigned long atomCount = _glfwGetWindowPropertyX11(_glfw.x11.root,
_glfw.x11.NET_SUPPORTED,
XA_ATOM,
(unsigned char**) &supportedAtoms);
const unsigned long atomCount =
_glfwGetWindowPropertyX11(_glfw.x11.root,
_glfw.x11.NET_SUPPORTED,
XA_ATOM,
(unsigned char**) &supportedAtoms);
if (!supportedAtoms)
return;
// See which of the atoms we support that are supported by the WM
_glfw.x11.NET_WM_STATE =
getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE");
getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE");
_glfw.x11.NET_WM_STATE_ABOVE =
getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_ABOVE");
getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_ABOVE");
_glfw.x11.NET_WM_STATE_FULLSCREEN =
getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_FULLSCREEN");
getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_FULLSCREEN");
_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT =
getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_VERT");
getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_VERT");
_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ =
getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_HORZ");
getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_HORZ");
_glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION =
getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_DEMANDS_ATTENTION");
getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_DEMANDS_ATTENTION");
_glfw.x11.NET_WM_FULLSCREEN_MONITORS =
getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_FULLSCREEN_MONITORS");
getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_FULLSCREEN_MONITORS");
_glfw.x11.NET_WM_WINDOW_TYPE =
getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE");
getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE");
_glfw.x11.NET_WM_WINDOW_TYPE_NORMAL =
getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE_NORMAL");
getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE_NORMAL");
_glfw.x11.NET_WORKAREA =
getSupportedAtom(supportedAtoms, atomCount, "_NET_WORKAREA");
getAtomIfSupported(supportedAtoms, atomCount, "_NET_WORKAREA");
_glfw.x11.NET_CURRENT_DESKTOP =
getSupportedAtom(supportedAtoms, atomCount, "_NET_CURRENT_DESKTOP");
getAtomIfSupported(supportedAtoms, atomCount, "_NET_CURRENT_DESKTOP");
_glfw.x11.NET_ACTIVE_WINDOW =
getSupportedAtom(supportedAtoms, atomCount, "_NET_ACTIVE_WINDOW");
getAtomIfSupported(supportedAtoms, atomCount, "_NET_ACTIVE_WINDOW");
_glfw.x11.NET_FRAME_EXTENTS =
getSupportedAtom(supportedAtoms, atomCount, "_NET_FRAME_EXTENTS");
getAtomIfSupported(supportedAtoms, atomCount, "_NET_FRAME_EXTENTS");
_glfw.x11.NET_REQUEST_FRAME_EXTENTS =
getSupportedAtom(supportedAtoms, atomCount, "_NET_REQUEST_FRAME_EXTENTS");
getAtomIfSupported(supportedAtoms, atomCount, "_NET_REQUEST_FRAME_EXTENTS");
XFree(supportedAtoms);
}
@@ -326,6 +327,30 @@ static bool initExtensions(void)
}
}
#if defined(__CYGWIN__)
_glfw.x11.xshape.handle = _glfw_dlopen("libXext-6.so");
#else
_glfw.x11.xshape.handle = _glfw_dlopen("libXext.so.6");
#endif
if (_glfw.x11.xshape.handle)
{
glfw_dlsym(_glfw.x11.xshape.QueryExtension, _glfw.x11.xshape.handle, "XShapeQueryExtension");
glfw_dlsym(_glfw.x11.xshape.ShapeCombineRegion, _glfw.x11.xshape.handle, "XShapeCombineRegion");
glfw_dlsym(_glfw.x11.xshape.QueryVersion, _glfw.x11.xshape.handle, "XShapeQueryVersion");
if (XShapeQueryExtension(_glfw.x11.display,
&_glfw.x11.xshape.errorBase,
&_glfw.x11.xshape.eventBase))
{
if (XShapeQueryVersion(_glfw.x11.display,
&_glfw.x11.xshape.major,
&_glfw.x11.xshape.minor))
{
_glfw.x11.xshape.available = true;
}
}
}
_glfw.x11.xkb.major = 1;
_glfw.x11.xkb.minor = 0;
_glfw.x11.xkb.available = XkbQueryExtension(_glfw.x11.display,
@@ -502,8 +527,11 @@ static Window createHelperWindow(void)
// X error handler
//
static int errorHandler(Display *display UNUSED, XErrorEvent* event)
static int errorHandler(Display *display, XErrorEvent* event)
{
if (_glfw.x11.display != display)
return 0;
_glfw.x11.errorCode = event->error_code;
return 0;
}
@@ -625,15 +653,6 @@ int _glfwPlatformInit(void)
_glfw.x11.helperWindowHandle = createHelperWindow();
_glfw.x11.hiddenCursorHandle = createHiddenCursor();
#if defined(__linux__)
if (_glfw.hints.init.enableJoysticks) {
if (!_glfwInitJoysticksLinux())
return false;
if (_glfw.linjs.inotify > 0)
addWatch(&_glfw.x11.eventLoopData, "joystick", _glfw.linjs.inotify, POLLIN, 1, NULL, NULL);
}
#endif
_glfwPollMonitorsX11();
return true;
}
@@ -712,9 +731,6 @@ void _glfwPlatformTerminate(void)
_glfwTerminateEGL();
_glfwTerminateGLX();
#if defined(__linux__)
_glfwTerminateJoysticksLinux();
#endif
finalizePollData(&_glfw.x11.eventLoopData);
}

29
glfw/x11_monitor.c vendored
View File

@@ -169,7 +169,7 @@ void _glfwPollMonitorsX11(void)
if (widthMM <= 0 || heightMM <= 0)
{
// HACK: If RandR does not provide a physical size, assume the
// X11 default 96 DPI and calcuate from the CRTC viewport
// X11 default 96 DPI and calculate from the CRTC viewport
// NOTE: These members are affected by rotation, unlike the mode
// info and output info members
widthMM = (int) (ci->width * 25.4f / 96.f);
@@ -241,7 +241,8 @@ void _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired)
if (_glfwCompareVideoModes(&current, best) == 0)
return;
XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
XRRScreenResources* sr =
XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output);
@@ -289,7 +290,8 @@ void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor)
if (monitor->x11.oldMode == None)
return;
XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
XRRScreenResources* sr =
XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
XRRSetCrtcConfig(_glfw.x11.display,
@@ -321,9 +323,10 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
{
if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
{
XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
XRRScreenResources* sr =
XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
if (ci)
{
if (xpos)
@@ -333,6 +336,7 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
XRRFreeCrtcInfo(ci);
}
XRRFreeScreenResources(sr);
}
}
@@ -352,8 +356,8 @@ void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos
if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
{
XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
XRRScreenResources* sr =
XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
areaX = ci->x;
@@ -446,7 +450,8 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
{
XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
XRRScreenResources* sr =
XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output);
@@ -493,17 +498,19 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode)
{
if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
{
XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
XRRScreenResources* sr =
XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
if (ci) {
if (ci)
{
const XRRModeInfo* mi = getModeInfo(sr, ci->mode);
if (mi) // mi can be NULL if the monitor has been disconnected
*mode = vidmodeFromModeInfo(mi, ci);
XRRFreeCrtcInfo(ci);
}
XRRFreeScreenResources(sr);
}
else

31
glfw/x11_platform.h vendored
View File

@@ -51,6 +51,9 @@
// The XInput extension provides raw mouse motion input
#include <X11/extensions/XInput2.h>
// The Shape extension provides custom window shapes
#include <X11/extensions/shape.h>
// The libxkb library is used for improved keyboard support
#include "xkb_glfw.h"
#include "backend_utils.h"
@@ -125,6 +128,16 @@ typedef XRenderPictFormat* (* PFN_XRenderFindVisualFormat)(Display*,Visual const
#define XRenderQueryVersion _glfw.x11.xrender.QueryVersion
#define XRenderFindVisualFormat _glfw.x11.xrender.FindVisualFormat
typedef Bool (* PFN_XShapeQueryExtension)(Display*,int*,int*);
typedef Status (* PFN_XShapeQueryVersion)(Display*dpy,int*,int*);
typedef void (* PFN_XShapeCombineRegion)(Display*,Window,int,int,int,Region,int);
typedef void (* PFN_XShapeCombineMask)(Display*,Window,int,int,int,Pixmap,int);
#define XShapeQueryExtension _glfw.x11.xshape.QueryExtension
#define XShapeQueryVersion _glfw.x11.xshape.QueryVersion
#define XShapeCombineRegion _glfw.x11.xshape.ShapeCombineRegion
#define XShapeCombineMask _glfw.x11.xshape.ShapeCombineMask
typedef VkFlags VkXlibSurfaceCreateFlagsKHR;
typedef VkFlags VkXcbSurfaceCreateFlagsKHR;
@@ -153,8 +166,6 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(Vk
#include "posix_thread.h"
#include "glx_context.h"
#include "egl_context.h"
#include "osmesa_context.h"
#if defined(__linux__)
#include "linux_joystick.h"
#else
@@ -165,9 +176,6 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(Vk
#define _glfw_dlclose(handle) dlclose(handle)
#define _glfw_dlsym(handle, name) dlsym(handle, name)
#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->x11.handle)
#define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.x11.display)
#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowX11 x11
#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryX11 x11
#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorX11 x11
@@ -382,6 +390,19 @@ typedef struct _GLFWlibraryX11
PFN_XRenderFindVisualFormat FindVisualFormat;
} xrender;
struct {
bool available;
void* handle;
int major;
int minor;
int eventBase;
int errorBase;
PFN_XShapeQueryExtension QueryExtension;
PFN_XShapeCombineRegion ShapeCombineRegion;
PFN_XShapeQueryVersion QueryVersion;
PFN_XShapeCombineMask ShapeCombineMask;
} xshape;
EventLoopData eventLoopData;
} _GLFWlibraryX11;

236
glfw/x11_window.c vendored
View File

@@ -455,22 +455,41 @@ static void updateCursorImage(_GLFWwindow* window)
}
}
// Enable XI2 raw mouse motion events
//
static void enableRawMouseMotion(_GLFWwindow* window UNUSED)
{
XIEventMask em;
unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 };
em.deviceid = XIAllMasterDevices;
em.mask_len = sizeof(mask);
em.mask = mask;
XISetMask(mask, XI_RawMotion);
XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
}
// Disable XI2 raw mouse motion events
//
static void disableRawMouseMotion(_GLFWwindow* window UNUSED)
{
XIEventMask em;
unsigned char mask[] = { 0 };
em.deviceid = XIAllMasterDevices;
em.mask_len = sizeof(mask);
em.mask = mask;
XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
}
// Apply disabled cursor mode to a focused window
//
static void disableCursor(_GLFWwindow* window)
{
if (_glfw.x11.xi.available)
{
XIEventMask em;
unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 };
em.deviceid = XIAllMasterDevices;
em.mask_len = sizeof(mask);
em.mask = mask;
XISetMask(mask, XI_RawMotion);
XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
}
if (window->rawMouseMotion)
enableRawMouseMotion(window);
_glfw.x11.disabledCursorWindow = window;
_glfwPlatformGetCursorPos(window,
@@ -490,17 +509,8 @@ static void disableCursor(_GLFWwindow* window)
//
static void enableCursor(_GLFWwindow* window)
{
if (_glfw.x11.xi.available)
{
XIEventMask em;
unsigned char mask[] = { 0 };
em.deviceid = XIAllMasterDevices;
em.mask_len = sizeof(mask);
em.mask = mask;
XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
}
if (window->rawMouseMotion)
disableRawMouseMotion(window);
_glfw.x11.disabledCursorWindow = NULL;
XUngrabPointer(_glfw.x11.display, CurrentTime);
@@ -1075,6 +1085,21 @@ static void onConfigChange(void)
}
}
static void
get_atom_names(Atom *atoms, int count, char **atom_names) {
_glfwGrabErrorHandlerX11();
XGetAtomNames(_glfw.x11.display, atoms, count, atom_names);
_glfwReleaseErrorHandlerX11();
if (_glfw.x11.errorCode != Success) {
for (int i = 0; i < count; i++) {
_glfwGrabErrorHandlerX11();
atom_names[i] = XGetAtomName(_glfw.x11.display, atoms[i]);
_glfwReleaseErrorHandlerX11();
if (_glfw.x11.errorCode != Success) atom_names[i] = NULL;
}
}
}
// Process the specified X event
//
static void processEvent(XEvent *event)
@@ -1107,6 +1132,7 @@ static void processEvent(XEvent *event)
_GLFWwindow* window = _glfw.x11.disabledCursorWindow;
if (window &&
window->rawMouseMotion &&
event->xcookie.extension == _glfw.x11.xi.majorOpcode &&
XGetEventData(_glfw.x11.display, &event->xcookie) &&
event->xcookie.evtype == XI_RawMotion)
@@ -1150,12 +1176,26 @@ static void processEvent(XEvent *event)
else if (event->type == _glfw.x11.xkb.eventBase)
{
XkbEvent *kb_event = (XkbEvent*)event;
if (kb_event->any.device != (unsigned int)_glfw.x11.xkb.keyboard_device_id) return;
switch(kb_event->any.xkb_type) {
case XkbNewKeyboardNotify:
if(!glfw_xkb_update_x11_keyboard_id(&_glfw.x11.xkb)) return;
/* fallthrough */
case XkbNewKeyboardNotify: {
XkbNewKeyboardNotifyEvent *newkb_event = (XkbNewKeyboardNotifyEvent*)kb_event;
if (_glfw.hints.init.debugKeyboard) printf(
"Got XkbNewKeyboardNotify event with changes: key codes: %d geometry: %d device id: %d\n",
!!(newkb_event->changed & XkbNKN_KeycodesMask), !!(newkb_event->changed & XkbNKN_GeometryMask),
!!(newkb_event->changed & XkbNKN_DeviceIDMask));
if (newkb_event->changed & XkbNKN_DeviceIDMask) {
keymap_dirty = true;
if (!glfw_xkb_update_x11_keyboard_id(&_glfw.x11.xkb)) return;
}
if (newkb_event->changed & XkbNKN_KeycodesMask) {
keymap_dirty = true;
}
return;
}
case XkbMapNotify:
{
if (_glfw.hints.init.debugKeyboard) printf("Got XkbMapNotify event, keymaps will be reloaded\n");
keymap_dirty = true;
return;
}
@@ -1250,13 +1290,13 @@ static void processEvent(XEvent *event)
// Modern X provides scroll events as mouse button presses
else if (event->xbutton.button == Button4)
_glfwInputScroll(window, 0.0, 1.0, 0);
_glfwInputScroll(window, 0.0, 1.0, 0, mods);
else if (event->xbutton.button == Button5)
_glfwInputScroll(window, 0.0, -1.0, 0);
_glfwInputScroll(window, 0.0, -1.0, 0, mods);
else if (event->xbutton.button == Button6)
_glfwInputScroll(window, 1.0, 0.0, 0);
_glfwInputScroll(window, 1.0, 0.0, 0, mods);
else if (event->xbutton.button == Button7)
_glfwInputScroll(window, -1.0, 0.0, 0);
_glfwInputScroll(window, -1.0, 0.0, 0, mods);
else
{
@@ -1348,7 +1388,7 @@ static void processEvent(XEvent *event)
{
if (_glfw.x11.disabledCursorWindow != window)
return;
if (_glfw.x11.xi.available)
if (window->rawMouseMotion)
return;
const int dx = x - window->x11.lastCursorPosX;
@@ -1393,12 +1433,18 @@ static void processEvent(XEvent *event)
if (!event->xany.send_event && window->x11.parent != _glfw.x11.root)
{
Window dummy;
_glfwGrabErrorHandlerX11();
XTranslateCoordinates(_glfw.x11.display,
window->x11.parent,
_glfw.x11.root,
xpos, ypos,
&xpos, &ypos,
&dummy);
_glfwReleaseErrorHandlerX11();
if (_glfw.x11.errorCode != Success) {
_glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to translate ConfigureNotiy co-ords for reparented window");
return;
}
}
if (xpos != window->x11.xpos || ypos != window->x11.ypos)
@@ -1474,7 +1520,7 @@ static void processEvent(XEvent *event)
}
char **atom_names = calloc(count, sizeof(char**));
if (atom_names) {
XGetAtomNames(_glfw.x11.display, formats, count, atom_names);
get_atom_names(formats, count, atom_names);
for (i = 0; i < count; i++)
{
@@ -1535,17 +1581,21 @@ static void processEvent(XEvent *event)
const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff;
const int yabs = (event->xclient.data.l[2]) & 0xffff;
Window dummy;
int xpos, ypos;
int xpos = 0, ypos = 0;
if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
return;
_glfwGrabErrorHandlerX11();
XTranslateCoordinates(_glfw.x11.display,
_glfw.x11.root,
window->x11.handle,
xabs, yabs,
&xpos, &ypos,
&dummy);
_glfwReleaseErrorHandlerX11();
if (_glfw.x11.errorCode != Success)
_glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to get DND event position");
_glfwInputCursorPos(window, xpos, ypos);
@@ -1800,7 +1850,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig)
{
Visual* visual;
Visual* visual = NULL;
int depth;
if (ctxconfig->client != GLFW_NO_API)
@@ -1826,8 +1876,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
}
}
if (ctxconfig->client == GLFW_NO_API ||
ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
if (!visual)
{
visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen);
depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen);
@@ -1973,10 +2022,14 @@ void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
{
Window dummy;
int x, y;
int x = 0, y = 0;
_glfwGrabErrorHandlerX11();
XTranslateCoordinates(_glfw.x11.display, window->x11.handle, _glfw.x11.root,
0, 0, &x, &y, &dummy);
_glfwReleaseErrorHandlerX11();
if (_glfw.x11.errorCode != Success)
_glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to get window position");
if (xpos)
*xpos = x;
@@ -2375,6 +2428,7 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window)
{
return maximized;
}
const unsigned long count =
_glfwGetWindowPropertyX11(window->x11.handle,
_glfw.x11.NET_WM_STATE,
@@ -2406,13 +2460,19 @@ int _glfwPlatformWindowHovered(_GLFWwindow* window)
int rootX, rootY, childX, childY;
unsigned int mask;
if (!XQueryPointer(_glfw.x11.display, w,
&root, &w, &rootX, &rootY, &childX, &childY, &mask))
{
return false;
}
_glfwGrabErrorHandlerX11();
if (w == window->x11.handle)
const Bool result = XQueryPointer(_glfw.x11.display, w,
&root, &w, &rootX, &rootY,
&childX, &childY, &mask);
_glfwReleaseErrorHandlerX11();
if (_glfw.x11.errorCode == BadWindow)
w = _glfw.x11.root;
else if (!result)
return false;
else if (w == window->x11.handle)
return true;
}
@@ -2526,6 +2586,25 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, bool enabled)
XFlush(_glfw.x11.display);
}
void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, bool enabled)
{
if (!_glfw.x11.xshape.available)
return;
if (enabled)
{
Region region = XCreateRegion();
XShapeCombineRegion(_glfw.x11.display, window->x11.handle,
ShapeInput, 0, 0, region, ShapeSet);
XDestroyRegion(region);
}
else
{
XShapeCombineMask(_glfw.x11.display, window->x11.handle,
ShapeInput, 0, 0, None, ShapeSet);
}
}
float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
{
float opacity = 1.f;
@@ -2574,7 +2653,8 @@ _glfwDispatchX11Events(void) {
unsigned dispatched = 0;
#if defined(__linux__)
if (_glfw.hints.init.enableJoysticks) _glfwDetectJoystickConnectionLinux();
if (_glfw.joysticksInitialized)
_glfwDetectJoystickConnectionLinux();
#endif
dispatched += dispatch_x11_queued_events(XEventsQueued(_glfw.x11.display, QueuedAfterFlush));
@@ -2601,6 +2681,25 @@ _glfwDispatchX11Events(void) {
return dispatched;
}
void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, bool enabled)
{
if (!_glfw.x11.xi.available)
return;
if (_glfw.x11.disabledCursorWindow != window)
return;
if (enabled)
enableRawMouseMotion(window);
else
disableRawMouseMotion(window);
}
bool _glfwPlatformRawMouseMotionSupported(void)
{
return _glfw.x11.xi.available;
}
void _glfwPlatformPollEvents(void)
{
_glfwDispatchX11Events();
@@ -2782,6 +2881,55 @@ const char* _glfwPlatformGetPrimarySelectionString(void)
return getSelectionString(_glfw.x11.PRIMARY);
}
EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs)
{
if (_glfw.egl.ANGLE_platform_angle)
{
int type = 0;
if (_glfw.egl.ANGLE_platform_angle_opengl)
{
if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_OPENGL)
type = EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE;
}
if (_glfw.egl.ANGLE_platform_angle_vulkan)
{
if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_VULKAN)
type = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE;
}
if (type)
{
*attribs = calloc(5, sizeof(EGLint));
(*attribs)[0] = EGL_PLATFORM_ANGLE_TYPE_ANGLE;
(*attribs)[1] = type;
(*attribs)[2] = EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE;
(*attribs)[3] = EGL_PLATFORM_X11_EXT;
(*attribs)[4] = EGL_NONE;
return EGL_PLATFORM_ANGLE_ANGLE;
}
}
if (_glfw.egl.EXT_platform_base && _glfw.egl.EXT_platform_x11)
return EGL_PLATFORM_X11_EXT;
return 0;
}
EGLNativeDisplayType _glfwPlatformGetEGLNativeDisplay(void)
{
return _glfw.x11.display;
}
EGLNativeWindowType _glfwPlatformGetEGLNativeWindow(_GLFWwindow* window)
{
if (_glfw.egl.platform)
return &window->x11.handle;
else
return (EGLNativeWindowType) window->x11.handle;
}
void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
{
if (!_glfw.vk.KHR_surface)

59
glfw/xkb_glfw.c vendored
View File

@@ -371,9 +371,31 @@ load_compose_tables(_GLFWXKBData *xkb) {
xkb_compose_table_unref(compose_table);
}
static inline xkb_mod_mask_t
active_unknown_modifiers(_GLFWXKBData *xkb, struct xkb_state *state) {
size_t i = 0;
xkb_mod_mask_t ans = 0;
while (xkb->unknownModifiers[i] != XKB_MOD_INVALID) {
if (xkb_state_mod_index_is_active(state, xkb->unknownModifiers[i], XKB_STATE_MODS_EFFECTIVE)) ans |= (1 << xkb->unknownModifiers[i]);
i++;
}
return ans;
}
static void
update_modifiers(_GLFWXKBData *xkb) {
XKBStateGroup *group = &xkb->states;
#define S(attr, name) if (xkb_state_mod_index_is_active(group->state, xkb->attr##Idx, XKB_STATE_MODS_EFFECTIVE)) group->modifiers |= GLFW_MOD_##name
S(control, CONTROL); S(alt, ALT); S(shift, SHIFT); S(super, SUPER); S(capsLock, CAPS_LOCK); S(numLock, NUM_LOCK);
#undef S
xkb->states.activeUnknownModifiers = active_unknown_modifiers(xkb, xkb->states.state);
}
bool
glfw_xkb_compile_keymap(_GLFWXKBData *xkb, const char *map_str) {
const char *err;
debug("Loading new XKB keymaps\n");
release_keyboard_data(xkb);
err = load_keymaps(xkb, map_str);
if (err) {
@@ -396,36 +418,17 @@ glfw_xkb_compile_keymap(_GLFWXKBData *xkb, const char *map_str) {
S(capsLock, XKB_MOD_NAME_CAPS);
S(numLock, XKB_MOD_NAME_NUM);
#undef S
size_t capacity = sizeof(xkb->unknownModifiers)/sizeof(xkb->unknownModifiers[0]), j = 0;
size_t capacity = arraysz(xkb->unknownModifiers), j = 0;
for (xkb_mod_index_t i = 0; i < capacity; i++) xkb->unknownModifiers[i] = XKB_MOD_INVALID;
for (xkb_mod_index_t i = 0; i < xkb_keymap_num_mods(xkb->keymap) && j < capacity - 1; i++) {
if (i != xkb->controlIdx && i != xkb->altIdx && i != xkb->shiftIdx && i != xkb->superIdx && i != xkb->capsLockIdx && i != xkb->numLockIdx) xkb->unknownModifiers[j++] = i;
}
xkb->states.modifiers = 0;
xkb->states.activeUnknownModifiers = 0;
update_modifiers(xkb);
return true;
}
static inline xkb_mod_mask_t
active_unknown_modifiers(_GLFWXKBData *xkb, struct xkb_state *state) {
size_t i = 0;
xkb_mod_mask_t ans = 0;
while (xkb->unknownModifiers[i] != XKB_MOD_INVALID) {
if (xkb_state_mod_index_is_active(state, xkb->unknownModifiers[i], XKB_STATE_MODS_EFFECTIVE)) ans |= (1 << xkb->unknownModifiers[i]);
i++;
}
return ans;
}
static void
update_modifiers(_GLFWXKBData *xkb, XKBStateGroup *group) {
#define S(attr, name) if (xkb_state_mod_index_is_active(group->state, xkb->attr##Idx, XKB_STATE_MODS_EFFECTIVE)) group->modifiers |= GLFW_MOD_##name
S(control, CONTROL); S(alt, ALT); S(shift, SHIFT); S(super, SUPER); S(capsLock, CAPS_LOCK); S(numLock, NUM_LOCK);
#undef S
xkb->states.activeUnknownModifiers = active_unknown_modifiers(xkb, xkb->states.state);
}
void
glfw_xkb_update_modifiers(_GLFWXKBData *xkb, xkb_mod_mask_t depressed, xkb_mod_mask_t latched, xkb_mod_mask_t locked, xkb_layout_index_t base_group, xkb_layout_index_t latched_group, xkb_layout_index_t locked_group) {
if (!xkb->keymap) return;
@@ -434,7 +437,7 @@ glfw_xkb_update_modifiers(_GLFWXKBData *xkb, xkb_mod_mask_t depressed, xkb_mod_m
// We have to update the groups in clean_state, as they change for
// different keyboard layouts, see https://github.com/kovidgoyal/kitty/issues/488
xkb_state_update_mask(xkb->states.clean_state, 0, 0, 0, base_group, latched_group, locked_group);
update_modifiers(xkb, &xkb->states);
update_modifiers(xkb);
}
bool
@@ -503,14 +506,19 @@ static inline const char*
format_xkb_mods(_GLFWXKBData *xkb, const char* name, xkb_mod_mask_t mods) {
static char buf[512];
char *p = buf, *s;
#define pr(x) p += snprintf(p, sizeof(buf) - (p - buf) - 1, "%s", x)
#define pr(x) { \
int num_needed = -1; \
ssize_t space_left = sizeof(buf) - (p - buf) - 1; \
if (space_left > 0) num_needed = snprintf(p, space_left, "%s", x); \
if (num_needed > 0) p += num_needed; \
}
pr(name); pr(": ");
s = p;
for (xkb_mod_index_t i = 0; i < xkb_keymap_num_mods(xkb->keymap); i++) {
xkb_mod_mask_t m = 1 << i;
if (m & mods) { pr(xkb_keymap_mod_get_name(xkb->keymap, i)); pr("+"); }
}
if (p == s) pr("none");
if (p == s) { pr("none"); }
else p--;
pr(" ");
#undef pr
@@ -620,6 +628,9 @@ glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t
}
if (key_text[0]) { debug("%s: %s ", text_type, key_text); }
}
if (xkb_sym == XKB_KEY_ISO_First_Group || xkb_sym == XKB_KEY_ISO_Last_Group || xkb_sym == XKB_KEY_ISO_Next_Group || xkb_sym == XKB_KEY_ISO_Prev_Group || xkb_sym == XKB_KEY_Mode_switch) {
return;
}
int glfw_sym = glfw_key_for_sym(xkb_sym);
bool is_fallback = false;
if (glfw_sym == GLFW_KEY_UNKNOWN && !key_text[0]) {

View File

@@ -3,6 +3,7 @@
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import os
import sys
from contextlib import suppress
from typing import TYPE_CHECKING, List, Optional, Tuple
@@ -12,7 +13,7 @@ from kitty.constants import cache_dir
from kitty.typing import BossType
from ..tui.handler import result_handler
from ..tui.operations import alternate_screen, styled
from ..tui.operations import alternate_screen, set_cursor_visible, styled
if TYPE_CHECKING:
import readline
@@ -69,7 +70,7 @@ class HistoryCompleter:
def option_text() -> str:
return '''\
--type -t
choices=line
choices=line,yesno
default=line
Type of input. Defaults to asking for a line of text.
@@ -96,14 +97,30 @@ class Response(TypedDict):
response: Optional[str]
def yesno(cli_opts: AskCLIOptions, items: List[str]) -> Response:
import tty
with alternate_screen():
if cli_opts.message:
print(styled(cli_opts.message, bold=True))
print()
print(' ', styled('Y', fg='green') + 'es', ' ', styled('N', fg='red') + 'o', set_cursor_visible(False))
sys.stdout.flush()
tty.setraw(sys.stdin.fileno())
try:
response = sys.stdin.buffer.read(1)
yes = response in (b'y', b'Y', b'\r', b'\n' b' ')
return {'items': items, 'response': 'y' if yes else 'n'}
finally:
sys.stdout.write(set_cursor_visible(True))
tty.setcbreak(sys.stdin.fileno())
sys.stdout.flush()
def main(args: List[str]) -> Response:
# For some reason importing readline in a key handler in the main kitty process
# causes a crash of the python interpreter, probably because of some global
# lock
global readline
import readline as rl
readline = rl
from kitty.shell import init_readline
msg = 'Ask the user for input'
try:
cli_opts, items = parse_args(args[1:], option_text, '', msg, 'kitty ask', result_class=AskCLIOptions)
@@ -113,6 +130,12 @@ def main(args: List[str]) -> Response:
input('Press enter to quit...')
raise SystemExit(e.code)
if cli_opts.type == 'yesno':
return yesno(cli_opts, items)
import readline as rl
readline = rl
from kitty.shell import init_readline
init_readline(readline)
response = None
@@ -134,7 +157,6 @@ def handle_result(args: List[str], data: Response, target_window_id: int, boss:
if __name__ == '__main__':
import sys
ans = main(sys.argv)
if ans:
print(ans)

View File

@@ -19,7 +19,7 @@ from .config_data import all_options, type_convert
defaults: Optional[DiffOptions] = None
formats = {
formats: Dict[str, str] = {
'title': '',
'margin': '',
'text': '',
@@ -35,6 +35,7 @@ def set_formats(opts: DiffOptions) -> None:
formats['added'] = '48' + color_as_sgr(opts.added_bg)
formats['removed'] = '48' + color_as_sgr(opts.removed_bg)
formats['filler'] = '48' + color_as_sgr(opts.filler_bg)
formats['margin_filler'] = '48' + color_as_sgr(opts.margin_filler_bg or opts.filler_bg)
formats['hunk_margin'] = '38' + color_as_sgr(opts.margin_fg) + ';48' + color_as_sgr(opts.hunk_margin_bg)
formats['hunk'] = '38' + color_as_sgr(opts.margin_fg) + ';48' + color_as_sgr(opts.hunk_bg)
formats['removed_highlight'] = '48' + color_as_sgr(opts.highlight_removed_bg)

View File

@@ -9,7 +9,9 @@ from gettext import gettext as _
from typing import Any, Dict, Sequence, Union
from kitty.conf.definition import Option, Shortcut, option_func
from kitty.conf.utils import positive_int, python_string, to_color
from kitty.conf.utils import (
positive_int, python_string, to_color, to_color_or_none
)
# }}}
@@ -78,6 +80,8 @@ c('highlight_added_bg', '#acf2bd')
c('added_margin_bg', '#cdffd8')
c('filler_bg', '#fafbfc', long_text=_('Filler (empty) line background'))
c('margin_filler_bg', 'none', option_type=to_color_or_none, long_text=_(
'Filler (empty) line background in margins, defaults to the filler background'))
c('hunk_margin_bg', '#dbedff', long_text=_('Hunk header colors'))
c('hunk_bg', '#f1f8ff')

View File

@@ -12,6 +12,7 @@ from pygments.formatter import Formatter # type: ignore
from pygments.lexers import get_lexer_for_filename # type: ignore
from pygments.util import ClassNotFound # type: ignore
from kitty.multiprocessing import get_process_pool_executor
from kitty.rgb import color_as_sgr, parse_sharp
from .collect import Collection, Segment, data_for_path, lines_for_path
@@ -139,7 +140,7 @@ def highlight_for_diff(path: str, aliases: Dict[str, str]) -> DiffHighlight:
def highlight_collection(collection: Collection, aliases: Optional[Dict[str, str]] = None) -> Union[str, Dict[str, DiffHighlight]]:
jobs = {}
ans: Dict[str, DiffHighlight] = {}
with concurrent.futures.ProcessPoolExecutor(max_workers=os.cpu_count()) as executor:
with get_process_pool_executor(prefer_fork=True) as executor:
for path, item_type, other_path in collection:
if item_type != 'rename':
for p in (path, other_path):

View File

@@ -113,13 +113,13 @@ class DiffHandler(Handler):
return self.scroll_lines(amt)
if func == 'change_context':
new_ctx = self.current_context_count
to = int(args[0])
to = args[0]
if to == 'all':
new_ctx = 100000
elif to == 'default':
new_ctx = self.original_context_count
else:
new_ctx += to
new_ctx += int(to)
return self.change_context_count(new_ctx)
if func == 'start_search':
self.start_search(bool(args[0]), bool(args[1]))

View File

@@ -141,6 +141,7 @@ removed_format = format_func('removed')
removed_margin_format = format_func('removed_margin')
added_margin_format = format_func('added_margin')
filler_format = format_func('filler')
margin_filler_format = format_func('margin_filler')
hunk_margin_format = format_func('hunk_margin')
hunk_format = format_func('hunk')
highlight_map = {'remove': ('removed_highlight', 'removed'), 'add': ('added_highlight', 'added')}
@@ -214,7 +215,7 @@ def split_with_highlights(line: str, width: int, highlights: List, bg_highlight:
return _split_with_highlights(line, truncate_pts, highlights, bg_highlight)
margin_bg_map = {'filler': filler_format, 'remove': removed_margin_format, 'add': added_margin_format, 'context': margin_format}
margin_bg_map = {'filler': margin_filler_format, 'remove': removed_margin_format, 'add': added_margin_format, 'context': margin_format}
text_bg_map = {'filler': filler_format, 'remove': removed_format, 'add': added_format, 'context': text_format}

View File

@@ -72,7 +72,7 @@ def decode_hint(x: str, alphabet: str = DEFAULT_HINT_ALPHABET) -> int:
return i
def highlight_mark(m: Mark, text: str, current_input: str, alphabet: str) -> str:
def highlight_mark(m: Mark, text: str, current_input: str, alphabet: str, colors: Dict[str, str]) -> str:
hint = encode_hint(m.index, alphabet)
if current_input and not hint.startswith(current_input):
return faint(text)
@@ -80,19 +80,19 @@ def highlight_mark(m: Mark, text: str, current_input: str, alphabet: str) -> str
text = text[len(hint):]
return styled(
hint,
fg='black',
bg='green',
fg=colors['foreground'],
bg=colors['background'],
bold=True
) + styled(
text, fg='gray', fg_intense=True, bold=True
text, fg=colors['text'], fg_intense=True, bold=True
)
def render(text: str, current_input: str, all_marks: Sequence[Mark], ignore_mark_indices: Set[int], alphabet: str) -> str:
def render(text: str, current_input: str, all_marks: Sequence[Mark], ignore_mark_indices: Set[int], alphabet: str, colors: Dict[str, str]) -> str:
for mark in reversed(all_marks):
if mark.index in ignore_mark_indices:
continue
mtext = highlight_mark(mark, text[mark.start:mark.end], current_input, alphabet)
mtext = highlight_mark(mark, text[mark.start:mark.end], current_input, alphabet, colors)
text = text[:mark.start] + mtext + text[mark.end:]
text = text.replace('\0', '')
@@ -105,6 +105,9 @@ class Hints(Handler):
def __init__(self, text: str, all_marks: Sequence[Mark], index_map: Dict[int, Mark], args: HintsCLIOptions):
self.text, self.index_map = text, index_map
self.alphabet = args.alphabet or DEFAULT_HINT_ALPHABET
self.colors = {'foreground': args.hints_foreground_color,
'background': args.hints_background_color,
'text': args.hints_text_color}
self.all_marks = all_marks
self.ignore_mark_indices: Set[int] = set()
self.args = args
@@ -198,7 +201,7 @@ class Hints(Handler):
def draw_screen(self) -> None:
if self.current_text is None:
self.current_text = render(self.text, self.current_input, self.all_marks, self.ignore_mark_indices, self.alphabet)
self.current_text = render(self.text, self.current_input, self.all_marks, self.ignore_mark_indices, self.alphabet, self.colors)
self.cmd.clear_screen()
self.write(self.current_text)
@@ -315,7 +318,7 @@ def functions_for(args: HintsCLIOptions) -> Tuple[str, List[PostprocessorFunc]]:
pattern = '[0-9a-f]{7,128}'
elif args.type == 'word':
chars = args.word_characters
if not chars:
if chars is None:
chars = kitty_common_opts()['select_by_word_characters']
pattern = r'(?u)[{}\w]{{{},}}'.format(escape(chars), args.minimum_match_length)
post_processors.extend((brackets, quotes))
@@ -419,10 +422,11 @@ programs.
--type
default=url
choices=url,regex,path,line,hash,word,linenum
The type of text to search for. A value of :code:`linenum` looks for error messages
using the pattern specified with :option:`--regex`, which must have the named groups,
path and line. If not specified, will look for :code:`path:line`.
The :option:`--linenum-action` option controls what to do with the selected error message.
The type of text to search for. A value of :code:`linenum` is special, it looks
for error messages using the pattern specified with :option:`--regex`, which
must have the named groups, :code:`path` and :code:`line`. If not specified,
will look for :code:`path:line`. The :option:`--linenum-action` option
controls what to do with the selected error message, other options are ignored.
--regex
@@ -512,6 +516,24 @@ type=bool-set
Have the hints increase from top to bottom instead of decreasing from top to bottom.
--hints-foreground-color
default=black
type=str
The foreground color for hints
--hints-background-color
default=green
type=str
The background color for hints
--hints-text-color
default=gray
type=str
The foreground color for text pointed to by the hints
--customize-processing
Name of a python file in the kitty config directory which will be imported to provide
custom implementations for pattern finding and performing actions
@@ -558,7 +580,7 @@ def linenum_handle_result(args: List[str], data: Dict[str, Any], target_window_i
for m, g in zip(data['match'], data['groupdicts']):
if m:
path, line = g['path'], g['line']
path = path.split(':')[-1]
path = os.path.expanduser(path.split(':')[-1])
line = int(line)
break
else:

View File

@@ -2,9 +2,11 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
import contextlib
import mimetypes
import os
import re
import socket
import signal
import sys
import zlib
@@ -339,6 +341,16 @@ help_text = (
usage = 'image-file-or-url-or-directory ...'
@contextlib.contextmanager
def socket_timeout(seconds: int) -> Generator[None, None, None]:
old = socket.getdefaulttimeout()
socket.setdefaulttimeout(seconds)
try:
yield
finally:
socket.setdefaulttimeout(old)
def process_single_item(
item: Union[bytes, str],
args: IcatCLIOptions,
@@ -358,7 +370,8 @@ def process_single_item(
from urllib.request import urlretrieve
with NamedTemporaryFile(prefix='url-image-data-', delete=False) as tf:
try:
urlretrieve(item, filename=tf.name)
with socket_timeout(30):
urlretrieve(item, filename=tf.name)
except Exception as e:
raise SystemExit('Failed to download image at URL: {} with error: {}'.format(item, e))
item = tf.name

View File

@@ -10,8 +10,8 @@ from typing import Callable, Dict, List, Tuple
from kitty.cli import parse_args
from kitty.cli_stub import PanelCLIOptions
from kitty.options_stub import Options
from kitty.constants import is_macos
from kitty.os_window_size import WindowSizeData
OPTIONS = r'''
--lines
@@ -108,7 +108,7 @@ def setup_x11_window(win_id: int) -> None:
func(win_id, window_width, window_height)
def initial_window_size_func(opts: Options, cached_values: Dict) -> Callable[[int, int, float, float, float, float], Tuple[int, int]]:
def initial_window_size_func(opts: WindowSizeData, cached_values: Dict) -> Callable[[int, int, float, float, float, float], Tuple[int, int]]:
from kitty.fast_data_types import glfw_primary_monitor_size, set_smallest_allowed_resize
def initial_window_size(cell_width: int, cell_height: int, dpi_x: float, dpi_y: float, xscale: float, yscale: float) -> Tuple[int, int]:

View File

@@ -94,6 +94,7 @@ def convert(
if scaled or width > available_width or height > available_height:
width, height = fit_image(width, height, available_width, available_height)
cmd += ['-resize', '{}x{}!'.format(width, height)]
cmd += ['-depth', '8']
with NamedTemporaryFile(prefix='icat-', suffix='.' + m.mode, delete=False, dir=tdir) as outfile:
run_imagemagick(path, cmd + [outfile.name])
# ImageMagick sometimes generated rgba images smaller than the specified

View File

@@ -341,7 +341,7 @@ def request_from_clipboard(use_primary: bool = False) -> str:
return '\x1b]52;{};?\x07'.format('p' if use_primary else 'c')
# Boilerplate to make operations availble via Handler.cmd {{{
# Boilerplate to make operations available via Handler.cmd {{{
def writer(handler: HandlerType, func: Callable) -> Callable:

View File

@@ -20,7 +20,7 @@ from kitty.config import cached_values_for
from kitty.constants import config_dir
from kitty.fast_data_types import is_emoji_presentation_base, wcswidth
from kitty.key_encoding import (
CTRL, RELEASE, SHIFT, KeyEvent, enter_key, key_defs as K
CTRL, PRESS, RELEASE, SHIFT, KeyEvent, enter_key, key_defs as K
)
from kitty.typing import BossType
from kitty.utils import ScreenSize, get_editor
@@ -38,6 +38,8 @@ UP = K['UP']
DOWN = K['DOWN']
LEFT = K['LEFT']
RIGHT = K['RIGHT']
RIGHT_BRACKET = K['RIGHT_BRACKET']
LEFT_BRACKET = K['LEFT_BRACKET']
TAB = K['TAB']
ESCAPE = K['ESCAPE']
F1 = K['F1']
@@ -478,7 +480,7 @@ class UnicodeInput(Handler):
return
if key_event is enter_key:
self.quit_loop(0)
elif key_event.type is RELEASE:
elif key_event.type is PRESS:
if not key_event.mods:
if key_event.key is ESCAPE:
self.quit_loop(1)
@@ -492,8 +494,8 @@ class UnicodeInput(Handler):
self.switch_mode(FAVORITES)
elif key_event.key is F12 and self.mode is FAVORITES:
self.edit_favorites()
elif key_event.mods == CTRL and key_event.key is TAB:
self.next_mode()
elif key_event.mods == CTRL and key_event.key in (TAB, RIGHT_BRACKET, LEFT_BRACKET):
self.next_mode(-1 if key_event.key is LEFT_BRACKET else 1)
elif key_event.mods == CTRL | SHIFT and key_event.key is TAB:
self.next_mode(-1)

File diff suppressed because one or more lines are too long

View File

@@ -4,16 +4,16 @@
from enum import IntFlag
from itertools import chain
from typing import List, Optional, Sequence, Tuple
from typing import Sequence, Tuple
from .constants import WindowGeometry
from .fast_data_types import (
BORDERS_PROGRAM, add_borders_rect, compile_program, init_borders_program,
os_window_has_background_image
)
from .options_stub import Options
from .typing import LayoutType
from .utils import load_shaders
from .typing import WindowType, LayoutType
from .window_list import WindowGroup, WindowList
class BorderColor(IntFlag):
@@ -22,22 +22,40 @@ class BorderColor(IntFlag):
def vertical_edge(os_window_id: int, tab_id: int, color: int, width: int, top: int, bottom: int, left: int) -> None:
add_borders_rect(os_window_id, tab_id, left, top, left + width, bottom, color)
if width > 0:
add_borders_rect(os_window_id, tab_id, left, top, left + width, bottom, color)
def horizontal_edge(os_window_id: int, tab_id: int, color: int, height: int, left: int, right: int, top: int) -> None:
add_borders_rect(os_window_id, tab_id, left, top, right, top + height, color)
if height > 0:
add_borders_rect(os_window_id, tab_id, left, top, right, top + height, color)
def draw_edges(os_window_id: int, tab_id: int, colors: Sequence[int], width: int, geometry: 'WindowGeometry', base_width: int = 0) -> None:
left = geometry.left - (width + base_width)
top = geometry.top - (width + base_width)
right = geometry.right + (width + base_width)
bottom = geometry.bottom + (width + base_width)
horizontal_edge(os_window_id, tab_id, colors[1], width, left, right, top)
horizontal_edge(os_window_id, tab_id, colors[3], width, left, right, geometry.bottom + base_width)
vertical_edge(os_window_id, tab_id, colors[0], width, top, bottom, left)
vertical_edge(os_window_id, tab_id, colors[2], width, top, bottom, geometry.right + base_width)
def draw_edges(os_window_id: int, tab_id: int, colors: Sequence[int], wg: WindowGroup, borders: bool = False) -> None:
geometry = wg.geometry
if geometry is None:
return
pl, pt = wg.effective_padding('left'), wg.effective_padding('top')
pr, pb = wg.effective_padding('right'), wg.effective_padding('bottom')
left = geometry.left - pl
top = geometry.top - pt
lr = geometry.right
right = lr + pr
bt = geometry.bottom
bottom = bt + pb
if borders:
width = wg.effective_border()
bt = bottom
lr = right
left -= width
top -= width
right += width
bottom += width
pl = pr = pb = pt = width
horizontal_edge(os_window_id, tab_id, colors[1], pt, left, right, top)
horizontal_edge(os_window_id, tab_id, colors[3], pb, left, right, bt)
vertical_edge(os_window_id, tab_id, colors[0], pl, top, bottom, left)
vertical_edge(os_window_id, tab_id, colors[2], pr, top, bottom, lr)
def load_borders_program() -> None:
@@ -54,12 +72,9 @@ class Borders:
def __call__(
self,
windows: List[WindowType],
active_window: Optional[WindowType],
all_windows: WindowList,
current_layout: LayoutType,
extra_blank_rects: Sequence[Tuple[int, int, int, int]],
padding_width: int,
border_width: int,
draw_window_borders: bool = True,
) -> None:
add_borders_rect(self.os_window_id, self.tab_id, 0, 0, 0, 0, BorderColor.default_bg)
@@ -68,32 +83,34 @@ class Borders:
for br in chain(current_layout.blank_rects, extra_blank_rects):
left, top, right, bottom = br
add_borders_rect(self.os_window_id, self.tab_id, left, top, right, bottom, BorderColor.default_bg)
bw, pw = border_width, padding_width
if bw + pw <= 0:
return
bw = 0
groups = tuple(all_windows.iter_all_layoutable_groups(only_visible=True))
if groups:
bw = groups[0].effective_border()
draw_borders = bw > 0 and draw_window_borders
if draw_borders:
border_data = current_layout.resolve_borders(windows, active_window)
border_data = current_layout.resolve_borders(all_windows)
active_group = all_windows.active_group
for i, w in enumerate(windows):
g = w.geometry
window_bg = w.screen.color_profile.default_bg
for i, wg in enumerate(groups):
window_bg = wg.default_bg
window_bg = (window_bg << 8) | BorderColor.window_bg
if draw_borders:
# Draw the border rectangles
if w is active_window and self.draw_active_borders:
if wg is active_group and self.draw_active_borders:
color = BorderColor.active
else:
color = BorderColor.bell if w.needs_attention else BorderColor.inactive
colors = tuple(color if needed else window_bg for needed in next(border_data))
draw_edges(
self.os_window_id, self.tab_id, colors, bw, g, base_width=pw)
if pw > 0 and not has_background_image:
color = BorderColor.bell if wg.needs_attention else BorderColor.inactive
try:
colors = tuple(color if needed else window_bg for needed in next(border_data))
draw_edges(self.os_window_id, self.tab_id, colors, wg, borders=True)
except StopIteration:
pass
if not has_background_image:
# Draw the background rectangles over the padding region
colors = (window_bg, window_bg, window_bg, window_bg)
draw_edges(
self.os_window_id, self.tab_id, colors, pw, g)
colors = window_bg, window_bg, window_bg, window_bg
draw_edges(self.os_window_id, self.tab_id, colors, wg)
color = BorderColor.inactive
for (left, top, right, bottom) in current_layout.window_independent_borders(windows, active_window):
for (left, top, right, bottom) in current_layout.window_independent_borders(all_windows):
add_borders_rect(self.os_window_id, self.tab_id, left, top, right, bottom, color)

View File

@@ -19,7 +19,7 @@ from .cli import create_opts, parse_args
from .cli_stub import CLIOptions
from .conf.utils import BadLine, to_cmdline
from .config import (
KeyAction, SubSequenceMap, common_opts_as_dict, initial_window_size_func,
KeyAction, SubSequenceMap, common_opts_as_dict,
prepare_config_file_for_editing
)
from .config_data import MINIMUM_FONT_SIZE
@@ -27,19 +27,22 @@ from .constants import (
appname, config_dir, is_macos, kitty_exe, supports_primary_selection
)
from .fast_data_types import (
CLOSE_BEING_CONFIRMED, IMPERATIVE_CLOSE_REQUESTED, NO_CLOSE_REQUESTED,
ChildMonitor, background_opacity_of, change_background_opacity,
change_os_window_state, cocoa_set_menubar_title, create_os_window,
current_os_window, destroy_global_data, focus_os_window,
get_clipboard_string, global_font_size, mark_os_window_for_close,
os_window_font_size, patch_global_colors, safe_pipe, set_background_image,
set_boss, set_clipboard_string, set_in_sequence_mode, thread_write,
current_application_quit_request, current_os_window, destroy_global_data,
focus_os_window, get_clipboard_string, global_font_size,
mark_os_window_for_close, os_window_font_size, patch_global_colors,
safe_pipe, set_application_quit_request, set_background_image, set_boss,
set_clipboard_string, set_in_sequence_mode, thread_write,
toggle_fullscreen, toggle_maximized
)
from .keys import get_shortcut, shortcut_matches
from .layout import set_layout_options
from .layout.base import set_layout_options
from .options_stub import Options
from .os_window_size import initial_window_size_func
from .rgb import Color, color_from_int
from .session import Session, create_sessions
from .session import Session, create_sessions, get_os_window_sizing_data
from .tabs import (
SpecialWindow, SpecialWindowInstance, Tab, TabDict, TabManager
)
@@ -47,14 +50,15 @@ from .typing import PopenType, TypedDict
from .utils import (
func_name, get_editor, get_primary_selection, is_path_in_temp_dir,
log_error, open_url, parse_address_spec, parse_uri_list,
remove_socket_file, safe_print, set_primary_selection, single_instance,
startup_notification_handler
platform_window_id, remove_socket_file, safe_print, set_primary_selection,
single_instance, startup_notification_handler
)
from .window import MatchPatternType, Window
class OSWindowDict(TypedDict):
id: int
platform_window_id: Optional[int]
is_focused: bool
tabs: List[TabDict]
@@ -192,13 +196,13 @@ class Boss:
startup_id: Optional[str] = None
) -> int:
if os_window_id is None:
opts_for_size = opts_for_size or getattr(startup_session, 'os_window_size', None) or self.opts
wclass = wclass or self.args.cls or appname
size_data = get_os_window_sizing_data(opts_for_size or self.opts, startup_session)
wclass = wclass or getattr(startup_session, 'os_window_class', None) or self.args.cls or appname
with startup_notification_handler(do_notify=startup_id is not None, startup_id=startup_id) as pre_show_callback:
os_window_id = create_os_window(
initial_window_size_func(opts_for_size, self.cached_values),
initial_window_size_func(size_data, self.cached_values),
pre_show_callback,
appname, wname or self.args.name or wclass, wclass)
self.args.title or appname, wname or self.args.name or wclass, wclass)
tm = TabManager(os_window_id, self.opts, self.args, startup_session)
self.os_window_map[os_window_id] = tm
return os_window_id
@@ -210,6 +214,7 @@ class Boss:
for os_window_id, tm in self.os_window_map.items():
yield {
'id': os_window_id,
'platform_window_id': platform_window_id(os_window_id),
'is_focused': tm is active_tab_manager,
'tabs': list(tm.list_tabs(active_tab, active_window)),
}
@@ -304,10 +309,9 @@ class Boss:
@property
def active_window_for_cwd(self) -> Optional[Window]:
w = self.active_window
if w is not None and w.overlay_for is not None and w.overlay_for in self.window_id_map:
w = self.window_id_map[w.overlay_for]
return w
t = self.active_tab
if t is not None:
return t.active_window_for_cwd
def new_os_window_with_cwd(self, *args: str) -> None:
w = self.active_window_for_cwd
@@ -336,19 +340,41 @@ class Boss:
if not getattr(err, 'hide_traceback', False):
response['tb'] = traceback.format_exc()
else:
response = {'ok': False, 'error': 'Remote control is disabled. Add allow_remote_control to your kitty.conf'}
no_response = False
try:
no_response = json.loads(cmd).get('no_response')
except Exception:
pass
if not no_response:
response = {'ok': False, 'error': 'Remote control is disabled. Add allow_remote_control to your kitty.conf'}
return response
def remote_control(self, *args: str) -> None:
from .remote_control import parse_rc_args
from .rc.base import command_for_name, parse_subcommand_cli, PayloadGetter
try:
global_opts, items = parse_rc_args(['@'] + list(args))
if not items:
return
cmd = items[0]
c = command_for_name(cmd)
opts, items = parse_subcommand_cli(c, items)
payload = c.message_to_kitty(global_opts, opts, items)
c.response_from_kitty(self, self.active_window, PayloadGetter(c, payload if isinstance(payload, dict) else {}))
except (Exception, SystemExit) as e:
self.show_error(_('remote_control mapping failed'), str(e))
def peer_message_received(self, msg_bytes: bytes) -> Optional[bytes]:
msg = msg_bytes.decode('utf-8')
cmd_prefix = '\x1bP@kitty-cmd'
if msg.startswith(cmd_prefix):
cmd = msg[len(cmd_prefix):-2]
cmd_prefix = b'\x1bP@kitty-cmd'
terminator = b'\x1b\\'
if msg_bytes.startswith(cmd_prefix) and msg_bytes.endswith(terminator):
cmd = msg_bytes[len(cmd_prefix):-len(terminator)].decode('utf-8')
response = self._handle_remote_command(cmd, from_peer=True)
if response is None:
return None
return (cmd_prefix + json.dumps(response) + '\x1b\\').encode('utf-8')
data = json.loads(msg)
return cmd_prefix + json.dumps(response).encode('utf-8') + terminator
data = json.loads(msg_bytes.decode('utf-8'))
if isinstance(data, dict) and data.get('cmd') == 'new_instance':
from .cli_stub import CLIOptions
startup_id = data.get('startup_id')
@@ -359,6 +385,8 @@ class Boss:
args.directory = os.path.join(data['cwd'], args.directory)
for session in create_sessions(opts, args, respect_cwd=True):
os_window_id = self.add_os_window(session, wclass=args.cls, wname=args.name, opts_for_size=opts, startup_id=startup_id)
if opts.background_opacity != self.opts.background_opacity:
self._set_os_window_background_opacity(os_window_id, opts.background_opacity)
if data.get('notify_on_os_window_death'):
self.os_window_death_actions[os_window_id] = partial(self.notify_on_os_window_death, data['notify_on_os_window_death'])
else:
@@ -419,8 +447,34 @@ class Boss:
def close_tab(self, tab: Optional[Tab] = None) -> None:
tab = tab or self.active_tab
if tab:
for window in tab:
self.close_window(window)
self.confirm_tab_close(tab)
def confirm_tab_close(self, tab: Tab) -> None:
windows = tuple(tab)
needs_confirmation = self.opts.confirm_os_window_close > 0 and len(windows) >= self.opts.confirm_os_window_close
if not needs_confirmation:
self.close_tab_no_confirm(tab)
return
self._run_kitten('ask', ['--type=yesno', '--message', _(
'Are you sure you want to close this tab, it has {}'
' windows running?').format(len(windows))],
window=tab.active_window,
custom_callback=partial(self.handle_close_tab_confirmation, tab.id)
)
def handle_close_tab_confirmation(self, tab_id: int, data: Dict[str, Any], *a: Any) -> None:
if data['response'] != 'y':
return
for tab in self.all_tabs:
if tab.id == tab_id:
break
else:
return
self.close_tab_no_confirm(tab)
def close_tab_no_confirm(self, tab: Tab) -> None:
for window in tab:
self.close_window(window)
def toggle_fullscreen(self) -> None:
toggle_fullscreen()
@@ -534,7 +588,9 @@ class Boss:
sz = os_window_font_size(os_window_id)
if sz:
os_window_font_size(os_window_id, sz, True)
tm.update_dpi_based_sizes()
for tab in tm:
for window in tab:
window.on_dpi_change(sz)
tm.resize()
def _set_os_window_background_opacity(self, os_window_id: int, opacity: float) -> None:
@@ -700,6 +756,32 @@ class Boss:
text = '\n'.join(parse_uri_list(text))
w.paste(text)
def close_os_window(self) -> None:
tm = self.active_tab_manager
if tm is not None:
self.confirm_os_window_close(tm.os_window_id)
def confirm_os_window_close(self, os_window_id: int) -> None:
tm = self.os_window_map.get(os_window_id)
needs_confirmation = tm is not None and self.opts.confirm_os_window_close > 0 and tm.number_of_windows >= self.opts.confirm_os_window_close
if not needs_confirmation:
mark_os_window_for_close(os_window_id)
return
if tm is not None:
w = tm.active_window
self._run_kitten('ask', ['--type=yesno', '--message', _(
'Are you sure you want to close this OS window, it has {}'
' windows running?').format(tm.number_of_windows)],
window=w,
custom_callback=partial(self.handle_close_os_window_confirmation, os_window_id)
)
def handle_close_os_window_confirmation(self, os_window_id: int, data: Dict[str, Any], *a: Any) -> None:
if data['response'] == 'y':
mark_os_window_for_close(os_window_id)
else:
mark_os_window_for_close(os_window_id, NO_CLOSE_REQUESTED)
def on_os_window_closed(self, os_window_id: int, viewport_width: int, viewport_height: int) -> None:
self.cached_values['window-size'] = viewport_width, viewport_height
tm = self.os_window_map.pop(os_window_id, None)
@@ -713,6 +795,28 @@ class Boss:
if action is not None:
action()
def quit(self, *args: Any) -> None:
tm = self.active_tab
num = 0
for q in self.os_window_map.values():
num += q.number_of_windows
needs_confirmation = tm is not None and self.opts.confirm_os_window_close > 0 and num >= self.opts.confirm_os_window_close
if not needs_confirmation:
set_application_quit_request(IMPERATIVE_CLOSE_REQUESTED)
return
if current_application_quit_request() == CLOSE_BEING_CONFIRMED:
return
assert tm is not None
self._run_kitten('ask', ['--type=yesno', '--message', _(
'Are you sure you want to quit kitty, it has {} windows running?').format(num)],
window=tm.active_window,
custom_callback=self.handle_quit_confirmation
)
set_application_quit_request(CLOSE_BEING_CONFIRMED)
def handle_quit_confirmation(self, data: Dict[str, Any], *a: Any) -> None:
set_application_quit_request(IMPERATIVE_CLOSE_REQUESTED if data['response'] == 'y' else NO_CLOSE_REQUESTED)
def notify_on_os_window_death(self, address: str) -> None:
import socket
s = socket.socket(family=socket.AF_UNIX)
@@ -725,9 +829,9 @@ class Boss:
def display_scrollback(self, window: Window, data: Optional[bytes], cmd: Optional[List[str]]) -> None:
tab = self.active_tab
if tab is not None and window.overlay_for is None:
if tab is not None:
tab.new_special_window(
SpecialWindow(cmd, data, _('History'), overlay_for=window.id),
SpecialWindow(cmd, data, _('History'), overlay_for=window.id, cwd=window.cwd_of_child),
copy_colors_from=self.active_window
)
@@ -755,7 +859,7 @@ class Boss:
window: Optional[Window] = None,
custom_callback: Optional[Callable] = None,
action_on_removal: Optional[Callable] = None
) -> Optional[Window]:
) -> Any:
orig_args, args = list(args), list(args)
from kittens.runner import create_kitten_handler
end_kitten = create_kitten_handler(kitten, orig_args)
@@ -766,10 +870,9 @@ class Boss:
w = window
tab = w.tabref() if w else None
if end_kitten.no_ui:
end_kitten(None, getattr(w, 'id', None), self)
return None
return end_kitten(None, getattr(w, 'id', None), self)
if w is not None and tab is not None and w.overlay_for is None:
if w is not None and tab is not None:
args[0:0] = [config_dir, kitten]
if input_data is None:
type_of_input = end_kitten.type_of_input
@@ -779,6 +882,9 @@ class Boss:
add_history='history' in type_of_input,
add_wrap_markers='screen' in type_of_input
).encode('utf-8')
elif type_of_input == 'selection':
sel = self.data_for_at(which='@selection', window=w)
data = sel.encode('utf-8') if sel else None
elif type_of_input is None:
data = None
else:
@@ -786,9 +892,16 @@ class Boss:
else:
data = input_data if isinstance(input_data, bytes) else input_data.encode('utf-8')
copts = common_opts_as_dict(self.opts)
final_args: List[str] = []
for x in args:
if x == '@selection':
sel = self.data_for_at(which='@selection', window=w)
if sel:
x = sel
final_args.append(x)
overlay_window = tab.new_special_window(
SpecialWindow(
[kitty_exe(), '+runpy', 'from kittens.runner import main; main()'] + args,
[kitty_exe(), '+runpy', 'from kittens.runner import main; main()'] + final_args,
stdin=data,
env={
'KITTY_COMMON_OPTS': json.dumps(copts),
@@ -870,24 +983,35 @@ class Boss:
custom_callback=done, action_on_removal=done2)
def kitty_shell(self, window_type: str) -> None:
cmd = ['@', kitty_exe(), '@']
kw: Dict[str, Any] = {}
cmd = [kitty_exe(), '@']
aw = self.active_window
if aw is not None:
kw['env'] = {'KITTY_SHELL_ACTIVE_WINDOW_ID': str(aw.id)}
if window_type == 'tab':
self._new_tab(cmd)
tab = self._new_tab(SpecialWindow(cmd, **kw))
if tab is not None:
for w in tab:
w.allow_remote_control = True
elif window_type == 'os_window':
os_window_id = self._new_os_window(cmd)
self.os_window_map[os_window_id]
os_window_id = self._new_os_window(SpecialWindow(cmd, **kw))
for tab in self.os_window_map[os_window_id]:
for w in tab:
w.allow_remote_control = True
elif window_type == 'overlay':
w = self.active_window
tab = self.active_tab
if w is not None and tab is not None and w.overlay_for is None:
tab.new_special_window(SpecialWindow(cmd, overlay_for=w.id))
if aw is not None and tab is not None:
kw['overlay_for'] = aw.id
tab.new_special_window(SpecialWindow(cmd, **kw), allow_remote_control=True)
else:
self._new_window(cmd)
tab = self.active_tab
if tab is not None:
tab.new_special_window(SpecialWindow(cmd, **kw), allow_remote_control=True)
def switch_focus_to(self, window_idx: int) -> None:
def switch_focus_to(self, window_id: int) -> None:
tab = self.active_tab
if tab:
tab.set_active_window_idx(window_idx)
tab.set_active_window(window_id)
def open_url(self, url: str, program: Optional[Union[str, List[str]]] = None, cwd: Optional[str] = None) -> None:
if url:
@@ -1021,7 +1145,7 @@ class Boss:
continue
arg = q
cmdline.append(arg)
overlay_for = w.id if w and as_overlay and w.overlay_for is None else None
overlay_for = w.id if w and as_overlay else None
return SpecialWindow(cmd, input_data, cwd_from=cwd_from, overlay_for=overlay_for, env=env)
def run_background_process(
@@ -1292,11 +1416,8 @@ class Boss:
else:
return
underlaid_window, overlaid_window = src_tab.detach_window(window)
if underlaid_window:
target_tab.attach_window(underlaid_window)
if overlaid_window:
target_tab.attach_window(overlaid_window)
for detached_window in src_tab.detach_window(window):
target_tab.attach_window(detached_window)
self._cleanup_tab_after_window_removal(src_tab)
target_tab.make_active()
@@ -1342,8 +1463,6 @@ class Boss:
done_tab_id = tab_id_map[int(data['groupdicts'][0]['index'])]
def done2(target_window_id: int, self: Boss) -> None:
if not hasattr(done, 'tab_id'):
return
tab_id = done_tab_id
target_window = None
for w in self.all_windows:

View File

@@ -208,7 +208,7 @@ void main() {
// Background {{{
#ifdef NEEDS_BACKROUND
float cell_has_non_default_bg = step(1, float(abs(bg_as_uint - default_colors[bg_index])));
float cell_has_non_default_bg = step(1, float(abs(bg_as_uint - default_colors[1])));
draw_bg = 1;
#if defined(BACKGROUND)

View File

@@ -40,7 +40,7 @@ static void (*parse_func)(Screen*, PyObject*, monotonic_t);
typedef struct {
char *data;
size_t sz;
int fd;
id_type peer_id;
} Message;
typedef struct {
@@ -71,9 +71,8 @@ static const Child EMPTY_CHILD = {0};
pthread_mutex_##op(&screen->which##_buf_lock);
#define children_mutex(op) \
pthread_mutex_##op(&children_lock);
#define peer_mutex(op) \
pthread_mutex_##op(&talk_data.peer_lock);
#define talk_mutex(op) \
pthread_mutex_##op(&talk_lock);
static Child children[MAX_CHILDREN] = {{0}};
@@ -82,7 +81,7 @@ static Child add_queue[MAX_CHILDREN] = {{0}}, remove_queue[MAX_CHILDREN] = {{0}}
static unsigned long remove_notify[MAX_CHILDREN] = {0};
static size_t add_queue_count = 0, remove_queue_count = 0;
static struct pollfd fds[MAX_CHILDREN + EXTRA_FDS] = {{0}};
static pthread_mutex_t children_lock;
static pthread_mutex_t children_lock, talk_lock;
static bool kill_signal_received = false;
static ChildMonitor *the_monitor = NULL;
static uint8_t drain_buf[1024];
@@ -131,6 +130,10 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
PyErr_Format(PyExc_RuntimeError, "Failed to create children_lock mutex: %s", strerror(ret));
return NULL;
}
if ((ret = pthread_mutex_init(&talk_lock, NULL)) != 0) {
PyErr_Format(PyExc_RuntimeError, "Failed to create talk_lock mutex: %s", strerror(ret));
return NULL;
}
self = (ChildMonitor *)type->tp_alloc(type, 0);
if (!init_loop_data(&self->io_loop_data)) return PyErr_SetFromErrno(PyExc_OSError);
if (!install_signal_handlers(&self->io_loop_data)) return PyErr_SetFromErrno(PyExc_OSError);
@@ -152,7 +155,13 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
static void
dealloc(ChildMonitor* self) {
if (self->messages) {
for (size_t i = 0; i < self->messages_count; i++) free(self->messages[i].data);
free(self->messages); self->messages = NULL;
self->messages_count = 0; self->messages_capacity = 0;
}
pthread_mutex_destroy(&children_lock);
pthread_mutex_destroy(&talk_lock);
Py_CLEAR(self->dump_callback);
Py_CLEAR(self->death_notify);
Py_TYPE(self)->tp_free((PyObject*)self);
@@ -174,7 +183,7 @@ wakeup_io_loop(ChildMonitor *self, bool in_signal_handler) {
static void* io_loop(void *data);
static void* talk_loop(void *data);
static void send_response(int fd, const char *msg, size_t msg_sz);
static void send_response(id_type peer_id, const char *msg, size_t msg_sz);
static void wakeup_talk_loop(bool);
static bool talk_thread_started = false;
@@ -324,7 +333,6 @@ parse_input(ChildMonitor *self) {
size_t count = 0, remove_count = 0;
bool input_read = false;
monotonic_t now = monotonic();
PyObject *msg = NULL;
children_mutex(lock);
while (remove_queue_count) {
remove_queue_count--;
@@ -333,20 +341,11 @@ parse_input(ChildMonitor *self) {
FREE_CHILD(remove_queue[remove_queue_count]);
}
if (UNLIKELY(self->messages_count)) {
msg = PyTuple_New(self->messages_count);
if (msg) {
for (size_t i = 0; i < self->messages_count; i++) {
Message *m = self->messages + i;
PyTuple_SET_ITEM(msg, i, Py_BuildValue("y#i", m->data, (int)m->sz, m->fd));
free(m->data); m->data = NULL; m->sz = 0;
}
self->messages_count = 0;
} else fatal("Out of memory");
}
if (UNLIKELY(kill_signal_received)) {
global_state.terminate = true;
global_state.quit_request = IMPERATIVE_CLOSE_REQUESTED;
global_state.has_pending_closes = true;
request_tick_callback();
kill_signal_received = false;
} else {
count = self->count;
for (size_t i = 0; i < count; i++) {
@@ -355,15 +354,35 @@ parse_input(ChildMonitor *self) {
}
}
children_mutex(unlock);
if (msg) {
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(msg); i++) {
PyObject *resp = PyObject_CallMethod(global_state.boss, "peer_message_received", "O", PyTuple_GET_ITEM(PyTuple_GET_ITEM(msg, i), 0));
int peer_fd = (int)PyLong_AsLong(PyTuple_GET_ITEM(PyTuple_GET_ITEM(msg, i), 1));
if (resp && PyBytes_Check(resp)) send_response(peer_fd, PyBytes_AS_STRING(resp), PyBytes_GET_SIZE(resp));
else { send_response(peer_fd, NULL, 0); if (!resp) PyErr_Print(); }
Message *msgs = NULL;
size_t msgs_count = 0;
talk_mutex(lock);
if (UNLIKELY(self->messages_count)) {
msgs = malloc(sizeof(Message) * self->messages_count);
if (msgs) {
memcpy(msgs, self->messages, sizeof(Message) * self->messages_count);
msgs_count = self->messages_count;
}
memset(self->messages, 0, sizeof(Message) * self->messages_capacity);
self->messages_count = 0;
}
talk_mutex(unlock);
if (msgs_count) {
for (size_t i = 0; i < msgs_count; i++) {
Message *msg = msgs + i;
PyObject *resp = NULL;
if (msg->data) {
resp = PyObject_CallMethod(global_state.boss, "peer_message_received", "y#", msg->data, (int)msg->sz);
free(msg->data);
if (!resp) PyErr_Print();
}
if (resp && PyBytes_Check(resp)) send_response(msg->peer_id, PyBytes_AS_STRING(resp), PyBytes_GET_SIZE(resp));
else send_response(msg->peer_id, NULL, 0);
Py_CLEAR(resp);
}
Py_CLEAR(msg);
free(msgs); msgs = NULL;
}
while(remove_count) {
@@ -461,10 +480,10 @@ set_iutf8(int UNUSED fd, bool UNUSED on) {
static PyObject*
pyset_iutf8(ChildMonitor *self, PyObject *args) {
unsigned long window_id;
id_type window_id;
int on;
PyObject *found = Py_False;
if (!PyArg_ParseTuple(args, "kp", &window_id, &on)) return NULL;
if (!PyArg_ParseTuple(args, "Kp", &window_id, &on)) return NULL;
children_mutex(lock);
for (size_t i = 0; i < self->count; i++) {
if (children[i].id == window_id) {
@@ -500,7 +519,7 @@ collect_cursor_info(CursorRenderInfo *ans, Window *w, monotonic_t now, OSWindow
int d = monotonic_t_to_ms(OPT(cursor_blink_interval));
int n = t / d;
do_draw_cursor = n % 2 == 0 ? true : false;
monotonic_t bucket = ms_to_monotonic_t((n + 1) * d);
monotonic_t bucket = ms_to_monotonic_t((monotonic_t)(n + 1) * d);
monotonic_t delay = bucket - time_since_start_blink;
set_maximum_wait(delay);
}
@@ -523,17 +542,6 @@ change_menubar_title(PyObject *title UNUSED) {
#endif
}
static inline bool
update_window_title(Window *w, OSWindow *os_window) {
if (w->title && w->title != os_window->window_title) {
os_window->window_title = w->title;
Py_INCREF(os_window->window_title);
set_os_window_title(os_window, PyUnicode_AsUTF8(w->title));
return true;
}
return false;
}
static inline bool
prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int *active_window_id, color_type *active_window_bg, unsigned int *num_visible_windows, bool *all_windows_have_same_bg) {
#define TD os_window->tab_bar_render_data
@@ -577,7 +585,7 @@ prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int *
*active_window_id = w->id;
collect_cursor_info(&WD.screen->cursor_render_info, w, now, os_window);
if (w->cursor_visible_at_last_render != WD.screen->cursor_render_info.is_visible || w->last_cursor_x != WD.screen->cursor_render_info.x || w->last_cursor_y != WD.screen->cursor_render_info.y || w->last_cursor_shape != WD.screen->cursor_render_info.shape) needs_render = true;
update_window_title(w, os_window);
set_os_window_title_from_window(w, os_window);
*active_window_bg = window_bg;
} else WD.screen->cursor_render_info.is_visible = false;
if (send_cell_data_to_gpu(WD.vao_idx, WD.gvao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen, os_window)) needs_render = true;
@@ -625,15 +633,6 @@ render_os_window(OSWindow *os_window, monotonic_t now, unsigned int active_windo
#undef TD
}
static inline void
update_os_window_title(OSWindow *os_window) {
Tab *tab = os_window->tabs + os_window->active_tab;
if (tab->num_windows) {
Window *w = tab->windows + tab->active_window;
update_window_title(w, os_window);
}
}
static void
draw_resizing_text(OSWindow *w) {
char text[32] = {0};
@@ -649,7 +648,13 @@ draw_resizing_text(OSWindow *w) {
static inline bool
no_render_frame_received_recently(OSWindow *w, monotonic_t now, monotonic_t max_wait) {
bool ans = now - w->last_render_frame_received_at > max_wait;
if (ans) log_error("No render frame received in %.2f seconds, re-requesting at: %f", monotonic_t_to_s_double(max_wait), monotonic_t_to_s_double(now));
if (ans && global_state.debug_rendering) {
if (global_state.is_wayland) {
log_error("No render frame received in %.2f seconds", monotonic_t_to_s_double(max_wait));
} else {
log_error("No render frame received in %.2f seconds, re-requesting at: %f", monotonic_t_to_s_double(max_wait), monotonic_t_to_s_double(now));
}
}
return ans;
}
@@ -777,7 +782,7 @@ thread_write(void *x) {
if (pos < data->sz) {
log_error("Failed to write all data to STDIN of child process with error: %s", strerror(errno));
}
safe_close(data->fd);
safe_close(data->fd, __FILE__, __LINE__);
free_twd(data);
return 0;
}
@@ -794,7 +799,7 @@ cm_thread_write(PyObject UNUSED *self, PyObject *args) {
data->fd = fd;
memcpy(data->buf, buf, data->sz);
int ret = pthread_create(&thread, NULL, thread_write, data);
if (ret != 0) { safe_close(fd); free_twd(data); return PyErr_SetFromErrno(PyExc_OSError); }
if (ret != 0) { safe_close(fd, __FILE__, __LINE__); free_twd(data); return PyErr_SetFromErrno(PyExc_OSError); }
pthread_detach(thread);
Py_RETURN_NONE;
}
@@ -864,32 +869,53 @@ process_pending_resizes(monotonic_t now) {
}
static inline void
close_all_windows(void) {
for (size_t w = 0; w < global_state.num_os_windows; w++) mark_os_window_for_close(&global_state.os_windows[w], true);
close_os_window(ChildMonitor *self, OSWindow *os_window) {
destroy_os_window(os_window);
call_boss(on_os_window_closed, "Kii", os_window->id, os_window->window_width, os_window->window_height);
for (size_t t=0; t < os_window->num_tabs; t++) {
Tab *tab = os_window->tabs + t;
for (size_t w = 0; w < tab->num_windows; w++) mark_child_for_close(self, tab->windows[w].id);
}
remove_os_window(os_window->id);
}
static inline bool
process_pending_closes(ChildMonitor *self) {
global_state.has_pending_closes = false;
if (global_state.quit_request == CONFIRMABLE_CLOSE_REQUESTED) {
call_boss(quit, "");
}
if (global_state.quit_request == IMPERATIVE_CLOSE_REQUESTED) {
for (size_t w = 0; w < global_state.num_os_windows; w++) global_state.os_windows[w].close_request = IMPERATIVE_CLOSE_REQUESTED;
}
bool has_open_windows = false;
for (size_t w = global_state.num_os_windows; w > 0; w--) {
OSWindow *os_window = global_state.os_windows + w - 1;
if (should_os_window_close(os_window)) {
destroy_os_window(os_window);
call_boss(on_os_window_closed, "Kii", os_window->id, os_window->window_width, os_window->window_height);
for (size_t t=0; t < os_window->num_tabs; t++) {
Tab *tab = os_window->tabs + t;
for (size_t w = 0; w < tab->num_windows; w++) mark_child_for_close(self, tab->windows[w].id);
}
remove_os_window(os_window->id);
} else has_open_windows = true;
switch(os_window->close_request) {
case NO_CLOSE_REQUESTED:
has_open_windows = true;
break;
case CONFIRMABLE_CLOSE_REQUESTED:
os_window->close_request = CLOSE_BEING_CONFIRMED;
call_boss(confirm_os_window_close, "K", os_window->id);
if (os_window->close_request == IMPERATIVE_CLOSE_REQUESTED) {
close_os_window(self, os_window);
} else has_open_windows = true;
break;
case CLOSE_BEING_CONFIRMED:
has_open_windows = true;
break;
case IMPERATIVE_CLOSE_REQUESTED:
close_os_window(self, os_window);
break;
}
}
global_state.has_pending_closes = false;
#ifdef __APPLE__
if (!OPT(macos_quit_when_last_window_closed)) {
if (!has_open_windows && !application_quit_requested()) has_open_windows = true;
if (!has_open_windows && global_state.quit_request != IMPERATIVE_CLOSE_REQUESTED) has_open_windows = true;
}
#endif
return has_open_windows;
return !has_open_windows;
}
#ifdef __APPLE__
@@ -950,23 +976,16 @@ process_global_state(void *data) {
cocoa_pending_actions = 0;
}
#endif
if (global_state.terminate) {
global_state.terminate = false;
close_all_windows();
#ifdef __APPLE__
request_application_quit();
#endif
}
report_reaped_pids();
bool has_open_windows = true;
if (global_state.has_pending_closes) has_open_windows = process_pending_closes(self);
if (has_open_windows) {
bool should_quit = false;
if (global_state.has_pending_closes) should_quit = process_pending_closes(self);
if (should_quit) {
stop_main_loop();
} else {
if (maximum_wait >= 0) {
if (maximum_wait == 0) request_tick_callback();
else state_check_timer_enabled = true;
}
} else {
stop_main_loop();
}
update_main_loop_timer(state_check_timer, MAX(0, maximum_wait), state_check_timer_enabled);
}
@@ -1014,7 +1033,7 @@ hangup(pid_t pid) {
static inline void
cleanup_child(ssize_t i) {
safe_close(children[i].fd);
safe_close(children[i].fd, __FILE__, __LINE__);
hangup(children[i].pid);
}
@@ -1276,36 +1295,33 @@ io_loop(void *data) {
// {{{ Talk thread functions
typedef struct {
char *data;
size_t capacity, used;
id_type id;
size_t num_of_unresponded_messages_sent_to_main_thread;
bool finished_reading;
int fd;
bool finished, close_socket;
} PeerReadData;
static PeerReadData empty_prd = {.fd = -1, 0};
struct {
char *data;
size_t capacity, used, command_end;
bool finished;
} read;
struct {
char *data;
size_t capacity, used;
bool failed;
} write;
} Peer;
static id_type peer_id_counter = 0;
typedef struct {
char *data;
size_t sz, pos;
int fd;
bool finished;
} PeerWriteData;
static PeerWriteData empty_pwd = {.fd = -1, 0};
typedef struct {
size_t num_listen_fds, num_talk_fds, num_reads, num_writes, num_queued_writes;
size_t fds_capacity, reads_capacity, writes_capacity, queued_writes_capacity;
struct pollfd *fds;
PeerReadData *reads;
PeerWriteData *writes;
PeerWriteData *queued_writes;
size_t num_peers, peers_capacity;
Peer *peers;
LoopData loop_data;
pthread_mutex_t peer_lock;
} TalkData;
static TalkData talk_data = {0};
typedef struct pollfd PollFD;
#define PEER_LIMIT 256
#define nuke_socket(s) { shutdown(s, SHUT_RDWR); safe_close(s); }
#define nuke_socket(s) { shutdown(s, SHUT_RDWR); safe_close(s, __FILE__, __LINE__); }
static inline bool
accept_peer(int listen_fd, bool shutting_down) {
@@ -1315,13 +1331,12 @@ accept_peer(int listen_fd, bool shutting_down) {
if (!shutting_down) perror("accept() on talk socket failed!");
return false;
}
size_t fd_idx = talk_data.num_listen_fds + talk_data.num_talk_fds;
if (fd_idx < PEER_LIMIT && talk_data.reads_capacity < PEER_LIMIT) {
ensure_space_for(&talk_data, fds, PollFD, fd_idx + 1, fds_capacity, 8, false);
talk_data.fds[fd_idx].fd = peer; talk_data.fds[fd_idx].events = POLLIN;
ensure_space_for(&talk_data, reads, PeerReadData, talk_data.num_reads + 1, reads_capacity, 8, false);
talk_data.reads[talk_data.num_reads] = empty_prd; talk_data.reads[talk_data.num_reads++].fd = peer;
talk_data.num_talk_fds++;
if (talk_data.num_peers < PEER_LIMIT) {
ensure_space_for(&talk_data, peers, Peer, talk_data.num_peers + 8, peers_capacity, 8, false);
Peer *p = talk_data.peers + talk_data.num_peers++;
memset(p, 0, sizeof(Peer));
p->fd = peer; p->id = ++peer_id_counter;
if (!p->id) p->id = ++peer_id_counter;
} else {
log_error("Too many peers want to talk, ignoring one.");
nuke_socket(peer);
@@ -1329,111 +1344,102 @@ accept_peer(int listen_fd, bool shutting_down) {
return true;
}
static inline bool
read_from_peer(ChildMonitor *self, int s) {
bool read_finished = false;
for (size_t i = 0; i < talk_data.num_reads; i++) {
PeerReadData *rd = talk_data.reads + i;
#define failed(msg) { read_finished = true; log_error("%s", msg); rd->finished = true; rd->close_socket = true; break; }
if (rd->fd == s) {
if (rd->used >= rd->capacity) {
if (rd->capacity >= 1024 * 1024) failed("Ignoring too large message from peer");
rd->capacity = MAX(8192u, rd->capacity * 2);
rd->data = realloc(rd->data, rd->capacity);
if (!rd->data) failed("Out of memory");
}
ssize_t n = recv(s, rd->data + rd->used, rd->capacity - rd->used, 0);
if (n == 0) {
read_finished = true; rd->finished = true;
children_mutex(lock);
ensure_space_for(self, messages, Message, self->messages_count + 1, messages_capacity, 16, true);
Message *m = self->messages + self->messages_count++;
m->data = rd->data; rd->data = NULL; m->sz = rd->used; m->fd = s;
children_mutex(unlock);
wakeup_main_loop();
} else if (n < 0) {
if (errno != EINTR) {
perror("Error reading from talk peer");
failed("");
}
} else rd->used += n;
break;
static inline void
free_peer(Peer *peer) {
free(peer->read.data); peer->read.data = NULL;
free(peer->write.data); peer->write.data = NULL;
if (peer->fd > -1) { nuke_socket(peer->fd); peer->fd = -1; }
}
#define KITTY_CMD_PREFIX "\x1bP@kitty-cmd{"
static inline void
queue_peer_message(ChildMonitor *self, Peer *peer) {
talk_mutex(lock);
ensure_space_for(self, messages, Message, self->messages_count + 16, messages_capacity, 16, true);
Message *m = self->messages + self->messages_count++;
memset(m, 0, sizeof(Message));
if (peer->read.used) {
m->data = malloc(peer->read.used);
if (m->data) {
memcpy(m->data, peer->read.data, peer->read.used);
m->sz = peer->read.used;
}
}
#undef failed
return read_finished;
m->peer_id = peer->id;
peer->num_of_unresponded_messages_sent_to_main_thread++;
talk_mutex(unlock);
wakeup_main_loop();
}
static inline bool
write_to_peer(int fd) {
bool write_finished = false;
for (size_t i = 0; i < talk_data.num_writes; i++) {
PeerWriteData *wd = talk_data.writes + i;
#define failed(msg) { write_finished = true; log_error("%s", msg); wd->finished = true; break; }
if (wd->fd == fd) {
ssize_t n = send(fd, wd->data + wd->pos, wd->sz - wd->pos, MSG_NOSIGNAL);
if (n == 0) { failed("send() to peer failed to send any data"); }
else if (n < 0) {
if (errno != EINTR) { perror("write() to peer socket failed with error"); failed(""); }
} else {
wd->pos += n;
if (wd->pos >= wd->sz) { write_finished = true; wd->finished = true; }
has_complete_peer_command(Peer *peer) {
peer->read.command_end = 0;
if (peer->read.used > sizeof(KITTY_CMD_PREFIX) && memcmp(peer->read.data, KITTY_CMD_PREFIX, sizeof(KITTY_CMD_PREFIX)-1) == 0) {
for (size_t i = sizeof(KITTY_CMD_PREFIX)-1; i < peer->read.used - 1; i++) {
if (peer->read.data[i] == 0x1b && peer->read.data[i+1] == '\\') {
peer->read.command_end = i + 2;
break;
}
break;
}
}
return peer->read.command_end ? true : false;
}
static inline void
dispatch_peer_command(ChildMonitor *self, Peer *peer) {
if (peer->read.command_end) {
size_t used = peer->read.used;
peer->read.used = peer->read.command_end;
queue_peer_message(self, peer);
peer->read.used = used;
if (peer->read.used > peer->read.command_end) {
peer->read.used -= peer->read.command_end;
memmove(peer->read.data, peer->read.data + peer->read.command_end, peer->read.used);
} else peer->read.used = 0;
peer->read.command_end = 0;
}
}
static inline void
read_from_peer(ChildMonitor *self, Peer *peer) {
#define failed(msg) { log_error("Reading from peer failed: %s", msg); shutdown(peer->fd, SHUT_RD); peer->read.finished = true; return; }
if (peer->read.used >= peer->read.capacity) {
if (peer->read.capacity >= 64 * 1024) failed("Ignoring too large message from peer");
peer->read.capacity = MAX(8192u, peer->read.capacity * 2);
peer->read.data = realloc(peer->read.data, peer->read.capacity);
if (!peer->read.data) failed("Out of memory");
}
ssize_t n = recv(peer->fd, peer->read.data + peer->read.used, peer->read.capacity - peer->read.used, 0);
if (n == 0) {
peer->read.finished = true;
shutdown(peer->fd, SHUT_RD);
while (has_complete_peer_command(peer)) dispatch_peer_command(self, peer);
queue_peer_message(self, peer);
free(peer->read.data); peer->read.data = NULL;
peer->read.used = 0; peer->read.capacity = 0;
} else if (n < 0) {
if (errno != EINTR) failed(strerror(errno));
} else {
peer->read.used += n;
while (has_complete_peer_command(peer)) dispatch_peer_command(self, peer);
}
#undef failed
return write_finished;
}
static inline void
remove_poll_fd(int fd) {
size_t count = talk_data.num_talk_fds + talk_data.num_listen_fds;
for (size_t i = talk_data.num_listen_fds; i < count; i++) {
struct pollfd *pfd = talk_data.fds + i;
if (pfd->fd == fd) {
size_t num_to_right = count - 1 - i;
if (num_to_right) memmove(talk_data.fds + i, talk_data.fds + i + 1, num_to_right * sizeof(struct pollfd));
talk_data.num_talk_fds--;
break;
}
}
}
static inline void
prune_finished_reads(void) {
if (!talk_data.num_reads) return;
for (ssize_t i = talk_data.num_reads - 1; i >= 0; i--) {
PeerReadData *rd = talk_data.reads + i;
if (rd->finished) {
remove_poll_fd(rd->fd);
if (rd->close_socket) { nuke_socket(rd->fd); }
else shutdown(rd->fd, SHUT_RD);
free(rd->data);
ssize_t num_to_right = talk_data.num_reads - 1 - i;
if (num_to_right > 0) memmove(talk_data.reads + i, talk_data.reads + i + 1, num_to_right * sizeof(PeerReadData));
else talk_data.reads[i] = empty_prd;
talk_data.num_reads--;
}
}
}
static inline void
prune_finished_writes(void) {
if (!talk_data.num_writes) return;
for (ssize_t i = talk_data.num_writes - 1; i >= 0; i--) {
PeerWriteData *wd = talk_data.writes + i;
if (wd->finished) {
remove_poll_fd(wd->fd);
shutdown(wd->fd, SHUT_WR); safe_close(wd->fd);
free(wd->data);
ssize_t num_to_right = talk_data.num_writes - 1 - i;
if (num_to_right > 0) memmove(talk_data.writes + i, talk_data.writes + i + 1, num_to_right * sizeof(PeerWriteData));
else talk_data.writes[i] = empty_pwd;
talk_data.num_writes--;
}
write_to_peer(Peer *peer) {
talk_mutex(lock);
ssize_t n = send(peer->fd, peer->write.data, peer->write.used, MSG_NOSIGNAL);
if (n == 0) { log_error("send() to peer failed to send any data"); peer->write.used = 0; peer->write.failed = true; }
else if (n < 0) {
if (errno != EINTR) { log_error("write() to peer socket failed with error: %s", strerror(errno)); peer->write.used = 0; peer->write.failed = true; }
} else {
if ((size_t)n > peer->write.used) memmove(peer->write.data, peer->write.data + n, peer->write.used - n);
peer->write.used -= n;
}
talk_mutex(unlock);
}
static void
@@ -1441,92 +1447,107 @@ wakeup_talk_loop(bool in_signal_handler) {
if (talk_thread_started) wakeup_loop(&talk_data.loop_data, in_signal_handler, "talk_loop");
}
static inline void
move_queued_writes(void) {
while (talk_data.num_queued_writes) {
PeerWriteData *src = talk_data.queued_writes + --talk_data.num_queued_writes;
size_t fd_idx = talk_data.num_listen_fds + talk_data.num_talk_fds;
if (fd_idx < PEER_LIMIT && talk_data.num_writes < PEER_LIMIT) {
ensure_space_for(&talk_data, fds, PollFD, fd_idx + 1, fds_capacity, 8, false);
talk_data.fds[fd_idx].fd = src->fd; talk_data.fds[fd_idx].events = POLLOUT;
ensure_space_for(&talk_data, writes, PeerWriteData, talk_data.num_writes + 1, writes_capacity, 8, false);
talk_data.writes[talk_data.num_writes++] = *src;
talk_data.num_talk_fds++;
} else {
log_error("Cannot send response to peer, too many peers");
free(src->data); nuke_socket(src->fd);
prune_peers(void) {
for (size_t i = 0; i < talk_data.num_peers; i++) {
size_t idx = talk_data.num_peers - 1 - i;
Peer *p = talk_data.peers + idx;
if (p->read.finished && !p->num_of_unresponded_messages_sent_to_main_thread && !p->write.used) {
free_peer(p);
remove_i_from_array(talk_data.peers, idx, talk_data.num_peers);
}
*src = empty_pwd;
}
}
static void*
talk_loop(void *data) {
// The talk thread loop
ChildMonitor *self = (ChildMonitor*)data;
set_thread_name("KittyPeerMon");
if ((pthread_mutex_init(&talk_data.peer_lock, NULL)) != 0) { perror("Failed to create peer mutex"); return 0; }
if (!init_loop_data(&talk_data.loop_data)) { log_error("Failed to create wakeup fd for talk thread with error: %s", strerror(errno)); }
ensure_space_for(&talk_data, fds, PollFD, 8, fds_capacity, 8, false);
PollFD fds[PEER_LIMIT + 8] = {0};
size_t num_listen_fds = 0, num_peer_fds = 0;
#define add_listener(which) \
if (self->which > -1) { \
talk_data.fds[talk_data.num_listen_fds].fd = self->which; talk_data.fds[talk_data.num_listen_fds++].events = POLLIN; \
fds[num_listen_fds].fd = self->which; fds[num_listen_fds++].events = POLLIN; \
}
add_listener(talk_fd); add_listener(listen_fd);
#undef add_listener
talk_data.fds[talk_data.num_listen_fds].fd = talk_data.loop_data.wakeup_read_fd; talk_data.fds[talk_data.num_listen_fds++].events = POLLIN;
fds[num_listen_fds].fd = talk_data.loop_data.wakeup_read_fd; fds[num_listen_fds++].events = POLLIN;
while (LIKELY(!self->shutting_down)) {
for (size_t i = 0; i < talk_data.num_listen_fds + talk_data.num_talk_fds; i++) { talk_data.fds[i].revents = 0; }
int ret = poll(talk_data.fds, talk_data.num_listen_fds + talk_data.num_talk_fds, -1);
num_peer_fds = 0;
if (talk_data.num_peers > 0) {
talk_mutex(lock);
prune_peers();
for (size_t i = 0; i < talk_data.num_peers; i++) {
Peer *p = talk_data.peers + i;
if (!p->read.finished || p->write.used) {
fds[num_listen_fds + num_peer_fds].fd = p->fd;
fds[num_listen_fds + num_peer_fds].revents = 0;
int flags = 0;
if (!p->read.finished) flags |= POLLIN;
if (p->write.used) flags |= POLLOUT;
fds[num_listen_fds + num_peer_fds++].events = flags;
}
}
talk_mutex(unlock);
}
for (size_t i = 0; i < num_listen_fds; i++) fds[i].revents = 0;
int ret = poll(fds, num_listen_fds + num_peer_fds, -1);
if (ret > 0) {
bool has_finished_reads = false, has_finished_writes = false;
for (size_t i = 0; i < talk_data.num_listen_fds - 1; i++) {
if (talk_data.fds[i].revents & POLLIN) {if (!accept_peer(talk_data.fds[i].fd, self->shutting_down)) goto end; }
for (size_t i = 0; i < num_listen_fds - 1; i++) {
if (fds[i].revents & POLLIN) {
if (!accept_peer(fds[i].fd, self->shutting_down)) goto end;
}
}
if (talk_data.fds[talk_data.num_listen_fds - 1].revents & POLLIN) drain_fd(talk_data.fds[talk_data.num_listen_fds - 1].fd); // wakeup
for (size_t i = talk_data.num_listen_fds; i < talk_data.num_talk_fds + talk_data.num_listen_fds; i++) {
if (talk_data.fds[i].revents & (POLLIN | POLLHUP)) { if (read_from_peer(self, talk_data.fds[i].fd)) has_finished_reads = true; }
if (talk_data.fds[i].revents & POLLOUT) { if (write_to_peer(talk_data.fds[i].fd)) has_finished_writes = true; }
if (fds[num_listen_fds - 1].revents & POLLIN) {
drain_fd(fds[num_listen_fds - 1].fd); // wakeup
}
for (size_t i = num_listen_fds, k = 0; i < num_peer_fds + num_listen_fds; i++, k++) {
Peer *p = talk_data.peers + k;
if (fds[i].revents & (POLLIN | POLLHUP)) read_from_peer(self, p);
if (fds[i].revents & POLLOUT) write_to_peer(p);
if (fds[i].revents & POLLNVAL) {
p->read.finished = true;
p->write.failed = true; p->write.used = 0;
}
}
if (has_finished_reads) prune_finished_reads();
if (has_finished_writes) prune_finished_writes();
peer_mutex(lock);
if (talk_data.num_queued_writes) move_queued_writes();
peer_mutex(unlock);
} else if (ret < 0) { if (errno != EAGAIN && errno != EINTR) perror("poll() on talk fds failed"); }
}
end:
free_loop_data(&talk_data.loop_data);
free(talk_data.fds); free(talk_data.reads); free(talk_data.writes); free(talk_data.queued_writes);
for (size_t i = 0; i < talk_data.num_peers; i++) free_peer(talk_data.peers + i);
free(talk_data.peers);
return 0;
}
static inline bool
add_peer_writer(int fd, const char* msg, size_t msg_sz) {
bool ok = false;
peer_mutex(lock);
if (talk_data.num_queued_writes < PEER_LIMIT) {
ensure_space_for(&talk_data, queued_writes, PeerWriteData, talk_data.num_queued_writes + 1, queued_writes_capacity, 8, false);
talk_data.queued_writes[talk_data.num_queued_writes] = empty_pwd;
talk_data.queued_writes[talk_data.num_queued_writes].data = malloc(msg_sz);
if (talk_data.queued_writes[talk_data.num_queued_writes].data) {
memcpy(talk_data.queued_writes[talk_data.num_queued_writes].data, msg, msg_sz);
talk_data.queued_writes[talk_data.num_queued_writes].sz = msg_sz;
talk_data.queued_writes[talk_data.num_queued_writes++].fd = fd;
ok = true;
}
} else log_error("Cannot send response to peer, too many peers");
peer_mutex(unlock);
return ok;
}
static void
send_response(int fd, const char *msg, size_t msg_sz) {
if (msg == NULL) { shutdown(fd, SHUT_WR); safe_close(fd); return; }
if (!add_peer_writer(fd, msg, msg_sz)) { shutdown(fd, SHUT_WR); safe_close(fd); }
else wakeup_talk_loop(false);
send_response(id_type peer_id, const char *msg, size_t msg_sz) {
bool wakeup = false;
talk_mutex(lock);
for (size_t i = 0; i < talk_data.num_peers; i++) {
Peer *peer = talk_data.peers + i;
if (peer->id == peer_id) {
if (peer->num_of_unresponded_messages_sent_to_main_thread) peer->num_of_unresponded_messages_sent_to_main_thread--;
if (!peer->write.failed) {
if (peer->write.capacity - peer->write.used < msg_sz) {
void *data = realloc(peer->write.data, peer->write.capacity + msg_sz);
if (data) {
peer->write.data = data;
peer->write.capacity += msg_sz;
} else fatal("Out of memory");
}
memcpy(peer->write.data + peer->write.used, msg, msg_sz);
peer->write.used += msg_sz;
}
wakeup = true;
break;
}
}
talk_mutex(unlock);
if (wakeup) wakeup_talk_loop(false);
}
// }}}
@@ -1541,6 +1562,7 @@ static PyMethodDef methods[] = {
METHOD(main_loop, METH_NOARGS)
METHOD(mark_for_close, METH_VARARGS)
METHOD(resize_pty, METH_VARARGS)
{"set_iutf8_winid", (PyCFunction)pyset_iutf8, METH_VARARGS, ""},
{NULL} /* Sentinel */
};
@@ -1582,7 +1604,6 @@ static PyMethodDef module_methods[] = {
{"add_timer", (PyCFunction)add_python_timer, METH_VARARGS, ""},
{"remove_timer", (PyCFunction)remove_python_timer, METH_VARARGS, ""},
METHODB(monitor_pid, METH_VARARGS),
{"set_iutf8_winid", (PyCFunction)pyset_iutf8, METH_VARARGS, ""},
METHODB(cocoa_set_menubar_title, METH_VARARGS),
{NULL} /* Sentinel */
};

View File

@@ -12,6 +12,8 @@
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <termios.h>
static inline char**
serialize_string_tuple(PyObject *src) {
@@ -93,32 +95,32 @@ spawn(PyObject *self UNUSED, PyObject *args) {
// Establish the controlling terminal (see man 7 credentials)
int tfd = open(name, O_RDWR);
if (tfd == -1) exit_on_err("Failed to open controlling terminal");
#ifdef TIOCSTTY
#ifdef TIOCSCTTY
// On BSD open() does not establish the controlling terminal
if (ioctl(tfd, TIOCSCTTY, 0) == -1) exit_on_err("Failed to set controlling terminal with TIOCSCTTY");
#endif
safe_close(tfd);
safe_close(tfd, __FILE__, __LINE__);
// Redirect stdin/stdout/stderr to the pty
if (dup2(slave, 1) == -1) exit_on_err("dup2() failed for fd number 1");
if (dup2(slave, 2) == -1) exit_on_err("dup2() failed for fd number 2");
if (stdin_read_fd > -1) {
if (dup2(stdin_read_fd, 0) == -1) exit_on_err("dup2() failed for fd number 0");
safe_close(stdin_read_fd);
safe_close(stdin_write_fd);
safe_close(stdin_read_fd, __FILE__, __LINE__);
safe_close(stdin_write_fd, __FILE__, __LINE__);
} else {
if (dup2(slave, 0) == -1) exit_on_err("dup2() failed for fd number 0");
}
safe_close(slave);
safe_close(master);
safe_close(slave, __FILE__, __LINE__);
safe_close(master, __FILE__, __LINE__);
// Wait for READY_SIGNAL which indicates kitty has setup the screen object
safe_close(ready_write_fd);
safe_close(ready_write_fd, __FILE__, __LINE__);
wait_for_terminal_ready(ready_read_fd);
safe_close(ready_read_fd);
safe_close(ready_read_fd, __FILE__, __LINE__);
// Close any extra fds inherited from parent
for (int c = 3; c < 201; c++) safe_close(c);
for (int c = 3; c < 201; c++) safe_close(c, __FILE__, __LINE__);
environ = env;
// for some reason SIGPIPE is set to SIG_IGN, so reset it, needed by bash,

View File

@@ -210,6 +210,8 @@ class Child:
env: Optional[Dict[str, str]] = getattr(self, '_final_env', None)
if env is None:
env = self._final_env = default_env().copy()
if is_macos and env.get('LC_CTYPE') == 'UTF-8' and not sys._xoptions.get('lc_ctype_before_python'):
del env['LC_CTYPE']
env.update(self.env)
env['TERM'] = self.opts.term
env['COLORTERM'] = 'truecolor'

View File

@@ -289,9 +289,9 @@ class PrintHelpForSeq:
leading_indent = indent
j = '\n' + (' ' * indent)
lines: List[str] = []
for l in text.splitlines():
if l:
lines.extend(wrap(l, limit=linesz - indent))
for ln in text.splitlines():
if ln:
lines.extend(wrap(ln, limit=linesz - indent))
else:
lines.append('')
a((' ' * leading_indent) + j.join(lines))
@@ -409,7 +409,10 @@ def as_type_stub(seq: OptionSpecSeq, disabled: OptionSpecSeq, class_name: str, e
elif otype == 'list':
t = 'typing.Sequence[str]'
elif otype in ('choice', 'choices'):
t = 'str'
if opt['choices']:
t = 'typing.Literal[{}]'.format(','.join(f'{x!r}' for x in opt['choices']))
else:
t = 'str'
elif otype.startswith('bool-'):
t = 'bool'
else:
@@ -624,12 +627,15 @@ Tell kitty to listen on the specified address for control
messages. For example, :option:`{appname} --listen-on`=unix:/tmp/mykitty or
:option:`{appname} --listen-on`=tcp:localhost:12345. On Linux systems, you can
also use abstract UNIX sockets, not associated with a file, like this:
:option:`{appname} --listen-on`=unix:@mykitty. To control kitty, you can send
:option:`{appname} --listen-on`=unix:@mykitty. Environment variables
in the setting are expanded and relative paths are resolved with
respect to the temporary directory. To control kitty, you can send
it commands with :italic:`kitty @` using the :option:`kitty @ --to` option to
specify this address. This option will be ignored, unless you set
:opt:`allow_remote_control` to yes in :file:`kitty.conf`. Note that if you run
:italic:`kitty @` within a kitty window, there is no need to specify the :italic:`--to`
option as it is read automatically from the environment.
option as it is read automatically from the environment. For UNIX sockets, this
can also be specified in :file:`kitty.conf`.
--start-as
@@ -662,10 +668,11 @@ can open a new kitty window to replay the commands with::
Path to file in which to store the raw bytes received from the child process
--debug-gl
--debug-rendering --debug-gl
type=bool-set
Debug OpenGL commands. This will cause all OpenGL calls to check for errors
instead of ignoring them. Useful when debugging rendering problems
Debug rendering commands. This will cause all OpenGL calls to check for errors
instead of ignoring them. Also prints out miscellaneous debug information.
Useful when debugging rendering problems
--debug-keyboard

View File

@@ -34,6 +34,14 @@ def screen_bell() -> None:
pass
def screen_normal_keypad_mode() -> None:
write('\x1b>')
def screen_alternate_keypad_mode() -> None:
write('\x1b=')
def screen_cursor_position(y: int, x: int) -> None:
write(CSI + '%s;%sH' % (y, x))
@@ -60,8 +68,8 @@ def screen_cursor_to_column(c: int) -> None:
write(CSI + '%dG' % c)
def screen_cursor_to_line(l: int) -> None:
write(CSI + '%dd' % l)
def screen_cursor_to_line(ln: int) -> None:
write(CSI + '%dd' % ln)
def screen_set_mode(x: int, private: bool) -> None:
@@ -80,6 +88,18 @@ def screen_indexn(n: int) -> None:
write(CSI + '%dS' % n)
def screen_delete_characters(count: int) -> None:
write(CSI + '%dP' % count)
def screen_insert_characters(count: int) -> None:
write(CSI + '%d@' % count)
def screen_scroll(count: int) -> None:
write(CSI + '%dS' % count)
def screen_erase_in_display(how: int, private: bool) -> None:
write(CSI + ('?' if private else '') + str(how) + 'J')
@@ -108,6 +128,10 @@ def screen_linefeed() -> None:
write('\n')
def screen_tab() -> None:
write('\t')
def screen_backspace() -> None:
write('\x08')

View File

@@ -14,6 +14,7 @@
// Needed for _NSGetProgname
#include <crt_externs.h>
#include <objc/runtime.h>
#include <xlocale.h>
#if (MAC_OS_X_VERSION_MAX_ALLOWED < 101200)
#define NSWindowStyleMaskResizable NSResizableWindowMask
@@ -389,6 +390,12 @@ cocoa_focus_window(void *w) {
[window makeKeyWindow];
}
long
cocoa_window_number(void *w) {
NSWindow *window = (NSWindow*)w;
return [window windowNumber];
}
size_t
cocoa_get_workspace_ids(void *w, size_t *workspace_ids, size_t array_sz) {
NSWindow *window = (NSWindow*)w;
@@ -419,7 +426,13 @@ cocoa_get_lang(PyObject UNUSED *self) {
locale = [[NSLocale currentLocale] localeIdentifier];
}
if (!locale) { Py_RETURN_NONE; }
return Py_BuildValue("s", [locale UTF8String]);
// Make sure the locale value is valid, that is it can be used
// to construct an actual locale
const char* locale_utf8 = [locale UTF8String];
locale_t test_locale = newlocale(LC_ALL_MASK, locale_utf8, NULL);
if (!test_locale) { Py_RETURN_NONE; }
freelocale(test_locale);
return Py_BuildValue("s", locale_utf8);
} // autoreleasepool
}
@@ -524,6 +537,11 @@ cocoa_hide_titlebar(void *w)
} // autoreleasepool
}
void
cocoa_system_beep(void) {
NSBeep();
}
static PyMethodDef module_methods[] = {
{"cocoa_get_lang", (PyCFunction)cocoa_get_lang, METH_NOARGS, ""},
{"cocoa_set_new_window_trigger", (PyCFunction)cocoa_set_new_window_trigger, METH_VARARGS, ""},

View File

@@ -29,7 +29,7 @@ your shell's rc file.
2) Add an input_parser function, this takes the input from
the shell for the text being completed and returns a list of words
alongwith a boolean indicating if we are on a new word or not. This
and a boolean indicating if we are on a new word or not. This
is passed to kitty's completion system.
3) An output_serializer function that is responsible for
@@ -47,12 +47,32 @@ def debug(*a: Any, **kw: Any) -> None:
print(*a, **kw)
class Delegate:
def __init__(self, words: Sequence[str] = (), pos: int = -1, new_word: bool = False):
self.words: Sequence[str] = words
self.pos = pos
self.num_of_unknown_args = len(words) - pos
self.new_word = new_word
def __bool__(self) -> bool:
return self.pos > -1 and self.num_of_unknown_args > 0
@property
def precommand(self) -> str:
try:
return self.words[self.pos]
except IndexError:
return ''
class Completions:
def __init__(self) -> None:
self.match_groups: Dict[str, MathGroup] = {}
self.no_space_groups: Set[str] = set()
self.files_groups: Set[str] = set()
self.delegate: Delegate = Delegate()
@lru_cache(maxsize=2)
@@ -153,6 +173,15 @@ def zsh_output_serializer(ans: Completions) -> str:
for word in matches:
cmd.append(shlex.quote(word))
lines.append(' '.join(cmd) + ';')
if ans.delegate:
if ans.delegate.num_of_unknown_args == 1 and not ans.delegate.new_word:
lines.append('_command_names -e')
elif ans.delegate.precommand:
for i in range(ans.delegate.pos + 1):
lines.append('shift words')
lines.append('(( CURRENT-- ))')
lines.append(f'_normal -p "{ans.delegate.precommand}"')
# debug('\n'.join(lines))
return '\n'.join(lines)
@@ -188,6 +217,8 @@ def completions_for_first_word(ans: Completions, prefix: str, entry_points: Iter
list(entry_points) + cmds + ['+' + k for k in namespaced_entry_points]
if not prefix or k.startswith(prefix)
}
if prefix:
ans.delegate = Delegate([prefix], 0)
def kitty_cli_opts(ans: Completions, prefix: Optional[str] = None) -> None:
@@ -201,9 +232,11 @@ def kitty_cli_opts(ans: Completions, prefix: Optional[str] = None) -> None:
ans.match_groups['Options'] = matches
def complete_kitty_cli_arg(ans: Completions, opt: Optional[OptionDict], prefix: str) -> None:
def complete_kitty_cli_arg(ans: Completions, opt: Optional[OptionDict], prefix: str, unknown_args: Delegate) -> None:
prefix = prefix or ''
if not opt:
if unknown_args.num_of_unknown_args > 0:
ans.delegate = unknown_args
return
dest = opt['dest']
if dest == 'override':
@@ -236,24 +269,24 @@ def complete_kitty_cli_arg(ans: Completions, opt: Optional[OptionDict], prefix:
complete_files_and_dirs(ans, prefix[len('unix:'):], files_group_name='UNIX sockets', add_prefix='unix:')
CompleteArgsFunc = Callable[[Completions, Optional[OptionDict], str], None]
CompleteArgsFunc = Callable[[Completions, Optional[OptionDict], str, Delegate], None]
def complete_alias_map(
ans: Completions,
words: Sequence[str],
new_word: str,
new_word: bool,
option_map: Dict[str, OptionDict],
complete_args: Optional[CompleteArgsFunc] = None
) -> None:
expecting_arg = False
opt: Optional[OptionDict] = None
last_word = words[-1] if words else ''
for w in words:
for i, w in enumerate(words):
if expecting_arg:
if w is last_word and not new_word:
if opt is not None and complete_args is not None:
complete_args(ans, opt, w)
complete_args(ans, opt, w, Delegate())
return
expecting_arg = False
continue
@@ -263,27 +296,27 @@ def complete_alias_map(
ans.match_groups['Options'] = {k: opt['help'] for k, opt in option_map.items() if k.startswith(last_word)}
else:
if complete_args is not None:
complete_args(ans, None, last_word)
complete_args(ans, None, last_word, Delegate(words, i))
return
if opt is None:
if complete_args is not None:
complete_args(ans, None, '' if new_word else last_word)
complete_args(ans, None, '' if new_word else last_word, Delegate(words, i, new_word))
return # some non-option word encountered
expecting_arg = not opt.get('type', '').startswith('bool-')
if expecting_arg:
if opt is not None and complete_args is not None:
complete_args(ans, opt, '' if new_word else last_word)
complete_args(ans, opt, '' if new_word else last_word, Delegate())
else:
prefix = '' if new_word else last_word
if complete_args is not None:
complete_args(ans, None, prefix)
complete_args(ans, None, prefix, Delegate())
ans.match_groups['Options'] = {k: opt['help'] for k, opt in option_map.items() if k.startswith(prefix)}
def complete_cli(
ans: Completions,
words: Sequence[str],
new_word: str,
new_word: bool,
seq: OptionSpecSeq,
complete_args: Optional[CompleteArgsFunc] = None
) -> None:
@@ -295,7 +328,7 @@ def complete_cli(
complete_alias_map(ans, words, new_word, option_map, complete_args)
def complete_remote_command(ans: Completions, cmd_name: str, words: Sequence[str], new_word: str) -> None:
def complete_remote_command(ans: Completions, cmd_name: str, words: Sequence[str], new_word: bool) -> None:
aliases, alias_map = options_for_cmd(cmd_name)
if not alias_map:
return
@@ -358,7 +391,7 @@ def complete_files_and_dirs(
ans.files_groups.add(files_group_name)
def complete_icat_args(ans: Completions, opt: Optional[OptionDict], prefix: str) -> None:
def complete_icat_args(ans: Completions, opt: Optional[OptionDict], prefix: str, unknown_args: Delegate) -> None:
from mimetypes import guess_type
def icat_file_predicate(filename: str) -> bool:
@@ -374,7 +407,7 @@ def complete_icat_args(ans: Completions, opt: Optional[OptionDict], prefix: str)
def remote_files_completer(spec: Tuple[str, Tuple[str, ...]]) -> CompleteArgsFunc:
name, matchers = spec
def complete_files_map(ans: Completions, opt: Optional[OptionDict], prefix: str) -> None:
def complete_files_map(ans: Completions, opt: Optional[OptionDict], prefix: str, unknown_args: Delegate) -> None:
def predicate(filename: str) -> bool:
for m in matchers:
@@ -392,14 +425,14 @@ def config_file_predicate(filename: str) -> bool:
return filename.endswith('.conf')
def complete_diff_args(ans: Completions, opt: Optional[OptionDict], prefix: str) -> None:
def complete_diff_args(ans: Completions, opt: Optional[OptionDict], prefix: str, unknown_args: Delegate) -> None:
if opt is None:
complete_files_and_dirs(ans, prefix, 'Files')
elif opt['dest'] == 'config':
complete_files_and_dirs(ans, prefix, 'Config Files', config_file_predicate)
def complete_kitten(ans: Completions, kitten: str, words: Sequence[str], new_word: str) -> None:
def complete_kitten(ans: Completions, kitten: str, words: Sequence[str], new_word: bool) -> None:
try:
cd = get_kitten_cli_docs(kitten)
except SystemExit:
@@ -419,7 +452,7 @@ def complete_kitten(ans: Completions, kitten: str, words: Sequence[str], new_wor
}.get(kitten))
def find_completions(words: Sequence[str], new_word: str, entry_points: Iterable[str], namespaced_entry_points: Iterable[str]) -> Completions:
def find_completions(words: Sequence[str], new_word: bool, entry_points: Iterable[str], namespaced_entry_points: Iterable[str]) -> Completions:
ans = Completions()
if not words or words[0] != 'kitty':
return ans

View File

@@ -9,7 +9,7 @@ from typing import (
Set, Tuple, Union, get_type_hints
)
from .utils import to_bool
from .utils import Choice, to_bool
def to_string(x: str) -> str:
@@ -56,6 +56,8 @@ class Option:
if type(self.option_type) is type:
return type_name(self.option_type)
if isinstance(self.option_type, Choice):
return 'typing.Literal[{}]'.format(','.join(f'{x!r}' for x in self.option_type.all_choices))
th = get_type_hints(self.option_type)
try:
rettype = th['return']

View File

@@ -68,17 +68,21 @@ def python_string(text: str) -> str:
return ans
def choices(*choices: str) -> Callable[[str], str]:
defval = choices[0]
uc = frozenset(choices)
class Choice:
def choice(x: str) -> str:
def __init__(self, choices: Sequence[str]):
self.defval = choices[0]
self.all_choices = frozenset(choices)
def __call__(self, x: str) -> str:
x = x.lower()
if x not in uc:
x = defval
if x not in self.all_choices:
x = self.defval
return x
return choice
def choices(*choices: str) -> Choice:
return Choice(choices)
def parse_line(

View File

@@ -9,8 +9,8 @@ import sys
from contextlib import contextmanager, suppress
from functools import partial
from typing import (
Any, Callable, Dict, Generator, Iterable, List, Match, NamedTuple,
Optional, Sequence, Set, Tuple, Type, Union
Any, Callable, Dict, Generator, Iterable, List, NamedTuple, Optional,
Sequence, Set, Tuple, Type, Union
)
from . import fast_data_types as defines
@@ -24,7 +24,7 @@ from .constants import cache_dir, defconf, is_macos
from .key_names import get_key_name_lookup, key_name_aliases
from .options_stub import Options as OptionsStub
from .typing import TypedDict
from .utils import log_error
from .utils import expandvars, log_error
KeySpec = Tuple[int, bool, int]
KeyMap = Dict[KeySpec, 'KeyAction']
@@ -140,6 +140,20 @@ def float_parse(func: str, rest: str) -> FuncArgsType:
return func, (float(rest),)
@func_with_args('signal_child')
def signal_child_parse(func: str, rest: str) -> FuncArgsType:
import signal
signals = []
for q in rest.split():
try:
signum = getattr(signal, q.upper())
except AttributeError:
log_error(f'Unknown signal: {rest} ignoring')
else:
signals.append(signum)
return func, tuple(signals)
@func_with_args('change_font_size')
def parse_change_font_size(func: str, rest: str) -> Tuple[str, Tuple[bool, Optional[str], float]]:
vals = rest.strip().split(maxsplit=1)
@@ -240,6 +254,15 @@ def set_colors(func: str, rest: str) -> FuncArgsType:
return func, r
@func_with_args('remote_control')
def remote_control(func: str, rest: str) -> FuncArgsType:
import shlex
r = shlex.split(rest)
if len(r) < 1:
log_error('Too few arguments to remote_control function')
return func, r
@func_with_args('nth_window')
def nth_window(func: str, rest: str) -> FuncArgsType:
try:
@@ -302,7 +325,7 @@ def parse_marker_spec(ftype: str, parts: Sequence[str]) -> Tuple[str, Union[str,
def toggle_marker(func: str, rest: str) -> FuncArgsType:
parts = rest.split(maxsplit=1)
if len(parts) != 2:
raise ValueError('{} if not a valid marker specification'.format(rest))
raise ValueError('{} is not a valid marker specification'.format(rest))
ftype, spec = parts
parts = spec.split()
return func, list(parse_marker_spec(ftype, parts))
@@ -576,20 +599,6 @@ def handle_deprecated_macos_show_window_title_in_menubar_alias(key: str, val: st
ans['macos_show_window_title_in'] = macos_show_window_title_in
def expandvars(val: str, env: Dict[str, str]) -> str:
def sub(m: Match) -> str:
key = m.group(1)
result = env.get(key)
if result is None:
result = os.environ.get(key)
if result is None:
result = m.group()
return result
return re.sub(r'\$\{(\S+?)\}', sub, val)
@special_handler
def handle_env(key: str, val: str, ans: Dict[str, Any]) -> None:
key, val = val.partition('=')[::2]
@@ -709,40 +718,6 @@ def cached_values_for(name: str) -> Generator[Dict, None, None]:
err))
def initial_window_size_func(opts: OptionsStub, cached_values: Dict) -> Callable[[int, int, float, float, float, float], Tuple[int, int]]:
if 'window-size' in cached_values and opts.remember_window_size:
ws = cached_values['window-size']
try:
w, h = map(int, ws)
def initial_window_size(*a: Any) -> Tuple[int, int]:
return w, h
return initial_window_size
except Exception:
log_error('Invalid cached window size, ignoring')
w, w_unit = opts.initial_window_width
h, h_unit = opts.initial_window_height
def get_window_size(cell_width: int, cell_height: int, dpi_x: float, dpi_y: float, xscale: float, yscale: float) -> Tuple[int, int]:
if not is_macos:
# scaling is not needed on Wayland, but is needed on macOS. Not
# sure about X11.
xscale = yscale = 1
if w_unit == 'cells':
width = cell_width * w / xscale + (dpi_x / 72) * (opts.window_margin_width + opts.window_padding_width) + 1
else:
width = w
if h_unit == 'cells':
height = cell_height * h / yscale + (dpi_y / 72) * (opts.window_margin_width + opts.window_padding_width) + 1
else:
height = h
return int(width), int(height)
return get_window_size
def commented_out_default_config() -> str:
ans = []
for line in as_conf_file(all_options.values()):

View File

@@ -6,8 +6,8 @@
import os
from gettext import gettext as _
from typing import (
Any, Dict, FrozenSet, Iterable, List, Optional, Sequence, Set, Tuple,
TypeVar, Union
Any, Callable, Dict, FrozenSet, Iterable, List, Optional, Sequence, Set,
Tuple, TypeVar, Union
)
from . import fast_data_types as defines
@@ -16,9 +16,9 @@ from .conf.utils import (
choices, positive_float, positive_int, to_bool, to_cmdline, to_color,
to_color_or_none, unit_float
)
from .constants import config_dir, is_macos
from .constants import FloatEdges, config_dir, is_macos
from .fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
from .layout import all_layouts
from .layout.interface import all_layouts
from .rgb import Color, color_as_int, color_as_sharp, color_from_int
from .utils import log_error
@@ -304,6 +304,9 @@ or by defining shortcuts for it in kitty.conf, for example::
map alt+2 disable_ligatures_in all never
map alt+3 disable_ligatures_in tab cursor
Note that this refers to programming ligatures, typically implemented using the
:code:`calt` OpenType feature. For disabling general ligatures, use the
:opt:`font_features` setting.
'''))
o('font_features', 'none', long_text=_('''
@@ -445,7 +448,8 @@ Program with which to view scrollback in a new window. The scrollback buffer is
passed as STDIN to this program. If you change it, make sure the program you
use can handle ANSI escape sequences for colors and text formatting.
INPUT_LINE_NUMBER in the command line above will be replaced by an integer
representing which line should be at the top of the screen.'''))
representing which line should be at the top of the screen. Similarly CURSOR_LINE and CURSOR_COLUMN
will be replaced by the current cursor position.'''))
o('scrollback_pager_history_size', 0, option_type=scrollback_pager_history_size, long_text=_('''
Separate scrollback history size, used only for browsing the scrollback buffer (in MB).
@@ -548,7 +552,7 @@ rectangular block with the mouse)'''))
o('terminal_select_modifiers', 'shift', option_type=to_modifiers, long_text=_('''
The modifiers to override mouse selection even when a terminal application has grabbed the mouse'''))
o('select_by_word_characters', ':@-./_~?&=%+#', long_text=_('''
o('select_by_word_characters', '@-./_~?&=%+#', long_text=_('''
Characters considered part of a word when double clicking. In addition to these characters
any character that is marked as an alphanumeric character in the unicode
database will be matched.'''))
@@ -675,15 +679,42 @@ that separate the inactive window from a neighbor. Note that setting
a non-zero window margin overrides this and causes all borders to be drawn.
'''))
o('window_margin_width', 0.0, option_type=positive_float, long_text=_('''
The window margin (in pts) (blank area outside the border)'''))
o('single_window_margin_width', -1000.0, option_type=float, long_text=_('''
def edge_width(x: str, converter: Callable[[str], float] = positive_float) -> FloatEdges:
parts = str(x).split()
num = len(parts)
if num == 1:
val = converter(parts[0])
return FloatEdges(val, val, val, val)
if num == 2:
v = converter(parts[0])
h = converter(parts[1])
return FloatEdges(h, v, h, v)
if num == 3:
top, h, bottom = map(converter, parts)
return FloatEdges(h, top, h, bottom)
top, right, bottom, left = map(converter, parts)
return FloatEdges(left, top, right, bottom)
def optional_edge_width(x: str) -> FloatEdges:
return edge_width(x, float)
edge_desc = _(
'A single value sets all four sides. Two values set the vertical and horizontal sides.'
' Three values set top, horizontal and bottom. Four values set top, right, bottom and left.')
o('window_margin_width', '0', option_type=edge_width, long_text=_('''
The window margin (in pts) (blank area outside the border). ''' + edge_desc))
o('single_window_margin_width', '-1', option_type=optional_edge_width, long_text=_('''
The window margin (in pts) to use when only a single window is visible.
Negative values will cause the value of :opt:`window_margin_width` to be used instead.'''))
Negative values will cause the value of :opt:`window_margin_width` to be used instead. ''' + edge_desc))
o('window_padding_width', 0.0, option_type=positive_float, long_text=_('''
The window padding (in pts) (blank area between the text and the window border)'''))
o('window_padding_width', '0', option_type=edge_width, long_text=_('''
The window padding (in pts) (blank area between the text and the window border). ''' + edge_desc))
o('placement_strategy', 'center', option_type=choices('center', 'top-left'), long_text=_('''
When the window size is not an exact multiple of the cell size, the cell area of the terminal
@@ -751,6 +782,12 @@ this option can be used to keep the margins as small as possible when resizing t
Note that this does not currently work on Wayland.
'''))
o('confirm_os_window_close', 0, option_type=positive_int, long_text=_('''
Ask for confirmation when closing an OS window or a tab that has at least this
number of kitty windows in it. A value of zero disables confirmation.
This confirmation also applies to requests to quit the entire application (all
OS windows, via the quit action).
'''))
# }}}
g('tabbar') # {{{
@@ -761,6 +798,8 @@ def tab_separator(x: str) -> str:
for q in '\'"':
if x.startswith(q) and x.endswith(q):
x = x[1:-1]
if not x:
return ''
break
if not x.strip():
x = ('\xa0' * len(x)) if x else default_tab_separator
@@ -843,6 +882,11 @@ A template to render the tab title. The default just renders
the title. If you wish to include the tab-index as well,
use something like: :code:`{index}: {title}`. Useful
if you have shortcuts mapped for :code:`goto_tab N`.
In addition you can use :code:`{layout_name}` for the current
layout name and :code:`{num_windows}` for the number of windows
in the tab. Note that formatting is done by Python's string formatting
machinery, so you can use, for instance, :code:`{layout_name[:2].upper()}` to
show only the first two letters of the layout name, upper-cased.
'''))
o('active_tab_title_template', 'none', option_type=active_tab_title_template, long_text=_('''
Template to use for active tabs, if not specified falls back
@@ -1012,6 +1056,19 @@ you want to prevent programs running on a remote computer over ssh from
controlling kitty.
'''))
o('listen_on', 'none', long_text=_('''
Tell kitty to listen to the specified unix/tcp socket for remote control
connections. Note that this will apply to all kitty instances. It can be
overridden by the :option:`kitty --listen-on` command line flag. This
option accepts only UNIX sockets, such as unix:${TEMP}/mykitty or (on Linux)
unix:@mykitty. Environment variables are expanded. If {kitty_pid} is present
then it is replaced by the PID of the kitty process, otherwise the PID of the kitty
process is appended to the value, with a hyphen. This option is ignored unless
you also set :opt:`allow_remote_control` to enable remote control. See the
help for :option:`kitty --listen-on` for more details.
'''))
o(
'+env', '',
add_to_default=False,
@@ -1329,8 +1386,7 @@ if is_macos:
k('close_tab', 'kitty_mod+q', 'close_tab', _('Close tab'))
if is_macos:
k('close_tab', 'cmd+w', 'close_tab', _('Close tab'), add_to_docs=False)
# Not yet implemented
# k('close_os_window', 'shift+cmd+w', 'close_os_window', _('Close os window'), add_to_docs=False)
k('close_os_window', 'shift+cmd+w', 'close_os_window', _('Close OS window'), add_to_docs=False)
k('move_tab_forward', 'kitty_mod+.', 'move_tab_forward', _('Move tab forward'))
k('move_tab_backward', 'kitty_mod+,', 'move_tab_backward', _('Move tab backward'))
k('set_tab_title', 'kitty_mod+alt+t', 'set_tab_title', _('Set tab title'))
@@ -1346,6 +1402,7 @@ g('shortcuts.fonts') # {{{
k('increase_font_size', 'kitty_mod+equal', 'change_font_size all +2.0', _('Increase font size'))
if is_macos:
k('increase_font_size', 'cmd+plus', 'change_font_size all +2.0', _('Increase font size'), add_to_docs=False)
k('increase_font_size', 'cmd+shift+equal', 'change_font_size all +2.0', _('Increase font size'), add_to_docs=False)
k('decrease_font_size', 'kitty_mod+minus', 'change_font_size all -2.0', _('Decrease font size'))
if is_macos:
k('decrease_font_size', 'cmd+minus', 'change_font_size all -2.0', _('Decrease font size'), add_to_docs=False)

Some files were not shown because too many files have changed in this diff Show More