diff options
| author | Vibhav Pant | 2022-12-20 21:56:02 +0530 |
|---|---|---|
| committer | Vibhav Pant | 2022-12-20 21:56:02 +0530 |
| commit | e7459fcbde4e468f9dfc74477072c7405b59e03e (patch) | |
| tree | 524457d1b9f6aa7edb393b540e4123d1fc60942b /src | |
| parent | 89892db0af4f8a803eb7ff3f5f7880d3126702ff (diff) | |
| parent | 9fafeb2a66fe1cba8f3ad7662196e36ca0b1eca7 (diff) | |
| download | emacs-e7459fcbde4e468f9dfc74477072c7405b59e03e.tar.gz emacs-e7459fcbde4e468f9dfc74477072c7405b59e03e.zip | |
Merge branch 'master' into scratch/comp-static-data
Diffstat (limited to 'src')
41 files changed, 2820 insertions, 938 deletions
diff --git a/src/.lldbinit b/src/.lldbinit index 358cea5f8b6..5fdac34b786 100644 --- a/src/.lldbinit +++ b/src/.lldbinit | |||
| @@ -16,7 +16,7 @@ | |||
| 16 | # You should have received a copy of the GNU General Public License | 16 | # You should have received a copy of the GNU General Public License |
| 17 | # along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. | 17 | # along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. |
| 18 | # | 18 | # |
| 19 | # Use 'lldb --local-init' or add to your ~/.lldbinit the line | 19 | # Use 'lldb --local-lldbinit' or add to your ~/.lldbinit the line |
| 20 | # | 20 | # |
| 21 | # settings set target.load-cwd-lldbinit true | 21 | # settings set target.load-cwd-lldbinit true |
| 22 | # | 22 | # |
diff --git a/src/ChangeLog.11 b/src/ChangeLog.11 index a00ca453ca4..77180262aca 100644 --- a/src/ChangeLog.11 +++ b/src/ChangeLog.11 | |||
| @@ -14235,7 +14235,7 @@ | |||
| 14235 | ns_alternate_modifier. (Bug#1217) | 14235 | ns_alternate_modifier. (Bug#1217) |
| 14236 | 14236 | ||
| 14237 | * nsmenu.m (EmacsMenu-parseKeyEquiv:, addItemWithWidgetValue:): | 14237 | * nsmenu.m (EmacsMenu-parseKeyEquiv:, addItemWithWidgetValue:): |
| 14238 | Display all shortcuts, including those w/o super modifier. | 14238 | Display all shortcuts, including those without super modifier. |
| 14239 | 14239 | ||
| 14240 | * nsfns.m (ns-read-file-name): Fix typo in assignment statement. | 14240 | * nsfns.m (ns-read-file-name): Fix typo in assignment statement. |
| 14241 | 14241 | ||
diff --git a/src/ChangeLog.12 b/src/ChangeLog.12 index 7f77c0ca077..f455c4de382 100644 --- a/src/ChangeLog.12 +++ b/src/ChangeLog.12 | |||
| @@ -10836,7 +10836,7 @@ | |||
| 10836 | * gtkutil.c (xg_maybe_add_timer): Port to higher-res time stamps. | 10836 | * gtkutil.c (xg_maybe_add_timer): Port to higher-res time stamps. |
| 10837 | 10837 | ||
| 10838 | * image.c (prepare_image_for_display, clear_image_cache) | 10838 | * image.c (prepare_image_for_display, clear_image_cache) |
| 10839 | (lookup_image): Port to higer-resolution time stamps. | 10839 | (lookup_image): Port to higher-resolution time stamps. |
| 10840 | 10840 | ||
| 10841 | * keyboard.c (start_polling, bind_polling_period): | 10841 | * keyboard.c (start_polling, bind_polling_period): |
| 10842 | Check for time stamp overflow. | 10842 | Check for time stamp overflow. |
diff --git a/src/ChangeLog.13 b/src/ChangeLog.13 index 91f8005ac51..0c4e2909ced 100644 --- a/src/ChangeLog.13 +++ b/src/ChangeLog.13 | |||
| @@ -10579,7 +10579,7 @@ | |||
| 10579 | (../src/$(OLDXMENU), $(OLDXMENU)): Remove. | 10579 | (../src/$(OLDXMENU), $(OLDXMENU)): Remove. |
| 10580 | (temacs$(EXEEXT)): Depend on $(LIBXMENU), not stamp-oldxmenu. | 10580 | (temacs$(EXEEXT)): Depend on $(LIBXMENU), not stamp-oldxmenu. |
| 10581 | ($(lwlibdir)/liblw.a, $(oldXMenudir)/libXMenu11.a, FORCE): New targets. | 10581 | ($(lwlibdir)/liblw.a, $(oldXMenudir)/libXMenu11.a, FORCE): New targets. |
| 10582 | (boostrap-clean): No need to remove stamp-oldxmenu. | 10582 | (bootstrap-clean): No need to remove stamp-oldxmenu. |
| 10583 | 10583 | ||
| 10584 | Fix recently introduced bool vector overrun. | 10584 | Fix recently introduced bool vector overrun. |
| 10585 | This was due to an optimization that went awry. | 10585 | This was due to an optimization that went awry. |
diff --git a/src/ChangeLog.3 b/src/ChangeLog.3 index 4b3675eaa8f..d32e894fa53 100644 --- a/src/ChangeLog.3 +++ b/src/ChangeLog.3 | |||
| @@ -14422,7 +14422,7 @@ | |||
| 14422 | * s-umips.h: Now include either s-usg5-2-2.h or s-bsd4-3.h | 14422 | * s-umips.h: Now include either s-usg5-2-2.h or s-bsd4-3.h |
| 14423 | and then override as needed. | 14423 | and then override as needed. |
| 14424 | * m-mips.h: System dependence deleted. | 14424 | * m-mips.h: System dependence deleted. |
| 14425 | LD_SWITCH_MACHINE remains w/ options needed on all systems. | 14425 | LD_SWITCH_MACHINE remains with options needed on all systems. |
| 14426 | * m-pmax.h: A little of that (LIBS_DEBUG) moved here. | 14426 | * m-pmax.h: A little of that (LIBS_DEBUG) moved here. |
| 14427 | No need to undef LIBS_MACHINE. | 14427 | No need to undef LIBS_MACHINE. |
| 14428 | 14428 | ||
diff --git a/src/alloc.c b/src/alloc.c index 0a8c1826614..0a4323d1ceb 100644 --- a/src/alloc.c +++ b/src/alloc.c | |||
| @@ -82,6 +82,37 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 82 | #include <valgrind/memcheck.h> | 82 | #include <valgrind/memcheck.h> |
| 83 | #endif | 83 | #endif |
| 84 | 84 | ||
| 85 | /* AddressSanitizer exposes additional functions for manually marking | ||
| 86 | memory as poisoned/unpoisoned. When ASan is enabled and the needed | ||
| 87 | header is available, memory is poisoned when: | ||
| 88 | |||
| 89 | * An ablock is freed (lisp_align_free), or ablocks are initially | ||
| 90 | allocated (lisp_align_malloc). | ||
| 91 | * An interval_block is initially allocated (make_interval). | ||
| 92 | * A dead INTERVAL is put on the interval free list | ||
| 93 | (sweep_intervals). | ||
| 94 | * A sdata is marked as dead (sweep_strings, pin_string). | ||
| 95 | * An sblock is initially allocated (allocate_string_data). | ||
| 96 | * A string_block is initially allocated (allocate_string). | ||
| 97 | * A dead string is put on string_free_list (sweep_strings). | ||
| 98 | * A float_block is initially allocated (make_float). | ||
| 99 | * A dead float is put on float_free_list. | ||
| 100 | * A cons_block is initially allocated (Fcons). | ||
| 101 | * A dead cons is put on cons_free_list (sweep_cons). | ||
| 102 | * A dead vector is put on vector_free_list (setup_on_free_list), | ||
| 103 | or a new vector block is allocated (allocate_vector_from_block). | ||
| 104 | Accordingly, objects reused from the free list are unpoisoned. | ||
| 105 | |||
| 106 | This feature can be disabled wtih the run-time flag | ||
| 107 | `allow_user_poisoning' set to zero. */ | ||
| 108 | #if ADDRESS_SANITIZER && defined HAVE_SANITIZER_ASAN_INTERFACE_H \ | ||
| 109 | && !defined GC_ASAN_POISON_OBJECTS | ||
| 110 | # define GC_ASAN_POISON_OBJECTS 1 | ||
| 111 | # include <sanitizer/asan_interface.h> | ||
| 112 | #else | ||
| 113 | # define GC_ASAN_POISON_OBJECTS 0 | ||
| 114 | #endif | ||
| 115 | |||
| 85 | /* GC_CHECK_MARKED_OBJECTS means do sanity checks on allocated objects. | 116 | /* GC_CHECK_MARKED_OBJECTS means do sanity checks on allocated objects. |
| 86 | We turn that on by default when ENABLE_CHECKING is defined; | 117 | We turn that on by default when ENABLE_CHECKING is defined; |
| 87 | define GC_CHECK_MARKED_OBJECTS to zero to disable. */ | 118 | define GC_CHECK_MARKED_OBJECTS to zero to disable. */ |
| @@ -1164,6 +1195,16 @@ struct ablocks | |||
| 1164 | (1 & (intptr_t) ABLOCKS_BUSY (abase) ? abase : ((void **) (abase))[-1]) | 1195 | (1 & (intptr_t) ABLOCKS_BUSY (abase) ? abase : ((void **) (abase))[-1]) |
| 1165 | #endif | 1196 | #endif |
| 1166 | 1197 | ||
| 1198 | #if GC_ASAN_POISON_OBJECTS | ||
| 1199 | # define ASAN_POISON_ABLOCK(b) \ | ||
| 1200 | __asan_poison_memory_region (&(b)->x, sizeof ((b)->x)) | ||
| 1201 | # define ASAN_UNPOISON_ABLOCK(b) \ | ||
| 1202 | __asan_unpoison_memory_region (&(b)->x, sizeof ((b)->x)) | ||
| 1203 | #else | ||
| 1204 | # define ASAN_POISON_ABLOCK(b) ((void) 0) | ||
| 1205 | # define ASAN_UNPOISON_ABLOCK(b) ((void) 0) | ||
| 1206 | #endif | ||
| 1207 | |||
| 1167 | /* The list of free ablock. */ | 1208 | /* The list of free ablock. */ |
| 1168 | static struct ablock *free_ablock; | 1209 | static struct ablock *free_ablock; |
| 1169 | 1210 | ||
| @@ -1242,6 +1283,7 @@ lisp_align_malloc (size_t nbytes, enum mem_type type) | |||
| 1242 | { | 1283 | { |
| 1243 | abase->blocks[i].abase = abase; | 1284 | abase->blocks[i].abase = abase; |
| 1244 | abase->blocks[i].x.next_free = free_ablock; | 1285 | abase->blocks[i].x.next_free = free_ablock; |
| 1286 | ASAN_POISON_ABLOCK (&abase->blocks[i]); | ||
| 1245 | free_ablock = &abase->blocks[i]; | 1287 | free_ablock = &abase->blocks[i]; |
| 1246 | } | 1288 | } |
| 1247 | intptr_t ialigned = aligned; | 1289 | intptr_t ialigned = aligned; |
| @@ -1254,6 +1296,7 @@ lisp_align_malloc (size_t nbytes, enum mem_type type) | |||
| 1254 | eassert ((intptr_t) ABLOCKS_BUSY (abase) == aligned); | 1296 | eassert ((intptr_t) ABLOCKS_BUSY (abase) == aligned); |
| 1255 | } | 1297 | } |
| 1256 | 1298 | ||
| 1299 | ASAN_UNPOISON_ABLOCK (free_ablock); | ||
| 1257 | abase = ABLOCK_ABASE (free_ablock); | 1300 | abase = ABLOCK_ABASE (free_ablock); |
| 1258 | ABLOCKS_BUSY (abase) | 1301 | ABLOCKS_BUSY (abase) |
| 1259 | = (struct ablocks *) (2 + (intptr_t) ABLOCKS_BUSY (abase)); | 1302 | = (struct ablocks *) (2 + (intptr_t) ABLOCKS_BUSY (abase)); |
| @@ -1285,6 +1328,7 @@ lisp_align_free (void *block) | |||
| 1285 | #endif | 1328 | #endif |
| 1286 | /* Put on free list. */ | 1329 | /* Put on free list. */ |
| 1287 | ablock->x.next_free = free_ablock; | 1330 | ablock->x.next_free = free_ablock; |
| 1331 | ASAN_POISON_ABLOCK (ablock); | ||
| 1288 | free_ablock = ablock; | 1332 | free_ablock = ablock; |
| 1289 | /* Update busy count. */ | 1333 | /* Update busy count. */ |
| 1290 | intptr_t busy = (intptr_t) ABLOCKS_BUSY (abase) - 2; | 1334 | intptr_t busy = (intptr_t) ABLOCKS_BUSY (abase) - 2; |
| @@ -1297,9 +1341,12 @@ lisp_align_free (void *block) | |||
| 1297 | bool aligned = busy; | 1341 | bool aligned = busy; |
| 1298 | struct ablock **tem = &free_ablock; | 1342 | struct ablock **tem = &free_ablock; |
| 1299 | struct ablock *atop = &abase->blocks[aligned ? ABLOCKS_SIZE : ABLOCKS_SIZE - 1]; | 1343 | struct ablock *atop = &abase->blocks[aligned ? ABLOCKS_SIZE : ABLOCKS_SIZE - 1]; |
| 1300 | |||
| 1301 | while (*tem) | 1344 | while (*tem) |
| 1302 | { | 1345 | { |
| 1346 | #if GC_ASAN_POISON_OBJECTS | ||
| 1347 | __asan_unpoison_memory_region (&(*tem)->x, | ||
| 1348 | sizeof ((*tem)->x)); | ||
| 1349 | #endif | ||
| 1303 | if (*tem >= (struct ablock *) abase && *tem < atop) | 1350 | if (*tem >= (struct ablock *) abase && *tem < atop) |
| 1304 | { | 1351 | { |
| 1305 | i++; | 1352 | i++; |
| @@ -1428,6 +1475,24 @@ static int interval_block_index = INTERVAL_BLOCK_SIZE; | |||
| 1428 | 1475 | ||
| 1429 | static INTERVAL interval_free_list; | 1476 | static INTERVAL interval_free_list; |
| 1430 | 1477 | ||
| 1478 | #if GC_ASAN_POISON_OBJECTS | ||
| 1479 | # define ASAN_POISON_INTERVAL_BLOCK(b) \ | ||
| 1480 | __asan_poison_memory_region ((b)->intervals, \ | ||
| 1481 | sizeof ((b)->intervals)) | ||
| 1482 | # define ASAN_UNPOISON_INTERVAL_BLOCK(b) \ | ||
| 1483 | __asan_unpoison_memory_region ((b)->intervals, \ | ||
| 1484 | sizeof ((b)->intervals)) | ||
| 1485 | # define ASAN_POISON_INTERVAL(i) \ | ||
| 1486 | __asan_poison_memory_region ((i), sizeof (*(i))) | ||
| 1487 | # define ASAN_UNPOISON_INTERVAL(i) \ | ||
| 1488 | __asan_unpoison_memory_region ((i), sizeof (*(i))) | ||
| 1489 | #else | ||
| 1490 | # define ASAN_POISON_INTERVAL_BLOCK(b) ((void) 0) | ||
| 1491 | # define ASAN_UNPOISON_INTERVAL_BLOCK(b) ((void) 0) | ||
| 1492 | # define ASAN_POISON_INTERVAL(i) ((void) 0) | ||
| 1493 | # define ASAN_UNPOISON_INTERVAL(i) ((void) 0) | ||
| 1494 | #endif | ||
| 1495 | |||
| 1431 | /* Return a new interval. */ | 1496 | /* Return a new interval. */ |
| 1432 | 1497 | ||
| 1433 | INTERVAL | 1498 | INTERVAL |
| @@ -1440,6 +1505,7 @@ make_interval (void) | |||
| 1440 | if (interval_free_list) | 1505 | if (interval_free_list) |
| 1441 | { | 1506 | { |
| 1442 | val = interval_free_list; | 1507 | val = interval_free_list; |
| 1508 | ASAN_UNPOISON_INTERVAL (val); | ||
| 1443 | interval_free_list = INTERVAL_PARENT (interval_free_list); | 1509 | interval_free_list = INTERVAL_PARENT (interval_free_list); |
| 1444 | } | 1510 | } |
| 1445 | else | 1511 | else |
| @@ -1450,10 +1516,12 @@ make_interval (void) | |||
| 1450 | = lisp_malloc (sizeof *newi, false, MEM_TYPE_NON_LISP); | 1516 | = lisp_malloc (sizeof *newi, false, MEM_TYPE_NON_LISP); |
| 1451 | 1517 | ||
| 1452 | newi->next = interval_block; | 1518 | newi->next = interval_block; |
| 1519 | ASAN_POISON_INTERVAL_BLOCK (newi); | ||
| 1453 | interval_block = newi; | 1520 | interval_block = newi; |
| 1454 | interval_block_index = 0; | 1521 | interval_block_index = 0; |
| 1455 | } | 1522 | } |
| 1456 | val = &interval_block->intervals[interval_block_index++]; | 1523 | val = &interval_block->intervals[interval_block_index++]; |
| 1524 | ASAN_UNPOISON_INTERVAL (val); | ||
| 1457 | } | 1525 | } |
| 1458 | 1526 | ||
| 1459 | MALLOC_UNBLOCK_INPUT; | 1527 | MALLOC_UNBLOCK_INPUT; |
| @@ -1694,6 +1762,41 @@ init_strings (void) | |||
| 1694 | staticpro (&empty_multibyte_string); | 1762 | staticpro (&empty_multibyte_string); |
| 1695 | } | 1763 | } |
| 1696 | 1764 | ||
| 1765 | #if GC_ASAN_POISON_OBJECTS | ||
| 1766 | /* Prepare s for denoting a free sdata struct, i.e, poison all bytes | ||
| 1767 | in the flexible array member, except the first SDATA_OFFSET bytes. | ||
| 1768 | This is only effective for strings of size n where n > sdata_size(n). | ||
| 1769 | */ | ||
| 1770 | # define ASAN_PREPARE_DEAD_SDATA(s, size) \ | ||
| 1771 | do { \ | ||
| 1772 | __asan_poison_memory_region ((s), sdata_size ((size))); \ | ||
| 1773 | __asan_unpoison_memory_region (&(((s))->string), \ | ||
| 1774 | sizeof (struct Lisp_String *)); \ | ||
| 1775 | __asan_unpoison_memory_region (&SDATA_NBYTES ((s)), \ | ||
| 1776 | sizeof (SDATA_NBYTES ((s)))); \ | ||
| 1777 | } while (false) | ||
| 1778 | /* Prepare s for storing string data for NBYTES bytes. */ | ||
| 1779 | # define ASAN_PREPARE_LIVE_SDATA(s, nbytes) \ | ||
| 1780 | __asan_unpoison_memory_region ((s), sdata_size ((nbytes))) | ||
| 1781 | # define ASAN_POISON_SBLOCK_DATA(b, size) \ | ||
| 1782 | __asan_poison_memory_region ((b)->data, (size)) | ||
| 1783 | # define ASAN_POISON_STRING_BLOCK(b) \ | ||
| 1784 | __asan_poison_memory_region ((b)->strings, STRING_BLOCK_SIZE) | ||
| 1785 | # define ASAN_UNPOISON_STRING_BLOCK(b) \ | ||
| 1786 | __asan_unpoison_memory_region ((b)->strings, STRING_BLOCK_SIZE) | ||
| 1787 | # define ASAN_POISON_STRING(s) \ | ||
| 1788 | __asan_poison_memory_region ((s), sizeof (*(s))) | ||
| 1789 | # define ASAN_UNPOISON_STRING(s) \ | ||
| 1790 | __asan_unpoison_memory_region ((s), sizeof (*(s))) | ||
| 1791 | #else | ||
| 1792 | # define ASAN_PREPARE_DEAD_SDATA(s, size) ((void) 0) | ||
| 1793 | # define ASAN_PREPARE_LIVE_SDATA(s, nbytes) ((void) 0) | ||
| 1794 | # define ASAN_POISON_SBLOCK_DATA(b, size) ((void) 0) | ||
| 1795 | # define ASAN_POISON_STRING_BLOCK(b) ((void) 0) | ||
| 1796 | # define ASAN_UNPOISON_STRING_BLOCK(b) ((void) 0) | ||
| 1797 | # define ASAN_POISON_STRING(s) ((void) 0) | ||
| 1798 | # define ASAN_UNPOISON_STRING(s) ((void) 0) | ||
| 1799 | #endif | ||
| 1697 | 1800 | ||
| 1698 | #ifdef GC_CHECK_STRING_BYTES | 1801 | #ifdef GC_CHECK_STRING_BYTES |
| 1699 | 1802 | ||
| @@ -1812,12 +1915,14 @@ allocate_string (void) | |||
| 1812 | NEXT_FREE_LISP_STRING (s) = string_free_list; | 1915 | NEXT_FREE_LISP_STRING (s) = string_free_list; |
| 1813 | string_free_list = s; | 1916 | string_free_list = s; |
| 1814 | } | 1917 | } |
| 1918 | ASAN_POISON_STRING_BLOCK (b); | ||
| 1815 | } | 1919 | } |
| 1816 | 1920 | ||
| 1817 | check_string_free_list (); | 1921 | check_string_free_list (); |
| 1818 | 1922 | ||
| 1819 | /* Pop a Lisp_String off the free-list. */ | 1923 | /* Pop a Lisp_String off the free-list. */ |
| 1820 | s = string_free_list; | 1924 | s = string_free_list; |
| 1925 | ASAN_UNPOISON_STRING (s); | ||
| 1821 | string_free_list = NEXT_FREE_LISP_STRING (s); | 1926 | string_free_list = NEXT_FREE_LISP_STRING (s); |
| 1822 | 1927 | ||
| 1823 | MALLOC_UNBLOCK_INPUT; | 1928 | MALLOC_UNBLOCK_INPUT; |
| @@ -1877,6 +1982,7 @@ allocate_string_data (struct Lisp_String *s, | |||
| 1877 | #endif | 1982 | #endif |
| 1878 | 1983 | ||
| 1879 | b = lisp_malloc (size + GC_STRING_EXTRA, clearit, MEM_TYPE_NON_LISP); | 1984 | b = lisp_malloc (size + GC_STRING_EXTRA, clearit, MEM_TYPE_NON_LISP); |
| 1985 | ASAN_POISON_SBLOCK_DATA (b, size); | ||
| 1880 | 1986 | ||
| 1881 | #ifdef DOUG_LEA_MALLOC | 1987 | #ifdef DOUG_LEA_MALLOC |
| 1882 | if (!mmap_lisp_allowed_p ()) | 1988 | if (!mmap_lisp_allowed_p ()) |
| @@ -1898,6 +2004,8 @@ allocate_string_data (struct Lisp_String *s, | |||
| 1898 | { | 2004 | { |
| 1899 | /* Not enough room in the current sblock. */ | 2005 | /* Not enough room in the current sblock. */ |
| 1900 | b = lisp_malloc (SBLOCK_SIZE, false, MEM_TYPE_NON_LISP); | 2006 | b = lisp_malloc (SBLOCK_SIZE, false, MEM_TYPE_NON_LISP); |
| 2007 | ASAN_POISON_SBLOCK_DATA (b, SBLOCK_SIZE); | ||
| 2008 | |||
| 1901 | data = b->data; | 2009 | data = b->data; |
| 1902 | b->next = NULL; | 2010 | b->next = NULL; |
| 1903 | b->next_free = data; | 2011 | b->next_free = data; |
| @@ -1910,10 +2018,19 @@ allocate_string_data (struct Lisp_String *s, | |||
| 1910 | } | 2018 | } |
| 1911 | 2019 | ||
| 1912 | data = b->next_free; | 2020 | data = b->next_free; |
| 2021 | |||
| 1913 | if (clearit) | 2022 | if (clearit) |
| 1914 | memset (SDATA_DATA (data), 0, nbytes); | 2023 | { |
| 2024 | #if GC_ASAN_POISON_OBJECTS | ||
| 2025 | /* We are accessing SDATA_DATA (data) before it gets | ||
| 2026 | * normally unpoisoned, so do it manually. */ | ||
| 2027 | __asan_unpoison_memory_region (SDATA_DATA (data), nbytes); | ||
| 2028 | #endif | ||
| 2029 | memset (SDATA_DATA (data), 0, nbytes); | ||
| 2030 | } | ||
| 1915 | } | 2031 | } |
| 1916 | 2032 | ||
| 2033 | ASAN_PREPARE_LIVE_SDATA (data, nbytes); | ||
| 1917 | data->string = s; | 2034 | data->string = s; |
| 1918 | b->next_free = (sdata *) ((char *) data + needed + GC_STRING_EXTRA); | 2035 | b->next_free = (sdata *) ((char *) data + needed + GC_STRING_EXTRA); |
| 1919 | eassert ((uintptr_t) b->next_free % alignof (sdata) == 0); | 2036 | eassert ((uintptr_t) b->next_free % alignof (sdata) == 0); |
| @@ -2005,12 +2122,16 @@ sweep_strings (void) | |||
| 2005 | int i, nfree = 0; | 2122 | int i, nfree = 0; |
| 2006 | struct Lisp_String *free_list_before = string_free_list; | 2123 | struct Lisp_String *free_list_before = string_free_list; |
| 2007 | 2124 | ||
| 2125 | ASAN_UNPOISON_STRING_BLOCK (b); | ||
| 2126 | |||
| 2008 | next = b->next; | 2127 | next = b->next; |
| 2009 | 2128 | ||
| 2010 | for (i = 0; i < STRING_BLOCK_SIZE; ++i) | 2129 | for (i = 0; i < STRING_BLOCK_SIZE; ++i) |
| 2011 | { | 2130 | { |
| 2012 | struct Lisp_String *s = b->strings + i; | 2131 | struct Lisp_String *s = b->strings + i; |
| 2013 | 2132 | ||
| 2133 | ASAN_UNPOISON_STRING (s); | ||
| 2134 | |||
| 2014 | if (s->u.s.data) | 2135 | if (s->u.s.data) |
| 2015 | { | 2136 | { |
| 2016 | /* String was not on free-list before. */ | 2137 | /* String was not on free-list before. */ |
| @@ -2047,6 +2168,8 @@ sweep_strings (void) | |||
| 2047 | 2168 | ||
| 2048 | /* Put the string on the free-list. */ | 2169 | /* Put the string on the free-list. */ |
| 2049 | NEXT_FREE_LISP_STRING (s) = string_free_list; | 2170 | NEXT_FREE_LISP_STRING (s) = string_free_list; |
| 2171 | ASAN_POISON_STRING (s); | ||
| 2172 | ASAN_PREPARE_DEAD_SDATA (data, SDATA_NBYTES (data)); | ||
| 2050 | string_free_list = s; | 2173 | string_free_list = s; |
| 2051 | ++nfree; | 2174 | ++nfree; |
| 2052 | } | 2175 | } |
| @@ -2055,6 +2178,8 @@ sweep_strings (void) | |||
| 2055 | { | 2178 | { |
| 2056 | /* S was on the free-list before. Put it there again. */ | 2179 | /* S was on the free-list before. Put it there again. */ |
| 2057 | NEXT_FREE_LISP_STRING (s) = string_free_list; | 2180 | NEXT_FREE_LISP_STRING (s) = string_free_list; |
| 2181 | ASAN_POISON_STRING (s); | ||
| 2182 | |||
| 2058 | string_free_list = s; | 2183 | string_free_list = s; |
| 2059 | ++nfree; | 2184 | ++nfree; |
| 2060 | } | 2185 | } |
| @@ -2181,6 +2306,7 @@ compact_small_strings (void) | |||
| 2181 | if (from != to) | 2306 | if (from != to) |
| 2182 | { | 2307 | { |
| 2183 | eassert (tb != b || to < from); | 2308 | eassert (tb != b || to < from); |
| 2309 | ASAN_PREPARE_LIVE_SDATA (to, nbytes); | ||
| 2184 | memmove (to, from, size + GC_STRING_EXTRA); | 2310 | memmove (to, from, size + GC_STRING_EXTRA); |
| 2185 | to->string->u.s.data = SDATA_DATA (to); | 2311 | to->string->u.s.data = SDATA_DATA (to); |
| 2186 | } | 2312 | } |
| @@ -2537,6 +2663,7 @@ pin_string (Lisp_Object string) | |||
| 2537 | memcpy (s->u.s.data, data, size); | 2663 | memcpy (s->u.s.data, data, size); |
| 2538 | old_sdata->string = NULL; | 2664 | old_sdata->string = NULL; |
| 2539 | SDATA_NBYTES (old_sdata) = size; | 2665 | SDATA_NBYTES (old_sdata) = size; |
| 2666 | ASAN_PREPARE_DEAD_SDATA (old_sdata, size); | ||
| 2540 | } | 2667 | } |
| 2541 | if (s->u.s.size_byte != -3) | 2668 | if (s->u.s.size_byte != -3) |
| 2542 | s->u.s.size_byte = -3; | 2669 | s->u.s.size_byte = -3; |
| @@ -2601,6 +2728,24 @@ struct float_block | |||
| 2601 | #define XFLOAT_UNMARK(fptr) \ | 2728 | #define XFLOAT_UNMARK(fptr) \ |
| 2602 | UNSETMARKBIT (FLOAT_BLOCK (fptr), FLOAT_INDEX ((fptr))) | 2729 | UNSETMARKBIT (FLOAT_BLOCK (fptr), FLOAT_INDEX ((fptr))) |
| 2603 | 2730 | ||
| 2731 | #if GC_ASAN_POISON_OBJECTS | ||
| 2732 | # define ASAN_POISON_FLOAT_BLOCK(fblk) \ | ||
| 2733 | __asan_poison_memory_region ((fblk)->floats, \ | ||
| 2734 | sizeof ((fblk)->floats)) | ||
| 2735 | # define ASAN_UNPOISON_FLOAT_BLOCK(fblk) \ | ||
| 2736 | __asan_unpoison_memory_region ((fblk)->floats, \ | ||
| 2737 | sizeof ((fblk)->floats)) | ||
| 2738 | # define ASAN_POISON_FLOAT(p) \ | ||
| 2739 | __asan_poison_memory_region ((p), sizeof (struct Lisp_Float)) | ||
| 2740 | # define ASAN_UNPOISON_FLOAT(p) \ | ||
| 2741 | __asan_unpoison_memory_region ((p), sizeof (struct Lisp_Float)) | ||
| 2742 | #else | ||
| 2743 | # define ASAN_POISON_FLOAT_BLOCK(fblk) ((void) 0) | ||
| 2744 | # define ASAN_UNPOISON_FLOAT_BLOCK(fblk) ((void) 0) | ||
| 2745 | # define ASAN_POISON_FLOAT(p) ((void) 0) | ||
| 2746 | # define ASAN_UNPOISON_FLOAT(p) ((void) 0) | ||
| 2747 | #endif | ||
| 2748 | |||
| 2604 | /* Current float_block. */ | 2749 | /* Current float_block. */ |
| 2605 | 2750 | ||
| 2606 | static struct float_block *float_block; | 2751 | static struct float_block *float_block; |
| @@ -2625,6 +2770,7 @@ make_float (double float_value) | |||
| 2625 | if (float_free_list) | 2770 | if (float_free_list) |
| 2626 | { | 2771 | { |
| 2627 | XSETFLOAT (val, float_free_list); | 2772 | XSETFLOAT (val, float_free_list); |
| 2773 | ASAN_UNPOISON_FLOAT (float_free_list); | ||
| 2628 | float_free_list = float_free_list->u.chain; | 2774 | float_free_list = float_free_list->u.chain; |
| 2629 | } | 2775 | } |
| 2630 | else | 2776 | else |
| @@ -2635,9 +2781,11 @@ make_float (double float_value) | |||
| 2635 | = lisp_align_malloc (sizeof *new, MEM_TYPE_FLOAT); | 2781 | = lisp_align_malloc (sizeof *new, MEM_TYPE_FLOAT); |
| 2636 | new->next = float_block; | 2782 | new->next = float_block; |
| 2637 | memset (new->gcmarkbits, 0, sizeof new->gcmarkbits); | 2783 | memset (new->gcmarkbits, 0, sizeof new->gcmarkbits); |
| 2784 | ASAN_POISON_FLOAT_BLOCK (new); | ||
| 2638 | float_block = new; | 2785 | float_block = new; |
| 2639 | float_block_index = 0; | 2786 | float_block_index = 0; |
| 2640 | } | 2787 | } |
| 2788 | ASAN_UNPOISON_FLOAT (&float_block->floats[float_block_index]); | ||
| 2641 | XSETFLOAT (val, &float_block->floats[float_block_index]); | 2789 | XSETFLOAT (val, &float_block->floats[float_block_index]); |
| 2642 | float_block_index++; | 2790 | float_block_index++; |
| 2643 | } | 2791 | } |
| @@ -2715,6 +2863,19 @@ static int cons_block_index = CONS_BLOCK_SIZE; | |||
| 2715 | 2863 | ||
| 2716 | static struct Lisp_Cons *cons_free_list; | 2864 | static struct Lisp_Cons *cons_free_list; |
| 2717 | 2865 | ||
| 2866 | #if GC_ASAN_POISON_OBJECTS | ||
| 2867 | # define ASAN_POISON_CONS_BLOCK(b) \ | ||
| 2868 | __asan_poison_memory_region ((b)->conses, sizeof ((b)->conses)) | ||
| 2869 | # define ASAN_POISON_CONS(p) \ | ||
| 2870 | __asan_poison_memory_region ((p), sizeof (struct Lisp_Cons)) | ||
| 2871 | # define ASAN_UNPOISON_CONS(p) \ | ||
| 2872 | __asan_unpoison_memory_region ((p), sizeof (struct Lisp_Cons)) | ||
| 2873 | #else | ||
| 2874 | # define ASAN_POISON_CONS_BLOCK(b) ((void) 0) | ||
| 2875 | # define ASAN_POISON_CONS(p) ((void) 0) | ||
| 2876 | # define ASAN_UNPOISON_CONS(p) ((void) 0) | ||
| 2877 | #endif | ||
| 2878 | |||
| 2718 | /* Explicitly free a cons cell by putting it on the free-list. */ | 2879 | /* Explicitly free a cons cell by putting it on the free-list. */ |
| 2719 | 2880 | ||
| 2720 | void | 2881 | void |
| @@ -2725,6 +2886,7 @@ free_cons (struct Lisp_Cons *ptr) | |||
| 2725 | cons_free_list = ptr; | 2886 | cons_free_list = ptr; |
| 2726 | ptrdiff_t nbytes = sizeof *ptr; | 2887 | ptrdiff_t nbytes = sizeof *ptr; |
| 2727 | tally_consing (-nbytes); | 2888 | tally_consing (-nbytes); |
| 2889 | ASAN_POISON_CONS (ptr); | ||
| 2728 | } | 2890 | } |
| 2729 | 2891 | ||
| 2730 | DEFUN ("cons", Fcons, Scons, 2, 2, 0, | 2892 | DEFUN ("cons", Fcons, Scons, 2, 2, 0, |
| @@ -2737,6 +2899,7 @@ DEFUN ("cons", Fcons, Scons, 2, 2, 0, | |||
| 2737 | 2899 | ||
| 2738 | if (cons_free_list) | 2900 | if (cons_free_list) |
| 2739 | { | 2901 | { |
| 2902 | ASAN_UNPOISON_CONS (cons_free_list); | ||
| 2740 | XSETCONS (val, cons_free_list); | 2903 | XSETCONS (val, cons_free_list); |
| 2741 | cons_free_list = cons_free_list->u.s.u.chain; | 2904 | cons_free_list = cons_free_list->u.s.u.chain; |
| 2742 | } | 2905 | } |
| @@ -2747,10 +2910,12 @@ DEFUN ("cons", Fcons, Scons, 2, 2, 0, | |||
| 2747 | struct cons_block *new | 2910 | struct cons_block *new |
| 2748 | = lisp_align_malloc (sizeof *new, MEM_TYPE_CONS); | 2911 | = lisp_align_malloc (sizeof *new, MEM_TYPE_CONS); |
| 2749 | memset (new->gcmarkbits, 0, sizeof new->gcmarkbits); | 2912 | memset (new->gcmarkbits, 0, sizeof new->gcmarkbits); |
| 2913 | ASAN_POISON_CONS_BLOCK (new); | ||
| 2750 | new->next = cons_block; | 2914 | new->next = cons_block; |
| 2751 | cons_block = new; | 2915 | cons_block = new; |
| 2752 | cons_block_index = 0; | 2916 | cons_block_index = 0; |
| 2753 | } | 2917 | } |
| 2918 | ASAN_UNPOISON_CONS (&cons_block->conses[cons_block_index]); | ||
| 2754 | XSETCONS (val, &cons_block->conses[cons_block_index]); | 2919 | XSETCONS (val, &cons_block->conses[cons_block_index]); |
| 2755 | cons_block_index++; | 2920 | cons_block_index++; |
| 2756 | } | 2921 | } |
| @@ -3005,6 +3170,19 @@ static struct large_vector *large_vectors; | |||
| 3005 | 3170 | ||
| 3006 | Lisp_Object zero_vector; | 3171 | Lisp_Object zero_vector; |
| 3007 | 3172 | ||
| 3173 | #if GC_ASAN_POISON_OBJECTS | ||
| 3174 | # define ASAN_POISON_VECTOR_CONTENTS(v, bytes) \ | ||
| 3175 | __asan_poison_memory_region ((v)->contents, (bytes)) | ||
| 3176 | # define ASAN_UNPOISON_VECTOR_CONTENTS(v, bytes) \ | ||
| 3177 | __asan_unpoison_memory_region ((v)->contents, (bytes)) | ||
| 3178 | # define ASAN_UNPOISON_VECTOR_BLOCK(b) \ | ||
| 3179 | __asan_unpoison_memory_region ((b)->data, sizeof ((b)->data)) | ||
| 3180 | #else | ||
| 3181 | # define ASAN_POISON_VECTOR_CONTENTS(v, bytes) ((void) 0) | ||
| 3182 | # define ASAN_UNPOISON_VECTOR_CONTENTS(v, bytes) ((void) 0) | ||
| 3183 | # define ASAN_UNPOISON_VECTOR_BLOCK(b) ((void) 0) | ||
| 3184 | #endif | ||
| 3185 | |||
| 3008 | /* Common shortcut to setup vector on a free list. */ | 3186 | /* Common shortcut to setup vector on a free list. */ |
| 3009 | 3187 | ||
| 3010 | static void | 3188 | static void |
| @@ -3017,6 +3195,7 @@ setup_on_free_list (struct Lisp_Vector *v, ptrdiff_t nbytes) | |||
| 3017 | ptrdiff_t vindex = VINDEX (nbytes); | 3195 | ptrdiff_t vindex = VINDEX (nbytes); |
| 3018 | eassert (vindex < VECTOR_MAX_FREE_LIST_INDEX); | 3196 | eassert (vindex < VECTOR_MAX_FREE_LIST_INDEX); |
| 3019 | set_next_vector (v, vector_free_lists[vindex]); | 3197 | set_next_vector (v, vector_free_lists[vindex]); |
| 3198 | ASAN_POISON_VECTOR_CONTENTS (v, nbytes - header_size); | ||
| 3020 | vector_free_lists[vindex] = v; | 3199 | vector_free_lists[vindex] = v; |
| 3021 | } | 3200 | } |
| 3022 | 3201 | ||
| @@ -3064,6 +3243,7 @@ allocate_vector_from_block (ptrdiff_t nbytes) | |||
| 3064 | if (vector_free_lists[index]) | 3243 | if (vector_free_lists[index]) |
| 3065 | { | 3244 | { |
| 3066 | vector = vector_free_lists[index]; | 3245 | vector = vector_free_lists[index]; |
| 3246 | ASAN_UNPOISON_VECTOR_CONTENTS (vector, nbytes - header_size); | ||
| 3067 | vector_free_lists[index] = next_vector (vector); | 3247 | vector_free_lists[index] = next_vector (vector); |
| 3068 | return vector; | 3248 | return vector; |
| 3069 | } | 3249 | } |
| @@ -3077,12 +3257,18 @@ allocate_vector_from_block (ptrdiff_t nbytes) | |||
| 3077 | { | 3257 | { |
| 3078 | /* This vector is larger than requested. */ | 3258 | /* This vector is larger than requested. */ |
| 3079 | vector = vector_free_lists[index]; | 3259 | vector = vector_free_lists[index]; |
| 3260 | ASAN_UNPOISON_VECTOR_CONTENTS (vector, nbytes - header_size); | ||
| 3080 | vector_free_lists[index] = next_vector (vector); | 3261 | vector_free_lists[index] = next_vector (vector); |
| 3081 | 3262 | ||
| 3082 | /* Excess bytes are used for the smaller vector, | 3263 | /* Excess bytes are used for the smaller vector, |
| 3083 | which should be set on an appropriate free list. */ | 3264 | which should be set on an appropriate free list. */ |
| 3084 | restbytes = index * roundup_size + VBLOCK_BYTES_MIN - nbytes; | 3265 | restbytes = index * roundup_size + VBLOCK_BYTES_MIN - nbytes; |
| 3085 | eassert (restbytes % roundup_size == 0); | 3266 | eassert (restbytes % roundup_size == 0); |
| 3267 | #if GC_ASAN_POISON_OBJECTS | ||
| 3268 | /* Ensure that accessing excess bytes does not trigger ASan. */ | ||
| 3269 | __asan_unpoison_memory_region (ADVANCE (vector, nbytes), | ||
| 3270 | restbytes); | ||
| 3271 | #endif | ||
| 3086 | setup_on_free_list (ADVANCE (vector, nbytes), restbytes); | 3272 | setup_on_free_list (ADVANCE (vector, nbytes), restbytes); |
| 3087 | return vector; | 3273 | return vector; |
| 3088 | } | 3274 | } |
| @@ -3258,6 +3444,7 @@ sweep_vectors (void) | |||
| 3258 | for (vector = (struct Lisp_Vector *) block->data; | 3444 | for (vector = (struct Lisp_Vector *) block->data; |
| 3259 | VECTOR_IN_BLOCK (vector, block); vector = next) | 3445 | VECTOR_IN_BLOCK (vector, block); vector = next) |
| 3260 | { | 3446 | { |
| 3447 | ASAN_UNPOISON_VECTOR_BLOCK (block); | ||
| 3261 | if (XVECTOR_MARKED_P (vector)) | 3448 | if (XVECTOR_MARKED_P (vector)) |
| 3262 | { | 3449 | { |
| 3263 | XUNMARK_VECTOR (vector); | 3450 | XUNMARK_VECTOR (vector); |
| @@ -3633,6 +3820,23 @@ struct symbol_block | |||
| 3633 | struct symbol_block *next; | 3820 | struct symbol_block *next; |
| 3634 | }; | 3821 | }; |
| 3635 | 3822 | ||
| 3823 | #if GC_ASAN_POISON_OBJECTS | ||
| 3824 | # define ASAN_POISON_SYMBOL_BLOCK(s) \ | ||
| 3825 | __asan_poison_memory_region ((s)->symbols, sizeof ((s)->symbols)) | ||
| 3826 | # define ASAN_UNPOISON_SYMBOL_BLOCK(s) \ | ||
| 3827 | __asan_unpoison_memory_region ((s)->symbols, sizeof ((s)->symbols)) | ||
| 3828 | # define ASAN_POISON_SYMBOL(sym) \ | ||
| 3829 | __asan_poison_memory_region ((sym), sizeof (*(sym))) | ||
| 3830 | # define ASAN_UNPOISON_SYMBOL(sym) \ | ||
| 3831 | __asan_unpoison_memory_region ((sym), sizeof (*(sym))) | ||
| 3832 | |||
| 3833 | #else | ||
| 3834 | # define ASAN_POISON_SYMBOL_BLOCK(s) ((void) 0) | ||
| 3835 | # define ASAN_UNPOISON_SYMBOL_BLOCK(s) ((void) 0) | ||
| 3836 | # define ASAN_POISON_SYMBOL(sym) ((void) 0) | ||
| 3837 | # define ASAN_UNPOISON_SYMBOL(sym) ((void) 0) | ||
| 3838 | #endif | ||
| 3839 | |||
| 3636 | /* Current symbol block and index of first unused Lisp_Symbol | 3840 | /* Current symbol block and index of first unused Lisp_Symbol |
| 3637 | structure in it. */ | 3841 | structure in it. */ |
| 3638 | 3842 | ||
| @@ -3686,6 +3890,7 @@ Its value is void, and its function definition and property list are nil. */) | |||
| 3686 | 3890 | ||
| 3687 | if (symbol_free_list) | 3891 | if (symbol_free_list) |
| 3688 | { | 3892 | { |
| 3893 | ASAN_UNPOISON_SYMBOL (symbol_free_list); | ||
| 3689 | XSETSYMBOL (val, symbol_free_list); | 3894 | XSETSYMBOL (val, symbol_free_list); |
| 3690 | symbol_free_list = symbol_free_list->u.s.next; | 3895 | symbol_free_list = symbol_free_list->u.s.next; |
| 3691 | } | 3896 | } |
| @@ -3695,10 +3900,13 @@ Its value is void, and its function definition and property list are nil. */) | |||
| 3695 | { | 3900 | { |
| 3696 | struct symbol_block *new | 3901 | struct symbol_block *new |
| 3697 | = lisp_malloc (sizeof *new, false, MEM_TYPE_SYMBOL); | 3902 | = lisp_malloc (sizeof *new, false, MEM_TYPE_SYMBOL); |
| 3903 | ASAN_POISON_SYMBOL_BLOCK (new); | ||
| 3698 | new->next = symbol_block; | 3904 | new->next = symbol_block; |
| 3699 | symbol_block = new; | 3905 | symbol_block = new; |
| 3700 | symbol_block_index = 0; | 3906 | symbol_block_index = 0; |
| 3701 | } | 3907 | } |
| 3908 | |||
| 3909 | ASAN_UNPOISON_SYMBOL (&symbol_block->symbols[symbol_block_index]); | ||
| 3702 | XSETSYMBOL (val, &symbol_block->symbols[symbol_block_index]); | 3910 | XSETSYMBOL (val, &symbol_block->symbols[symbol_block_index]); |
| 3703 | symbol_block_index++; | 3911 | symbol_block_index++; |
| 3704 | } | 3912 | } |
| @@ -4624,6 +4832,11 @@ static struct Lisp_String * | |||
| 4624 | live_string_holding (struct mem_node *m, void *p) | 4832 | live_string_holding (struct mem_node *m, void *p) |
| 4625 | { | 4833 | { |
| 4626 | eassert (m->type == MEM_TYPE_STRING); | 4834 | eassert (m->type == MEM_TYPE_STRING); |
| 4835 | #if GC_ASAN_POISON_OBJECTS | ||
| 4836 | if (__asan_address_is_poisoned (p)) | ||
| 4837 | return NULL; | ||
| 4838 | #endif | ||
| 4839 | |||
| 4627 | struct string_block *b = m->start; | 4840 | struct string_block *b = m->start; |
| 4628 | char *cp = p; | 4841 | char *cp = p; |
| 4629 | ptrdiff_t offset = cp - (char *) &b->strings[0]; | 4842 | ptrdiff_t offset = cp - (char *) &b->strings[0]; |
| @@ -4640,6 +4853,10 @@ live_string_holding (struct mem_node *m, void *p) | |||
| 4640 | || off == offsetof (struct Lisp_String, u.s.data)) | 4853 | || off == offsetof (struct Lisp_String, u.s.data)) |
| 4641 | { | 4854 | { |
| 4642 | struct Lisp_String *s = p = cp -= off; | 4855 | struct Lisp_String *s = p = cp -= off; |
| 4856 | #if GC_ASAN_POISON_OBJECTS | ||
| 4857 | if (__asan_region_is_poisoned (s, sizeof (*s))) | ||
| 4858 | return NULL; | ||
| 4859 | #endif | ||
| 4643 | if (s->u.s.data) | 4860 | if (s->u.s.data) |
| 4644 | return s; | 4861 | return s; |
| 4645 | } | 4862 | } |
| @@ -4661,6 +4878,11 @@ static struct Lisp_Cons * | |||
| 4661 | live_cons_holding (struct mem_node *m, void *p) | 4878 | live_cons_holding (struct mem_node *m, void *p) |
| 4662 | { | 4879 | { |
| 4663 | eassert (m->type == MEM_TYPE_CONS); | 4880 | eassert (m->type == MEM_TYPE_CONS); |
| 4881 | #if GC_ASAN_POISON_OBJECTS | ||
| 4882 | if (__asan_address_is_poisoned (p)) | ||
| 4883 | return NULL; | ||
| 4884 | #endif | ||
| 4885 | |||
| 4664 | struct cons_block *b = m->start; | 4886 | struct cons_block *b = m->start; |
| 4665 | char *cp = p; | 4887 | char *cp = p; |
| 4666 | ptrdiff_t offset = cp - (char *) &b->conses[0]; | 4888 | ptrdiff_t offset = cp - (char *) &b->conses[0]; |
| @@ -4678,6 +4900,10 @@ live_cons_holding (struct mem_node *m, void *p) | |||
| 4678 | || off == offsetof (struct Lisp_Cons, u.s.u.cdr)) | 4900 | || off == offsetof (struct Lisp_Cons, u.s.u.cdr)) |
| 4679 | { | 4901 | { |
| 4680 | struct Lisp_Cons *s = p = cp -= off; | 4902 | struct Lisp_Cons *s = p = cp -= off; |
| 4903 | #if GC_ASAN_POISON_OBJECTS | ||
| 4904 | if (__asan_region_is_poisoned (s, sizeof (*s))) | ||
| 4905 | return NULL; | ||
| 4906 | #endif | ||
| 4681 | if (!deadp (s->u.s.car)) | 4907 | if (!deadp (s->u.s.car)) |
| 4682 | return s; | 4908 | return s; |
| 4683 | } | 4909 | } |
| @@ -4700,6 +4926,10 @@ static struct Lisp_Symbol * | |||
| 4700 | live_symbol_holding (struct mem_node *m, void *p) | 4926 | live_symbol_holding (struct mem_node *m, void *p) |
| 4701 | { | 4927 | { |
| 4702 | eassert (m->type == MEM_TYPE_SYMBOL); | 4928 | eassert (m->type == MEM_TYPE_SYMBOL); |
| 4929 | #if GC_ASAN_POISON_OBJECTS | ||
| 4930 | if (__asan_address_is_poisoned (p)) | ||
| 4931 | return NULL; | ||
| 4932 | #endif | ||
| 4703 | struct symbol_block *b = m->start; | 4933 | struct symbol_block *b = m->start; |
| 4704 | char *cp = p; | 4934 | char *cp = p; |
| 4705 | ptrdiff_t offset = cp - (char *) &b->symbols[0]; | 4935 | ptrdiff_t offset = cp - (char *) &b->symbols[0]; |
| @@ -4725,6 +4955,10 @@ live_symbol_holding (struct mem_node *m, void *p) | |||
| 4725 | || off == offsetof (struct Lisp_Symbol, u.s.next)) | 4955 | || off == offsetof (struct Lisp_Symbol, u.s.next)) |
| 4726 | { | 4956 | { |
| 4727 | struct Lisp_Symbol *s = p = cp -= off; | 4957 | struct Lisp_Symbol *s = p = cp -= off; |
| 4958 | #if GC_ASAN_POISON_OBJECTS | ||
| 4959 | if (__asan_region_is_poisoned (s, sizeof (*s))) | ||
| 4960 | return NULL; | ||
| 4961 | #endif | ||
| 4728 | if (!deadp (s->u.s.function)) | 4962 | if (!deadp (s->u.s.function)) |
| 4729 | return s; | 4963 | return s; |
| 4730 | } | 4964 | } |
| @@ -4747,6 +4981,11 @@ static struct Lisp_Float * | |||
| 4747 | live_float_holding (struct mem_node *m, void *p) | 4981 | live_float_holding (struct mem_node *m, void *p) |
| 4748 | { | 4982 | { |
| 4749 | eassert (m->type == MEM_TYPE_FLOAT); | 4983 | eassert (m->type == MEM_TYPE_FLOAT); |
| 4984 | #if GC_ASAN_POISON_OBJECTS | ||
| 4985 | if (__asan_address_is_poisoned (p)) | ||
| 4986 | return NULL; | ||
| 4987 | #endif | ||
| 4988 | |||
| 4750 | struct float_block *b = m->start; | 4989 | struct float_block *b = m->start; |
| 4751 | char *cp = p; | 4990 | char *cp = p; |
| 4752 | ptrdiff_t offset = cp - (char *) &b->floats[0]; | 4991 | ptrdiff_t offset = cp - (char *) &b->floats[0]; |
| @@ -4761,8 +5000,12 @@ live_float_holding (struct mem_node *m, void *p) | |||
| 4761 | && (b != float_block | 5000 | && (b != float_block |
| 4762 | || offset / sizeof b->floats[0] < float_block_index)) | 5001 | || offset / sizeof b->floats[0] < float_block_index)) |
| 4763 | { | 5002 | { |
| 4764 | p = cp - off; | 5003 | struct Lisp_Float *f = (struct Lisp_Float *) (cp - off); |
| 4765 | return p; | 5004 | #if GC_ASAN_POISON_OBJECTS |
| 5005 | if (__asan_region_is_poisoned (f, sizeof (*f))) | ||
| 5006 | return NULL; | ||
| 5007 | #endif | ||
| 5008 | return f; | ||
| 4766 | } | 5009 | } |
| 4767 | } | 5010 | } |
| 4768 | return NULL; | 5011 | return NULL; |
| @@ -5338,7 +5581,8 @@ valid_lisp_object_p (Lisp_Object obj) | |||
| 5338 | if (valid <= 0) | 5581 | if (valid <= 0) |
| 5339 | return valid; | 5582 | return valid; |
| 5340 | 5583 | ||
| 5341 | if (SUBRP (obj)) | 5584 | /* Strings and conses produced by AUTO_STRING etc. all get here. */ |
| 5585 | if (SUBRP (obj) || STRINGP (obj) || CONSP (obj)) | ||
| 5342 | return 1; | 5586 | return 1; |
| 5343 | 5587 | ||
| 5344 | #ifdef HAVE_STATIC_LISP_GLOBALS | 5588 | #ifdef HAVE_STATIC_LISP_GLOBALS |
| @@ -6287,6 +6531,7 @@ garbage_collect (void) | |||
| 6287 | 6531 | ||
| 6288 | #ifdef HAVE_X_WINDOWS | 6532 | #ifdef HAVE_X_WINDOWS |
| 6289 | mark_xterm (); | 6533 | mark_xterm (); |
| 6534 | mark_xselect (); | ||
| 6290 | #endif | 6535 | #endif |
| 6291 | 6536 | ||
| 6292 | #ifdef HAVE_NS | 6537 | #ifdef HAVE_NS |
| @@ -6647,7 +6892,7 @@ mark_buffer (struct buffer *buffer) | |||
| 6647 | if (!BUFFER_LIVE_P (buffer)) | 6892 | if (!BUFFER_LIVE_P (buffer)) |
| 6648 | mark_object (BVAR (buffer, undo_list)); | 6893 | mark_object (BVAR (buffer, undo_list)); |
| 6649 | 6894 | ||
| 6650 | if (buffer->overlays) | 6895 | if (!itree_empty_p (buffer->overlays)) |
| 6651 | mark_overlays (buffer->overlays->root); | 6896 | mark_overlays (buffer->overlays->root); |
| 6652 | 6897 | ||
| 6653 | /* If this is an indirect buffer, mark its base buffer. */ | 6898 | /* If this is an indirect buffer, mark its base buffer. */ |
| @@ -7292,11 +7537,13 @@ sweep_conses (void) | |||
| 7292 | struct Lisp_Cons *acons = &cblk->conses[pos]; | 7537 | struct Lisp_Cons *acons = &cblk->conses[pos]; |
| 7293 | if (!XCONS_MARKED_P (acons)) | 7538 | if (!XCONS_MARKED_P (acons)) |
| 7294 | { | 7539 | { |
| 7540 | ASAN_UNPOISON_CONS (&cblk->conses[pos]); | ||
| 7295 | this_free++; | 7541 | this_free++; |
| 7296 | cblk->conses[pos].u.s.u.chain = cons_free_list; | 7542 | cblk->conses[pos].u.s.u.chain = cons_free_list; |
| 7297 | cons_free_list = &cblk->conses[pos]; | 7543 | cons_free_list = &cblk->conses[pos]; |
| 7298 | cons_free_list->u.s.car = dead_object (); | 7544 | cons_free_list->u.s.car = dead_object (); |
| 7299 | } | 7545 | ASAN_POISON_CONS (&cblk->conses[pos]); |
| 7546 | } | ||
| 7300 | else | 7547 | else |
| 7301 | { | 7548 | { |
| 7302 | num_used++; | 7549 | num_used++; |
| @@ -7314,6 +7561,7 @@ sweep_conses (void) | |||
| 7314 | { | 7561 | { |
| 7315 | *cprev = cblk->next; | 7562 | *cprev = cblk->next; |
| 7316 | /* Unhook from the free list. */ | 7563 | /* Unhook from the free list. */ |
| 7564 | ASAN_UNPOISON_CONS (&cblk->conses[0]); | ||
| 7317 | cons_free_list = cblk->conses[0].u.s.u.chain; | 7565 | cons_free_list = cblk->conses[0].u.s.u.chain; |
| 7318 | lisp_align_free (cblk); | 7566 | lisp_align_free (cblk); |
| 7319 | } | 7567 | } |
| @@ -7340,6 +7588,7 @@ sweep_floats (void) | |||
| 7340 | for (struct float_block *fblk; (fblk = *fprev); ) | 7588 | for (struct float_block *fblk; (fblk = *fprev); ) |
| 7341 | { | 7589 | { |
| 7342 | int this_free = 0; | 7590 | int this_free = 0; |
| 7591 | ASAN_UNPOISON_FLOAT_BLOCK (fblk); | ||
| 7343 | for (int i = 0; i < lim; i++) | 7592 | for (int i = 0; i < lim; i++) |
| 7344 | { | 7593 | { |
| 7345 | struct Lisp_Float *afloat = &fblk->floats[i]; | 7594 | struct Lisp_Float *afloat = &fblk->floats[i]; |
| @@ -7347,6 +7596,7 @@ sweep_floats (void) | |||
| 7347 | { | 7596 | { |
| 7348 | this_free++; | 7597 | this_free++; |
| 7349 | fblk->floats[i].u.chain = float_free_list; | 7598 | fblk->floats[i].u.chain = float_free_list; |
| 7599 | ASAN_POISON_FLOAT (&fblk->floats[i]); | ||
| 7350 | float_free_list = &fblk->floats[i]; | 7600 | float_free_list = &fblk->floats[i]; |
| 7351 | } | 7601 | } |
| 7352 | else | 7602 | else |
| @@ -7363,7 +7613,8 @@ sweep_floats (void) | |||
| 7363 | { | 7613 | { |
| 7364 | *fprev = fblk->next; | 7614 | *fprev = fblk->next; |
| 7365 | /* Unhook from the free list. */ | 7615 | /* Unhook from the free list. */ |
| 7366 | float_free_list = fblk->floats[0].u.chain; | 7616 | ASAN_UNPOISON_FLOAT (&fblk->floats[0]); |
| 7617 | float_free_list = fblk->floats[0].u.chain; | ||
| 7367 | lisp_align_free (fblk); | 7618 | lisp_align_free (fblk); |
| 7368 | } | 7619 | } |
| 7369 | else | 7620 | else |
| @@ -7389,13 +7640,14 @@ sweep_intervals (void) | |||
| 7389 | for (struct interval_block *iblk; (iblk = *iprev); ) | 7640 | for (struct interval_block *iblk; (iblk = *iprev); ) |
| 7390 | { | 7641 | { |
| 7391 | int this_free = 0; | 7642 | int this_free = 0; |
| 7392 | 7643 | ASAN_UNPOISON_INTERVAL_BLOCK (iblk); | |
| 7393 | for (int i = 0; i < lim; i++) | 7644 | for (int i = 0; i < lim; i++) |
| 7394 | { | 7645 | { |
| 7395 | if (!iblk->intervals[i].gcmarkbit) | 7646 | if (!iblk->intervals[i].gcmarkbit) |
| 7396 | { | 7647 | { |
| 7397 | set_interval_parent (&iblk->intervals[i], interval_free_list); | 7648 | set_interval_parent (&iblk->intervals[i], interval_free_list); |
| 7398 | interval_free_list = &iblk->intervals[i]; | 7649 | interval_free_list = &iblk->intervals[i]; |
| 7650 | ASAN_POISON_INTERVAL (&iblk->intervals[i]); | ||
| 7399 | this_free++; | 7651 | this_free++; |
| 7400 | } | 7652 | } |
| 7401 | else | 7653 | else |
| @@ -7412,6 +7664,7 @@ sweep_intervals (void) | |||
| 7412 | { | 7664 | { |
| 7413 | *iprev = iblk->next; | 7665 | *iprev = iblk->next; |
| 7414 | /* Unhook from the free list. */ | 7666 | /* Unhook from the free list. */ |
| 7667 | ASAN_UNPOISON_INTERVAL (&iblk->intervals[0]); | ||
| 7415 | interval_free_list = INTERVAL_PARENT (&iblk->intervals[0]); | 7668 | interval_free_list = INTERVAL_PARENT (&iblk->intervals[0]); |
| 7416 | lisp_free (iblk); | 7669 | lisp_free (iblk); |
| 7417 | } | 7670 | } |
| @@ -7441,6 +7694,8 @@ sweep_symbols (void) | |||
| 7441 | 7694 | ||
| 7442 | for (sblk = symbol_block; sblk; sblk = *sprev) | 7695 | for (sblk = symbol_block; sblk; sblk = *sprev) |
| 7443 | { | 7696 | { |
| 7697 | ASAN_UNPOISON_SYMBOL_BLOCK (sblk); | ||
| 7698 | |||
| 7444 | int this_free = 0; | 7699 | int this_free = 0; |
| 7445 | struct Lisp_Symbol *sym = sblk->symbols; | 7700 | struct Lisp_Symbol *sym = sblk->symbols; |
| 7446 | struct Lisp_Symbol *end = sym + lim; | 7701 | struct Lisp_Symbol *end = sym + lim; |
| @@ -7462,7 +7717,8 @@ sweep_symbols (void) | |||
| 7462 | sym->u.s.next = symbol_free_list; | 7717 | sym->u.s.next = symbol_free_list; |
| 7463 | symbol_free_list = sym; | 7718 | symbol_free_list = sym; |
| 7464 | symbol_free_list->u.s.function = dead_object (); | 7719 | symbol_free_list->u.s.function = dead_object (); |
| 7465 | ++this_free; | 7720 | ASAN_POISON_SYMBOL (sym); |
| 7721 | ++this_free; | ||
| 7466 | } | 7722 | } |
| 7467 | else | 7723 | else |
| 7468 | { | 7724 | { |
| @@ -7481,6 +7737,7 @@ sweep_symbols (void) | |||
| 7481 | { | 7737 | { |
| 7482 | *sprev = sblk->next; | 7738 | *sprev = sblk->next; |
| 7483 | /* Unhook from the free list. */ | 7739 | /* Unhook from the free list. */ |
| 7740 | ASAN_UNPOISON_SYMBOL (&sblk->symbols[0]); | ||
| 7484 | symbol_free_list = sblk->symbols[0].u.s.next; | 7741 | symbol_free_list = sblk->symbols[0].u.s.next; |
| 7485 | lisp_free (sblk); | 7742 | lisp_free (sblk); |
| 7486 | } | 7743 | } |
| @@ -7548,9 +7805,17 @@ DEFUN ("memory-info", Fmemory_info, Smemory_info, 0, 0, 0, | |||
| 7548 | doc: /* Return a list of (TOTAL-RAM FREE-RAM TOTAL-SWAP FREE-SWAP). | 7805 | doc: /* Return a list of (TOTAL-RAM FREE-RAM TOTAL-SWAP FREE-SWAP). |
| 7549 | All values are in Kbytes. If there is no swap space, | 7806 | All values are in Kbytes. If there is no swap space, |
| 7550 | last two values are zero. If the system is not supported | 7807 | last two values are zero. If the system is not supported |
| 7551 | or memory information can't be obtained, return nil. */) | 7808 | or memory information can't be obtained, return nil. |
| 7809 | If `default-directory’ is remote, return memory information of the | ||
| 7810 | respective remote host. */) | ||
| 7552 | (void) | 7811 | (void) |
| 7553 | { | 7812 | { |
| 7813 | Lisp_Object handler | ||
| 7814 | = Ffind_file_name_handler (BVAR (current_buffer, directory), | ||
| 7815 | Qmemory_info); | ||
| 7816 | if (!NILP (handler)) | ||
| 7817 | return call1 (handler, Qmemory_info); | ||
| 7818 | |||
| 7554 | #if defined HAVE_LINUX_SYSINFO | 7819 | #if defined HAVE_LINUX_SYSINFO |
| 7555 | struct sysinfo si; | 7820 | struct sysinfo si; |
| 7556 | uintmax_t units; | 7821 | uintmax_t units; |
| @@ -7972,6 +8237,8 @@ do hash-consing of the objects allocated to pure space. */); | |||
| 7972 | doc: /* Non-nil means Emacs cannot get much more Lisp memory. */); | 8237 | doc: /* Non-nil means Emacs cannot get much more Lisp memory. */); |
| 7973 | Vmemory_full = Qnil; | 8238 | Vmemory_full = Qnil; |
| 7974 | 8239 | ||
| 8240 | DEFSYM (Qmemory_info, "memory-info"); | ||
| 8241 | |||
| 7975 | DEFSYM (Qconses, "conses"); | 8242 | DEFSYM (Qconses, "conses"); |
| 7976 | DEFSYM (Qsymbols, "symbols"); | 8243 | DEFSYM (Qsymbols, "symbols"); |
| 7977 | DEFSYM (Qstrings, "strings"); | 8244 | DEFSYM (Qstrings, "strings"); |
diff --git a/src/buffer.c b/src/buffer.c index ac7f4f8e9d4..38c3150f2c5 100644 --- a/src/buffer.c +++ b/src/buffer.c | |||
| @@ -604,7 +604,6 @@ even if it is dead. The return value is never nil. */) | |||
| 604 | set_buffer_intervals (b, NULL); | 604 | set_buffer_intervals (b, NULL); |
| 605 | BUF_UNCHANGED_MODIFIED (b) = 1; | 605 | BUF_UNCHANGED_MODIFIED (b) = 1; |
| 606 | BUF_OVERLAY_UNCHANGED_MODIFIED (b) = 1; | 606 | BUF_OVERLAY_UNCHANGED_MODIFIED (b) = 1; |
| 607 | BUF_CHARS_UNCHANGED_MODIFIED (b) = 1; | ||
| 608 | BUF_END_UNCHANGED (b) = 0; | 607 | BUF_END_UNCHANGED (b) = 0; |
| 609 | BUF_BEG_UNCHANGED (b) = 0; | 608 | BUF_BEG_UNCHANGED (b) = 0; |
| 610 | *(BUF_GPT_ADDR (b)) = *(BUF_Z_ADDR (b)) = 0; /* Put an anchor '\0'. */ | 609 | *(BUF_GPT_ADDR (b)) = *(BUF_Z_ADDR (b)) = 0; /* Put an anchor '\0'. */ |
| @@ -1748,7 +1747,19 @@ other_buffer_safely (Lisp_Object buffer) | |||
| 1748 | if (candidate_buffer (buf, buffer)) | 1747 | if (candidate_buffer (buf, buffer)) |
| 1749 | return buf; | 1748 | return buf; |
| 1750 | 1749 | ||
| 1751 | return safe_call (1, Qget_scratch_buffer_create); | 1750 | /* This function must return a valid buffer, since it is frequently |
| 1751 | our last line of defense in the face of the expected buffers | ||
| 1752 | becoming dead under our feet. safe_call below could return nil | ||
| 1753 | if recreating *scratch* in Lisp, which does some fancy stuff, | ||
| 1754 | signals an error in some weird use case. */ | ||
| 1755 | buf = safe_call (1, Qget_scratch_buffer_create); | ||
| 1756 | if (NILP (buf)) | ||
| 1757 | { | ||
| 1758 | AUTO_STRING (scratch, "*scratch*"); | ||
| 1759 | buf = Fget_buffer_create (scratch, Qnil); | ||
| 1760 | Fset_buffer_major_mode (buf); | ||
| 1761 | } | ||
| 1762 | return buf; | ||
| 1752 | } | 1763 | } |
| 1753 | 1764 | ||
| 1754 | DEFUN ("buffer-enable-undo", Fbuffer_enable_undo, Sbuffer_enable_undo, | 1765 | DEFUN ("buffer-enable-undo", Fbuffer_enable_undo, Sbuffer_enable_undo, |
| @@ -3899,11 +3910,11 @@ the value is (point-min). */) | |||
| 3899 | /* These functions are for debugging overlays. */ | 3910 | /* These functions are for debugging overlays. */ |
| 3900 | 3911 | ||
| 3901 | DEFUN ("overlay-lists", Foverlay_lists, Soverlay_lists, 0, 0, 0, | 3912 | DEFUN ("overlay-lists", Foverlay_lists, Soverlay_lists, 0, 0, 0, |
| 3902 | doc: /* Return a pair of lists giving all the overlays of the current buffer. | 3913 | doc: /* Return a list giving all the overlays of the current buffer. |
| 3903 | The car has all the overlays before the overlay center; | 3914 | |
| 3904 | the cdr has all the overlays after the overlay center. | 3915 | For backward compatibility, the value is actually a list that |
| 3905 | Recentering overlays moves overlays between these lists. | 3916 | holds another list; the overlays are in the inner list. |
| 3906 | The lists you get are copies, so that changing them has no effect. | 3917 | The list you get is a copy, so that changing it has no effect. |
| 3907 | However, the overlays you get are the real objects that the buffer uses. */) | 3918 | However, the overlays you get are the real objects that the buffer uses. */) |
| 3908 | (void) | 3919 | (void) |
| 3909 | { | 3920 | { |
| @@ -3919,7 +3930,12 @@ However, the overlays you get are the real objects that the buffer uses. */) | |||
| 3919 | DEFUN ("overlay-recenter", Foverlay_recenter, Soverlay_recenter, 1, 1, 0, | 3930 | DEFUN ("overlay-recenter", Foverlay_recenter, Soverlay_recenter, 1, 1, 0, |
| 3920 | doc: /* Recenter the overlays of the current buffer around position POS. | 3931 | doc: /* Recenter the overlays of the current buffer around position POS. |
| 3921 | That makes overlay lookup faster for positions near POS (but perhaps slower | 3932 | That makes overlay lookup faster for positions near POS (but perhaps slower |
| 3922 | for positions far away from POS). */) | 3933 | for positions far away from POS). |
| 3934 | |||
| 3935 | Since Emacs 29.1, this function is a no-op, because the implementation | ||
| 3936 | of overlays changed and their lookup is now fast regardless of their | ||
| 3937 | position in the buffer. In particular, this function no longer affects | ||
| 3938 | the value returned by `overlay-lists'. */) | ||
| 3923 | (Lisp_Object pos) | 3939 | (Lisp_Object pos) |
| 3924 | { | 3940 | { |
| 3925 | CHECK_FIXNUM_COERCE_MARKER (pos); | 3941 | CHECK_FIXNUM_COERCE_MARKER (pos); |
| @@ -5898,7 +5914,42 @@ this threshold. | |||
| 5898 | If nil, these display shortcuts will always remain disabled. | 5914 | If nil, these display shortcuts will always remain disabled. |
| 5899 | 5915 | ||
| 5900 | There is no reason to change that value except for debugging purposes. */); | 5916 | There is no reason to change that value except for debugging purposes. */); |
| 5901 | XSETFASTINT (Vlong_line_threshold, 10000); | 5917 | XSETFASTINT (Vlong_line_threshold, 50000); |
| 5918 | |||
| 5919 | DEFVAR_INT ("long-line-locked-narrowing-region-size", | ||
| 5920 | long_line_locked_narrowing_region_size, | ||
| 5921 | doc: /* Region size for locked narrowing in buffers with long lines. | ||
| 5922 | |||
| 5923 | This variable has effect only in buffers which contain one or more | ||
| 5924 | lines whose length is above `long-line-threshold', which see. For | ||
| 5925 | performance reasons, in such buffers, low-level hooks such as | ||
| 5926 | `fontification-functions' or `post-command-hook' are executed on a | ||
| 5927 | narrowed buffer, with a narrowing locked with `narrowing-lock'. This | ||
| 5928 | variable specifies the size of the narrowed region around point. | ||
| 5929 | |||
| 5930 | To disable that narrowing, set this variable to 0. | ||
| 5931 | |||
| 5932 | See also `long-line-locked-narrowing-bol-search-limit'. | ||
| 5933 | |||
| 5934 | There is no reason to change that value except for debugging purposes. */); | ||
| 5935 | long_line_locked_narrowing_region_size = 500000; | ||
| 5936 | |||
| 5937 | DEFVAR_INT ("long-line-locked-narrowing-bol-search-limit", | ||
| 5938 | long_line_locked_narrowing_bol_search_limit, | ||
| 5939 | doc: /* Limit for beginning of line search in buffers with long lines. | ||
| 5940 | |||
| 5941 | This variable has effect only in buffers which contain one or more | ||
| 5942 | lines whose length is above `long-line-threshold', which see. For | ||
| 5943 | performance reasons, in such buffers, low-level hooks such as | ||
| 5944 | `fontification-functions' or `post-command-hook' are executed on a | ||
| 5945 | narrowed buffer, with a narrowing locked with `narrowing-lock'. The | ||
| 5946 | variable `long-line-locked-narrowing-region-size' specifies the size | ||
| 5947 | of the narrowed region around point. This variable, which should be a | ||
| 5948 | small integer, specifies the number of characters by which that region | ||
| 5949 | can be extended backwards to make it start at the beginning of a line. | ||
| 5950 | |||
| 5951 | There is no reason to change that value except for debugging purposes. */); | ||
| 5952 | long_line_locked_narrowing_bol_search_limit = 128; | ||
| 5902 | 5953 | ||
| 5903 | DEFVAR_INT ("large-hscroll-threshold", large_hscroll_threshold, | 5954 | DEFVAR_INT ("large-hscroll-threshold", large_hscroll_threshold, |
| 5904 | doc: /* Horizontal scroll of truncated lines above which to use redisplay shortcuts. | 5955 | doc: /* Horizontal scroll of truncated lines above which to use redisplay shortcuts. |
diff --git a/src/buffer.h b/src/buffer.h index dded0cd98c1..7c3d1903140 100644 --- a/src/buffer.h +++ b/src/buffer.h | |||
| @@ -149,18 +149,12 @@ enum { BEG = 1, BEG_BYTE = BEG }; | |||
| 149 | #define BUF_BEG_UNCHANGED(buf) ((buf)->text->beg_unchanged) | 149 | #define BUF_BEG_UNCHANGED(buf) ((buf)->text->beg_unchanged) |
| 150 | #define BUF_END_UNCHANGED(buf) ((buf)->text->end_unchanged) | 150 | #define BUF_END_UNCHANGED(buf) ((buf)->text->end_unchanged) |
| 151 | 151 | ||
| 152 | #define BUF_CHARS_UNCHANGED_MODIFIED(buf) \ | ||
| 153 | ((buf)->text->chars_unchanged_modified) | ||
| 154 | |||
| 155 | #define UNCHANGED_MODIFIED \ | 152 | #define UNCHANGED_MODIFIED \ |
| 156 | BUF_UNCHANGED_MODIFIED (current_buffer) | 153 | BUF_UNCHANGED_MODIFIED (current_buffer) |
| 157 | #define OVERLAY_UNCHANGED_MODIFIED \ | 154 | #define OVERLAY_UNCHANGED_MODIFIED \ |
| 158 | BUF_OVERLAY_UNCHANGED_MODIFIED (current_buffer) | 155 | BUF_OVERLAY_UNCHANGED_MODIFIED (current_buffer) |
| 159 | #define BEG_UNCHANGED BUF_BEG_UNCHANGED (current_buffer) | 156 | #define BEG_UNCHANGED BUF_BEG_UNCHANGED (current_buffer) |
| 160 | #define END_UNCHANGED BUF_END_UNCHANGED (current_buffer) | 157 | #define END_UNCHANGED BUF_END_UNCHANGED (current_buffer) |
| 161 | |||
| 162 | #define CHARS_UNCHANGED_MODIFIED \ | ||
| 163 | BUF_CHARS_UNCHANGED_MODIFIED (current_buffer) | ||
| 164 | 158 | ||
| 165 | /* Functions to set PT in the current buffer, or another buffer. */ | 159 | /* Functions to set PT in the current buffer, or another buffer. */ |
| 166 | 160 | ||
| @@ -274,11 +268,6 @@ struct buffer_text | |||
| 274 | end_unchanged contain no useful information. */ | 268 | end_unchanged contain no useful information. */ |
| 275 | modiff_count overlay_unchanged_modified; | 269 | modiff_count overlay_unchanged_modified; |
| 276 | 270 | ||
| 277 | /* CHARS_MODIFF as of last redisplay that finished. It's used | ||
| 278 | when we only care about changes in actual buffer text, not in | ||
| 279 | any other kind of changes, like properties etc. */ | ||
| 280 | modiff_count chars_unchanged_modified; | ||
| 281 | |||
| 282 | /* Properties of this buffer's text. */ | 271 | /* Properties of this buffer's text. */ |
| 283 | INTERVAL intervals; | 272 | INTERVAL intervals; |
| 284 | 273 | ||
| @@ -1277,8 +1266,7 @@ set_buffer_intervals (struct buffer *b, INTERVAL i) | |||
| 1277 | INLINE bool | 1266 | INLINE bool |
| 1278 | buffer_has_overlays (void) | 1267 | buffer_has_overlays (void) |
| 1279 | { | 1268 | { |
| 1280 | return current_buffer->overlays | 1269 | return !itree_empty_p (current_buffer->overlays); |
| 1281 | && (current_buffer->overlays->root != NULL); | ||
| 1282 | } | 1270 | } |
| 1283 | 1271 | ||
| 1284 | /* Functions for accessing a character or byte, | 1272 | /* Functions for accessing a character or byte, |
diff --git a/src/data.c b/src/data.c index c6b85e17bc2..7ad06a9faa5 100644 --- a/src/data.c +++ b/src/data.c | |||
| @@ -2619,6 +2619,7 @@ bool-vector. IDX starts at 0. */) | |||
| 2619 | } | 2619 | } |
| 2620 | else if (RECORDP (array)) | 2620 | else if (RECORDP (array)) |
| 2621 | { | 2621 | { |
| 2622 | CHECK_IMPURE (array, XVECTOR (array)); | ||
| 2622 | if (idxval < 0 || idxval >= PVSIZE (array)) | 2623 | if (idxval < 0 || idxval >= PVSIZE (array)) |
| 2623 | args_out_of_range (array, idx); | 2624 | args_out_of_range (array, idx); |
| 2624 | ASET (array, idxval, newelt); | 2625 | ASET (array, idxval, newelt); |
diff --git a/src/dispextern.h b/src/dispextern.h index 2afbdeabaab..df6134e68f0 100644 --- a/src/dispextern.h +++ b/src/dispextern.h | |||
| @@ -2342,6 +2342,14 @@ struct it | |||
| 2342 | optimize display. */ | 2342 | optimize display. */ |
| 2343 | ptrdiff_t narrowed_zv; | 2343 | ptrdiff_t narrowed_zv; |
| 2344 | 2344 | ||
| 2345 | /* Begin position of the buffer for the locked narrowing around | ||
| 2346 | low-level hooks. */ | ||
| 2347 | ptrdiff_t locked_narrowing_begv; | ||
| 2348 | |||
| 2349 | /* End position of the buffer for the locked narrowing around | ||
| 2350 | low-level hooks. */ | ||
| 2351 | ptrdiff_t locked_narrowing_zv; | ||
| 2352 | |||
| 2345 | /* C string to iterate over. Non-null means get characters from | 2353 | /* C string to iterate over. Non-null means get characters from |
| 2346 | this string, otherwise characters are read from current_buffer | 2354 | this string, otherwise characters are read from current_buffer |
| 2347 | or it->string. */ | 2355 | or it->string. */ |
| @@ -3405,6 +3413,8 @@ void init_iterator (struct it *, struct window *, ptrdiff_t, | |||
| 3405 | ptrdiff_t get_narrowed_begv (struct window *, ptrdiff_t); | 3413 | ptrdiff_t get_narrowed_begv (struct window *, ptrdiff_t); |
| 3406 | ptrdiff_t get_narrowed_zv (struct window *, ptrdiff_t); | 3414 | ptrdiff_t get_narrowed_zv (struct window *, ptrdiff_t); |
| 3407 | ptrdiff_t get_closer_narrowed_begv (struct window *, ptrdiff_t); | 3415 | ptrdiff_t get_closer_narrowed_begv (struct window *, ptrdiff_t); |
| 3416 | ptrdiff_t get_locked_narrowing_begv (ptrdiff_t); | ||
| 3417 | ptrdiff_t get_locked_narrowing_zv (ptrdiff_t); | ||
| 3408 | void init_iterator_to_row_start (struct it *, struct window *, | 3418 | void init_iterator_to_row_start (struct it *, struct window *, |
| 3409 | struct glyph_row *); | 3419 | struct glyph_row *); |
| 3410 | void start_display (struct it *, struct window *, struct text_pos); | 3420 | void start_display (struct it *, struct window *, struct text_pos); |
diff --git a/src/editfns.c b/src/editfns.c index 17dca4708ed..8d56ef21d90 100644 --- a/src/editfns.c +++ b/src/editfns.c | |||
| @@ -2653,88 +2653,216 @@ DEFUN ("delete-and-extract-region", Fdelete_and_extract_region, | |||
| 2653 | return del_range_1 (XFIXNUM (start), XFIXNUM (end), 1, 1); | 2653 | return del_range_1 (XFIXNUM (start), XFIXNUM (end), 1, 1); |
| 2654 | } | 2654 | } |
| 2655 | 2655 | ||
| 2656 | DEFUN ("widen", Fwiden, Swiden, 0, 0, "", | 2656 | /* Alist of buffers in which locked narrowing is used. The car of |
| 2657 | doc: /* Remove restrictions (narrowing) from current buffer. | 2657 | each list element is a buffer, the cdr is a list of triplets (tag |
| 2658 | This allows the buffer's full text to be seen and edited. | 2658 | begv-marker zv-marker). The last element of that list always uses |
| 2659 | the (uninterned) Qoutermost_narrowing tag and records the narrowing | ||
| 2660 | bounds that were set by the user and that are visible on display. | ||
| 2661 | This alist is used internally by narrow-to-region, widen, | ||
| 2662 | narrowing-lock, narrowing-unlock and save-restriction. */ | ||
| 2663 | static Lisp_Object narrowing_locks; | ||
| 2664 | |||
| 2665 | /* Add BUF with its LOCKS in the narrowing_locks alist. */ | ||
| 2666 | static void | ||
| 2667 | narrowing_locks_add (Lisp_Object buf, Lisp_Object locks) | ||
| 2668 | { | ||
| 2669 | narrowing_locks = nconc2 (list1 (list2 (buf, locks)), narrowing_locks); | ||
| 2670 | } | ||
| 2659 | 2671 | ||
| 2660 | Note that, when the current buffer contains one or more lines whose | 2672 | /* Remove BUF and its locks from the narrowing_locks alist. Do |
| 2661 | length is above `long-line-threshold', Emacs may decide to leave, for | 2673 | nothing if BUF is not present in narrowing_locks. */ |
| 2662 | performance reasons, the accessible portion of the buffer unchanged | 2674 | static void |
| 2663 | after this function is called from low-level hooks, such as | 2675 | narrowing_locks_remove (Lisp_Object buf) |
| 2664 | `jit-lock-functions' or `post-command-hook'. */) | 2676 | { |
| 2665 | (void) | 2677 | narrowing_locks = Fdelq (Fassoc (buf, narrowing_locks, Qnil), |
| 2678 | narrowing_locks); | ||
| 2679 | } | ||
| 2680 | |||
| 2681 | /* Retrieve one of the BEGV/ZV bounds of a narrowing in BUF from the | ||
| 2682 | narrowing_locks alist, as a pointer to a struct Lisp_Marker, or | ||
| 2683 | NULL if BUF is not in narrowing_locks or is a killed buffer. When | ||
| 2684 | OUTERMOST is true, the bounds that were set by the user and that | ||
| 2685 | are visible on display are returned. Otherwise the innermost | ||
| 2686 | locked narrowing bounds are returned. */ | ||
| 2687 | static struct Lisp_Marker * | ||
| 2688 | narrowing_lock_get_bound (Lisp_Object buf, bool begv, bool outermost) | ||
| 2689 | { | ||
| 2690 | if (NILP (Fbuffer_live_p (buf))) | ||
| 2691 | return NULL; | ||
| 2692 | Lisp_Object buffer_locks = assq_no_quit (buf, narrowing_locks); | ||
| 2693 | if (NILP (buffer_locks)) | ||
| 2694 | return NULL; | ||
| 2695 | buffer_locks = XCAR (XCDR (buffer_locks)); | ||
| 2696 | Lisp_Object bounds | ||
| 2697 | = outermost | ||
| 2698 | ? XCDR (assq_no_quit (Qoutermost_narrowing, buffer_locks)) | ||
| 2699 | : XCDR (XCAR (buffer_locks)); | ||
| 2700 | eassert (! NILP (bounds)); | ||
| 2701 | Lisp_Object marker = begv ? XCAR (bounds) : XCAR (XCDR (bounds)); | ||
| 2702 | eassert (EQ (Fmarker_buffer (marker), buf)); | ||
| 2703 | return XMARKER (marker); | ||
| 2704 | } | ||
| 2705 | |||
| 2706 | /* Retrieve the tag of the innermost narrowing in BUF. Return nil if | ||
| 2707 | BUF is not in narrowing_locks or is a killed buffer. */ | ||
| 2708 | static Lisp_Object | ||
| 2709 | narrowing_lock_peek_tag (Lisp_Object buf) | ||
| 2666 | { | 2710 | { |
| 2667 | if (! NILP (Vrestrictions_locked)) | 2711 | if (NILP (Fbuffer_live_p (buf))) |
| 2668 | return Qnil; | 2712 | return Qnil; |
| 2669 | if (BEG != BEGV || Z != ZV) | 2713 | Lisp_Object buffer_locks = assq_no_quit (buf, narrowing_locks); |
| 2670 | current_buffer->clip_changed = 1; | 2714 | if (NILP (buffer_locks)) |
| 2671 | BEGV = BEG; | 2715 | return Qnil; |
| 2672 | BEGV_BYTE = BEG_BYTE; | 2716 | Lisp_Object tag = XCAR (XCAR (XCAR (XCDR (buffer_locks)))); |
| 2673 | SET_BUF_ZV_BOTH (current_buffer, Z, Z_BYTE); | 2717 | eassert (! NILP (tag)); |
| 2674 | /* Changing the buffer bounds invalidates any recorded current column. */ | 2718 | return tag; |
| 2675 | invalidate_current_column (); | ||
| 2676 | return Qnil; | ||
| 2677 | } | 2719 | } |
| 2678 | 2720 | ||
| 2721 | /* Add a LOCK for BUF in the narrowing_locks alist. */ | ||
| 2679 | static void | 2722 | static void |
| 2680 | unwind_locked_begv (Lisp_Object point_min) | 2723 | narrowing_lock_push (Lisp_Object buf, Lisp_Object lock) |
| 2681 | { | 2724 | { |
| 2682 | SET_BUF_BEGV (current_buffer, XFIXNUM (point_min)); | 2725 | Lisp_Object buffer_locks = assq_no_quit (buf, narrowing_locks); |
| 2726 | if (NILP (buffer_locks)) | ||
| 2727 | narrowing_locks_add (buf, list1 (lock)); | ||
| 2728 | else | ||
| 2729 | XSETCDR (buffer_locks, list1 (nconc2 (list1 (lock), | ||
| 2730 | XCAR (XCDR (buffer_locks))))); | ||
| 2683 | } | 2731 | } |
| 2684 | 2732 | ||
| 2733 | /* Remove the innermost lock in BUF from the narrowing_locks alist. | ||
| 2734 | Do nothing if BUF is not present in narrowing_locks. */ | ||
| 2685 | static void | 2735 | static void |
| 2686 | unwind_locked_zv (Lisp_Object point_max) | 2736 | narrowing_lock_pop (Lisp_Object buf) |
| 2687 | { | 2737 | { |
| 2688 | SET_BUF_ZV (current_buffer, XFIXNUM (point_max)); | 2738 | Lisp_Object buffer_locks = assq_no_quit (buf, narrowing_locks); |
| 2739 | if (NILP (buffer_locks)) | ||
| 2740 | return; | ||
| 2741 | if (EQ (narrowing_lock_peek_tag (buf), Qoutermost_narrowing)) | ||
| 2742 | narrowing_locks_remove (buf); | ||
| 2743 | else | ||
| 2744 | XSETCDR (buffer_locks, list1 (XCDR (XCAR (XCDR (buffer_locks))))); | ||
| 2689 | } | 2745 | } |
| 2690 | 2746 | ||
| 2691 | /* Internal function for Fnarrow_to_region, meant to be used with a | 2747 | static void |
| 2692 | third argument 'true', in which case it should be followed by "specbind | 2748 | unwind_reset_outermost_narrowing (Lisp_Object buf) |
| 2693 | (Qrestrictions_locked, Qt)". */ | ||
| 2694 | Lisp_Object | ||
| 2695 | narrow_to_region_internal (Lisp_Object start, Lisp_Object end, bool lock) | ||
| 2696 | { | 2749 | { |
| 2697 | EMACS_INT s = fix_position (start), e = fix_position (end); | 2750 | struct Lisp_Marker *begv = narrowing_lock_get_bound (buf, true, false); |
| 2698 | 2751 | struct Lisp_Marker *zv = narrowing_lock_get_bound (buf, false, false); | |
| 2699 | if (e < s) | 2752 | if (begv != NULL && zv != NULL) |
| 2700 | { | 2753 | { |
| 2701 | EMACS_INT tem = s; s = e; e = tem; | 2754 | SET_BUF_BEGV_BOTH (XBUFFER (buf), begv->charpos, begv->bytepos); |
| 2755 | SET_BUF_ZV_BOTH (XBUFFER (buf), zv->charpos, zv->bytepos); | ||
| 2702 | } | 2756 | } |
| 2757 | else | ||
| 2758 | narrowing_locks_remove (buf); | ||
| 2759 | } | ||
| 2703 | 2760 | ||
| 2704 | if (lock) | 2761 | /* Restore the narrowing bounds that were set by the user, and restore |
| 2762 | the bounds of the locked narrowing upon return. | ||
| 2763 | In particular, this function is called when redisplay starts, so | ||
| 2764 | that if a Lisp function executed during redisplay calls (redisplay) | ||
| 2765 | while a locked narrowing is in effect, the locked narrowing will | ||
| 2766 | not be visible on display. */ | ||
| 2767 | void | ||
| 2768 | reset_outermost_narrowings (void) | ||
| 2769 | { | ||
| 2770 | Lisp_Object val, buf; | ||
| 2771 | for (val = narrowing_locks; CONSP (val); val = XCDR (val)) | ||
| 2705 | { | 2772 | { |
| 2706 | if (!(BEGV <= s && s <= e && e <= ZV)) | 2773 | buf = XCAR (XCAR (val)); |
| 2707 | args_out_of_range (start, end); | 2774 | eassert (BUFFERP (buf)); |
| 2775 | struct Lisp_Marker *begv = narrowing_lock_get_bound (buf, true, true); | ||
| 2776 | struct Lisp_Marker *zv = narrowing_lock_get_bound (buf, false, true); | ||
| 2777 | if (begv != NULL && zv != NULL) | ||
| 2778 | { | ||
| 2779 | SET_BUF_BEGV_BOTH (XBUFFER (buf), begv->charpos, begv->bytepos); | ||
| 2780 | SET_BUF_ZV_BOTH (XBUFFER (buf), zv->charpos, zv->bytepos); | ||
| 2781 | record_unwind_protect (unwind_reset_outermost_narrowing, buf); | ||
| 2782 | } | ||
| 2783 | else | ||
| 2784 | narrowing_locks_remove (buf); | ||
| 2785 | } | ||
| 2786 | } | ||
| 2708 | 2787 | ||
| 2709 | if (BEGV != s || ZV != e) | 2788 | /* Helper functions to save and restore the narrowing locks of the |
| 2710 | current_buffer->clip_changed = 1; | 2789 | current buffer in Fsave_restriction. */ |
| 2790 | static Lisp_Object | ||
| 2791 | narrowing_locks_save (void) | ||
| 2792 | { | ||
| 2793 | Lisp_Object buf = Fcurrent_buffer (); | ||
| 2794 | Lisp_Object locks = assq_no_quit (buf, narrowing_locks); | ||
| 2795 | if (NILP (locks)) | ||
| 2796 | return Qnil; | ||
| 2797 | locks = XCAR (XCDR (locks)); | ||
| 2798 | return Fcons (buf, Fcopy_sequence (locks)); | ||
| 2799 | } | ||
| 2711 | 2800 | ||
| 2712 | record_unwind_protect (restore_point_unwind, Fpoint_marker ()); | 2801 | static void |
| 2713 | record_unwind_protect (unwind_locked_begv, Fpoint_min ()); | 2802 | narrowing_locks_restore (Lisp_Object buf_and_saved_locks) |
| 2714 | record_unwind_protect (unwind_locked_zv, Fpoint_max ()); | 2803 | { |
| 2804 | if (NILP (buf_and_saved_locks)) | ||
| 2805 | return; | ||
| 2806 | Lisp_Object buf = XCAR (buf_and_saved_locks); | ||
| 2807 | Lisp_Object saved_locks = XCDR (buf_and_saved_locks); | ||
| 2808 | narrowing_locks_remove (buf); | ||
| 2809 | narrowing_locks_add (buf, saved_locks); | ||
| 2810 | } | ||
| 2715 | 2811 | ||
| 2716 | SET_BUF_BEGV (current_buffer, s); | 2812 | static void |
| 2717 | SET_BUF_ZV (current_buffer, e); | 2813 | unwind_narrow_to_region_locked (Lisp_Object tag) |
| 2814 | { | ||
| 2815 | Fnarrowing_unlock (tag); | ||
| 2816 | Fwiden (); | ||
| 2817 | } | ||
| 2818 | |||
| 2819 | /* Narrow current_buffer to BEGV-ZV with a narrowing locked with TAG. */ | ||
| 2820 | void | ||
| 2821 | narrow_to_region_locked (Lisp_Object begv, Lisp_Object zv, Lisp_Object tag) | ||
| 2822 | { | ||
| 2823 | Fnarrow_to_region (begv, zv); | ||
| 2824 | Fnarrowing_lock (tag); | ||
| 2825 | record_unwind_protect (restore_point_unwind, Fpoint_marker ()); | ||
| 2826 | record_unwind_protect (unwind_narrow_to_region_locked, tag); | ||
| 2827 | } | ||
| 2828 | |||
| 2829 | DEFUN ("widen", Fwiden, Swiden, 0, 0, "", | ||
| 2830 | doc: /* Remove restrictions (narrowing) from current buffer. | ||
| 2831 | |||
| 2832 | This allows the buffer's full text to be seen and edited, unless | ||
| 2833 | restrictions have been locked with `narrowing-lock', which see, in | ||
| 2834 | which case the narrowing that was current when `narrowing-lock' was | ||
| 2835 | called is restored. */) | ||
| 2836 | (void) | ||
| 2837 | { | ||
| 2838 | Fset (Qoutermost_narrowing, Qnil); | ||
| 2839 | Lisp_Object buf = Fcurrent_buffer (); | ||
| 2840 | Lisp_Object tag = narrowing_lock_peek_tag (buf); | ||
| 2841 | |||
| 2842 | if (NILP (tag)) | ||
| 2843 | { | ||
| 2844 | if (BEG != BEGV || Z != ZV) | ||
| 2845 | current_buffer->clip_changed = 1; | ||
| 2846 | BEGV = BEG; | ||
| 2847 | BEGV_BYTE = BEG_BYTE; | ||
| 2848 | SET_BUF_ZV_BOTH (current_buffer, Z, Z_BYTE); | ||
| 2718 | } | 2849 | } |
| 2719 | else | 2850 | else |
| 2720 | { | 2851 | { |
| 2721 | if (! NILP (Vrestrictions_locked)) | 2852 | struct Lisp_Marker *begv = narrowing_lock_get_bound (buf, true, false); |
| 2722 | return Qnil; | 2853 | struct Lisp_Marker *zv = narrowing_lock_get_bound (buf, false, false); |
| 2723 | 2854 | eassert (begv != NULL && zv != NULL); | |
| 2724 | if (!(BEG <= s && s <= e && e <= Z)) | 2855 | if (begv->charpos != BEGV || zv->charpos != ZV) |
| 2725 | args_out_of_range (start, end); | ||
| 2726 | |||
| 2727 | if (BEGV != s || ZV != e) | ||
| 2728 | current_buffer->clip_changed = 1; | 2856 | current_buffer->clip_changed = 1; |
| 2729 | 2857 | SET_BUF_BEGV_BOTH (current_buffer, begv->charpos, begv->bytepos); | |
| 2730 | SET_BUF_BEGV (current_buffer, s); | 2858 | SET_BUF_ZV_BOTH (current_buffer, zv->charpos, zv->bytepos); |
| 2731 | SET_BUF_ZV (current_buffer, e); | 2859 | /* If the only remaining bounds in narrowing_locks for |
| 2860 | current_buffer are the bounds that were set by the user, no | ||
| 2861 | locked narrowing is in effect in current_buffer anymore: | ||
| 2862 | remove it from the narrowing_locks alist. */ | ||
| 2863 | if (EQ (tag, Qoutermost_narrowing)) | ||
| 2864 | narrowing_lock_pop (buf); | ||
| 2732 | } | 2865 | } |
| 2733 | |||
| 2734 | if (PT < s) | ||
| 2735 | SET_PT (s); | ||
| 2736 | if (e < PT) | ||
| 2737 | SET_PT (e); | ||
| 2738 | /* Changing the buffer bounds invalidates any recorded current column. */ | 2866 | /* Changing the buffer bounds invalidates any recorded current column. */ |
| 2739 | invalidate_current_column (); | 2867 | invalidate_current_column (); |
| 2740 | return Qnil; | 2868 | return Qnil; |
| @@ -2751,14 +2879,110 @@ When calling from Lisp, pass two arguments START and END: | |||
| 2751 | positions (integers or markers) bounding the text that should | 2879 | positions (integers or markers) bounding the text that should |
| 2752 | remain visible. | 2880 | remain visible. |
| 2753 | 2881 | ||
| 2754 | Note that, when the current buffer contains one or more lines whose | 2882 | When restrictions have been locked with `narrowing-lock', which see, |
| 2755 | length is above `long-line-threshold', Emacs may decide to leave, for | 2883 | `narrow-to-region' can be used only within the limits of the |
| 2756 | performance reasons, the accessible portion of the buffer unchanged | 2884 | restrictions that were current when `narrowing-lock' was called. If |
| 2757 | after this function is called from low-level hooks, such as | 2885 | the START or END arguments are outside these limits, the corresponding |
| 2758 | `jit-lock-functions' or `post-command-hook'. */) | 2886 | limit of the locked restriction is used instead of the argument. */) |
| 2759 | (Lisp_Object start, Lisp_Object end) | 2887 | (Lisp_Object start, Lisp_Object end) |
| 2760 | { | 2888 | { |
| 2761 | return narrow_to_region_internal (start, end, false); | 2889 | EMACS_INT s = fix_position (start), e = fix_position (end); |
| 2890 | |||
| 2891 | if (e < s) | ||
| 2892 | { | ||
| 2893 | EMACS_INT tem = s; s = e; e = tem; | ||
| 2894 | } | ||
| 2895 | |||
| 2896 | if (!(BEG <= s && s <= e && e <= Z)) | ||
| 2897 | args_out_of_range (start, end); | ||
| 2898 | |||
| 2899 | Lisp_Object buf = Fcurrent_buffer (); | ||
| 2900 | if (! NILP (narrowing_lock_peek_tag (buf))) | ||
| 2901 | { | ||
| 2902 | struct Lisp_Marker *begv = narrowing_lock_get_bound (buf, true, false); | ||
| 2903 | struct Lisp_Marker *zv = narrowing_lock_get_bound (buf, false, false); | ||
| 2904 | eassert (begv != NULL && zv != NULL); | ||
| 2905 | /* Limit the start and end positions to those of the locked | ||
| 2906 | narrowing. */ | ||
| 2907 | if (s < begv->charpos) s = begv->charpos; | ||
| 2908 | if (s > zv->charpos) s = zv->charpos; | ||
| 2909 | if (e < begv->charpos) e = begv->charpos; | ||
| 2910 | if (e > zv->charpos) e = zv->charpos; | ||
| 2911 | } | ||
| 2912 | |||
| 2913 | /* Record the accessible range of the buffer when narrow-to-region | ||
| 2914 | is called, that is, before applying the narrowing. It is used | ||
| 2915 | only by narrowing-lock. */ | ||
| 2916 | Fset (Qoutermost_narrowing, list3 (Qoutermost_narrowing, | ||
| 2917 | Fpoint_min_marker (), | ||
| 2918 | Fpoint_max_marker ())); | ||
| 2919 | |||
| 2920 | if (BEGV != s || ZV != e) | ||
| 2921 | current_buffer->clip_changed = 1; | ||
| 2922 | |||
| 2923 | SET_BUF_BEGV (current_buffer, s); | ||
| 2924 | SET_BUF_ZV (current_buffer, e); | ||
| 2925 | |||
| 2926 | if (PT < s) | ||
| 2927 | SET_PT (s); | ||
| 2928 | if (e < PT) | ||
| 2929 | SET_PT (e); | ||
| 2930 | /* Changing the buffer bounds invalidates any recorded current column. */ | ||
| 2931 | invalidate_current_column (); | ||
| 2932 | return Qnil; | ||
| 2933 | } | ||
| 2934 | |||
| 2935 | DEFUN ("narrowing-lock", Fnarrowing_lock, Snarrowing_lock, 1, 1, 0, | ||
| 2936 | doc: /* Lock the current narrowing with TAG. | ||
| 2937 | |||
| 2938 | When restrictions are locked, `narrow-to-region' and `widen' can be | ||
| 2939 | used only within the limits of the restrictions that were current when | ||
| 2940 | `narrowing-lock' was called, unless the lock is removed by calling | ||
| 2941 | `narrowing-unlock' with TAG. | ||
| 2942 | |||
| 2943 | Locking restrictions should be used sparingly, after carefully | ||
| 2944 | considering the potential adverse effects on the code that will be | ||
| 2945 | executed within locked restrictions. It is typically meant to be used | ||
| 2946 | around portions of code that would become too slow, and make Emacs | ||
| 2947 | unresponsive, if they were executed in a large buffer. For example, | ||
| 2948 | restrictions are locked by Emacs around low-level hooks such as | ||
| 2949 | `fontification-functions' or `post-command-hook'. | ||
| 2950 | |||
| 2951 | Locked restrictions are never visible on display, and can therefore | ||
| 2952 | not be used as a stronger variant of normal restrictions. */) | ||
| 2953 | (Lisp_Object tag) | ||
| 2954 | { | ||
| 2955 | Lisp_Object buf = Fcurrent_buffer (); | ||
| 2956 | Lisp_Object outermost_narrowing | ||
| 2957 | = buffer_local_value (Qoutermost_narrowing, buf); | ||
| 2958 | /* If narrowing-lock is called without being preceded by | ||
| 2959 | narrow-to-region, do nothing. */ | ||
| 2960 | if (NILP (outermost_narrowing)) | ||
| 2961 | return Qnil; | ||
| 2962 | if (NILP (narrowing_lock_peek_tag (buf))) | ||
| 2963 | narrowing_lock_push (buf, outermost_narrowing); | ||
| 2964 | narrowing_lock_push (buf, list3 (tag, | ||
| 2965 | Fpoint_min_marker (), | ||
| 2966 | Fpoint_max_marker ())); | ||
| 2967 | return Qnil; | ||
| 2968 | } | ||
| 2969 | |||
| 2970 | DEFUN ("narrowing-unlock", Fnarrowing_unlock, Snarrowing_unlock, 1, 1, 0, | ||
| 2971 | doc: /* Unlock a narrowing locked with (narrowing-lock TAG). | ||
| 2972 | |||
| 2973 | Unlocking restrictions locked with `narrowing-lock' should be used | ||
| 2974 | sparingly, after carefully considering the reasons why restrictions | ||
| 2975 | were locked. Restrictions are typically locked around portions of | ||
| 2976 | code that would become too slow, and make Emacs unresponsive, if they | ||
| 2977 | were executed in a large buffer. For example, restrictions are locked | ||
| 2978 | by Emacs around low-level hooks such as `fontification-functions' or | ||
| 2979 | `post-command-hook'. */) | ||
| 2980 | (Lisp_Object tag) | ||
| 2981 | { | ||
| 2982 | Lisp_Object buf = Fcurrent_buffer (); | ||
| 2983 | if (EQ (narrowing_lock_peek_tag (buf), tag)) | ||
| 2984 | narrowing_lock_pop (buf); | ||
| 2985 | return Qnil; | ||
| 2762 | } | 2986 | } |
| 2763 | 2987 | ||
| 2764 | Lisp_Object | 2988 | Lisp_Object |
| @@ -2858,11 +3082,12 @@ DEFUN ("save-restriction", Fsave_restriction, Ssave_restriction, 0, UNEVALLED, 0 | |||
| 2858 | doc: /* Execute BODY, saving and restoring current buffer's restrictions. | 3082 | doc: /* Execute BODY, saving and restoring current buffer's restrictions. |
| 2859 | The buffer's restrictions make parts of the beginning and end invisible. | 3083 | The buffer's restrictions make parts of the beginning and end invisible. |
| 2860 | \(They are set up with `narrow-to-region' and eliminated with `widen'.) | 3084 | \(They are set up with `narrow-to-region' and eliminated with `widen'.) |
| 2861 | This special form, `save-restriction', saves the current buffer's restrictions | 3085 | This special form, `save-restriction', saves the current buffer's |
| 2862 | when it is entered, and restores them when it is exited. | 3086 | restrictions, as well as their locks if they have been locked with |
| 3087 | `narrowing-lock', when it is entered, and restores them when it is exited. | ||
| 2863 | So any `narrow-to-region' within BODY lasts only until the end of the form. | 3088 | So any `narrow-to-region' within BODY lasts only until the end of the form. |
| 2864 | The old restrictions settings are restored | 3089 | The old restrictions settings are restored even in case of abnormal exit |
| 2865 | even in case of abnormal exit (throw or error). | 3090 | \(throw or error). |
| 2866 | 3091 | ||
| 2867 | The value returned is the value of the last form in BODY. | 3092 | The value returned is the value of the last form in BODY. |
| 2868 | 3093 | ||
| @@ -2877,6 +3102,7 @@ usage: (save-restriction &rest BODY) */) | |||
| 2877 | specpdl_ref count = SPECPDL_INDEX (); | 3102 | specpdl_ref count = SPECPDL_INDEX (); |
| 2878 | 3103 | ||
| 2879 | record_unwind_protect (save_restriction_restore, save_restriction_save ()); | 3104 | record_unwind_protect (save_restriction_restore, save_restriction_save ()); |
| 3105 | record_unwind_protect (narrowing_locks_restore, narrowing_locks_save ()); | ||
| 2880 | val = Fprogn (body); | 3106 | val = Fprogn (body); |
| 2881 | return unbind_to (count, val); | 3107 | return unbind_to (count, val); |
| 2882 | } | 3108 | } |
| @@ -3053,18 +3279,18 @@ The other arguments are substituted into it to make the result, a string. | |||
| 3053 | The format control string may contain %-sequences meaning to substitute | 3279 | The format control string may contain %-sequences meaning to substitute |
| 3054 | the next available argument, or the argument explicitly specified: | 3280 | the next available argument, or the argument explicitly specified: |
| 3055 | 3281 | ||
| 3056 | %s means print a string argument. Actually, prints any object, with `princ'. | 3282 | %s means produce a string argument. Actually, produces any object with `princ'. |
| 3057 | %d means print as signed number in decimal. | 3283 | %d means produce as signed number in decimal. |
| 3058 | %o means print a number in octal. | 3284 | %o means produce a number in octal. |
| 3059 | %x means print a number in hex. | 3285 | %x means produce a number in hex. |
| 3060 | %X is like %x, but uses upper case. | 3286 | %X is like %x, but uses upper case. |
| 3061 | %e means print a number in exponential notation. | 3287 | %e means produce a number in exponential notation. |
| 3062 | %f means print a number in decimal-point notation. | 3288 | %f means produce a number in decimal-point notation. |
| 3063 | %g means print a number in exponential notation if the exponent would be | 3289 | %g means produce a number in exponential notation if the exponent would be |
| 3064 | less than -4 or greater than or equal to the precision (default: 6); | 3290 | less than -4 or greater than or equal to the precision (default: 6); |
| 3065 | otherwise it prints in decimal-point notation. | 3291 | otherwise it produces in decimal-point notation. |
| 3066 | %c means print a number as a single character. | 3292 | %c means produce a number as a single character. |
| 3067 | %S means print any object as an s-expression (using `prin1'). | 3293 | %S means produce any object as an s-expression (using `prin1'). |
| 3068 | 3294 | ||
| 3069 | The argument used for %d, %o, %x, %e, %f, %g or %c must be a number. | 3295 | The argument used for %d, %o, %x, %e, %f, %g or %c must be a number. |
| 3070 | %o, %x, and %X treat arguments as unsigned if `binary-as-unsigned' is t | 3296 | %o, %x, and %X treat arguments as unsigned if `binary-as-unsigned' is t |
| @@ -3099,7 +3325,7 @@ included even if the precision is zero, and also forces trailing | |||
| 3099 | zeros after the decimal point to be left in place. | 3325 | zeros after the decimal point to be left in place. |
| 3100 | 3326 | ||
| 3101 | The width specifier supplies a lower limit for the length of the | 3327 | The width specifier supplies a lower limit for the length of the |
| 3102 | printed representation. The padding, if any, normally goes on the | 3328 | produced representation. The padding, if any, normally goes on the |
| 3103 | left, but it goes on the right if the - flag is present. The padding | 3329 | left, but it goes on the right if the - flag is present. The padding |
| 3104 | character is normally a space, but it is 0 if the 0 flag is present. | 3330 | character is normally a space, but it is 0 if the 0 flag is present. |
| 3105 | The 0 flag is ignored if the - flag is present, or the format sequence | 3331 | The 0 flag is ignored if the - flag is present, or the format sequence |
| @@ -3108,7 +3334,7 @@ is something other than %d, %o, %x, %e, %f, and %g. | |||
| 3108 | For %e and %f sequences, the number after the "." in the precision | 3334 | For %e and %f sequences, the number after the "." in the precision |
| 3109 | specifier says how many decimal places to show; if zero, the decimal | 3335 | specifier says how many decimal places to show; if zero, the decimal |
| 3110 | point itself is omitted. For %g, the precision specifies how many | 3336 | point itself is omitted. For %g, the precision specifies how many |
| 3111 | significant digits to print; zero or omitted are treated as 1. | 3337 | significant digits to produce; zero or omitted are treated as 1. |
| 3112 | For %s and %S, the precision specifier truncates the string to the | 3338 | For %s and %S, the precision specifier truncates the string to the |
| 3113 | given width. | 3339 | given width. |
| 3114 | 3340 | ||
| @@ -4518,6 +4744,8 @@ syms_of_editfns (void) | |||
| 4518 | DEFSYM (Qwall, "wall"); | 4744 | DEFSYM (Qwall, "wall"); |
| 4519 | DEFSYM (Qpropertize, "propertize"); | 4745 | DEFSYM (Qpropertize, "propertize"); |
| 4520 | 4746 | ||
| 4747 | staticpro (&narrowing_locks); | ||
| 4748 | |||
| 4521 | DEFVAR_LISP ("inhibit-field-text-motion", Vinhibit_field_text_motion, | 4749 | DEFVAR_LISP ("inhibit-field-text-motion", Vinhibit_field_text_motion, |
| 4522 | doc: /* Non-nil means text motion commands don't notice fields. */); | 4750 | doc: /* Non-nil means text motion commands don't notice fields. */); |
| 4523 | Vinhibit_field_text_motion = Qnil; | 4751 | Vinhibit_field_text_motion = Qnil; |
| @@ -4577,11 +4805,12 @@ This variable is experimental; email 32252@debbugs.gnu.org if you need | |||
| 4577 | it to be non-nil. */); | 4805 | it to be non-nil. */); |
| 4578 | binary_as_unsigned = false; | 4806 | binary_as_unsigned = false; |
| 4579 | 4807 | ||
| 4580 | DEFSYM (Qrestrictions_locked, "restrictions-locked"); | 4808 | DEFVAR_LISP ("outermost-narrowing", Voutermost_narrowing, |
| 4581 | DEFVAR_LISP ("restrictions-locked", Vrestrictions_locked, | 4809 | doc: /* Outermost narrowing bounds, if any. Internal use only. */); |
| 4582 | doc: /* If non-nil, restrictions are currently locked. */); | 4810 | Voutermost_narrowing = Qnil; |
| 4583 | Vrestrictions_locked = Qnil; | 4811 | Fmake_variable_buffer_local (Qoutermost_narrowing); |
| 4584 | Funintern (Qrestrictions_locked, Qnil); | 4812 | DEFSYM (Qoutermost_narrowing, "outermost-narrowing"); |
| 4813 | Funintern (Qoutermost_narrowing, Qnil); | ||
| 4585 | 4814 | ||
| 4586 | defsubr (&Spropertize); | 4815 | defsubr (&Spropertize); |
| 4587 | defsubr (&Schar_equal); | 4816 | defsubr (&Schar_equal); |
| @@ -4674,6 +4903,8 @@ it to be non-nil. */); | |||
| 4674 | defsubr (&Sdelete_and_extract_region); | 4903 | defsubr (&Sdelete_and_extract_region); |
| 4675 | defsubr (&Swiden); | 4904 | defsubr (&Swiden); |
| 4676 | defsubr (&Snarrow_to_region); | 4905 | defsubr (&Snarrow_to_region); |
| 4906 | defsubr (&Snarrowing_lock); | ||
| 4907 | defsubr (&Snarrowing_unlock); | ||
| 4677 | defsubr (&Ssave_restriction); | 4908 | defsubr (&Ssave_restriction); |
| 4678 | defsubr (&Stranspose_regions); | 4909 | defsubr (&Stranspose_regions); |
| 4679 | } | 4910 | } |
diff --git a/src/emacs-module.h.in b/src/emacs-module.h.in index bef89b059fc..d485de5aa18 100644 --- a/src/emacs-module.h.in +++ b/src/emacs-module.h.in | |||
| @@ -183,6 +183,21 @@ struct emacs_env_29 | |||
| 183 | @module_env_snippet_29@ | 183 | @module_env_snippet_29@ |
| 184 | }; | 184 | }; |
| 185 | 185 | ||
| 186 | struct emacs_env_30 | ||
| 187 | { | ||
| 188 | @module_env_snippet_25@ | ||
| 189 | |||
| 190 | @module_env_snippet_26@ | ||
| 191 | |||
| 192 | @module_env_snippet_27@ | ||
| 193 | |||
| 194 | @module_env_snippet_28@ | ||
| 195 | |||
| 196 | @module_env_snippet_29@ | ||
| 197 | |||
| 198 | @module_env_snippet_30@ | ||
| 199 | }; | ||
| 200 | |||
| 186 | /* Every module should define a function as follows. */ | 201 | /* Every module should define a function as follows. */ |
| 187 | extern int emacs_module_init (struct emacs_runtime *runtime) | 202 | extern int emacs_module_init (struct emacs_runtime *runtime) |
| 188 | EMACS_NOEXCEPT | 203 | EMACS_NOEXCEPT |
diff --git a/src/emacs.c b/src/emacs.c index 105539aa192..d8a2863fd9c 100644 --- a/src/emacs.c +++ b/src/emacs.c | |||
| @@ -295,6 +295,7 @@ Initialization options:\n\ | |||
| 295 | --no-site-lisp, -nsl do not add site-lisp directories to load-path\n\ | 295 | --no-site-lisp, -nsl do not add site-lisp directories to load-path\n\ |
| 296 | --no-splash do not display a splash screen on startup\n\ | 296 | --no-splash do not display a splash screen on startup\n\ |
| 297 | --no-window-system, -nw do not communicate with X, ignoring $DISPLAY\n\ | 297 | --no-window-system, -nw do not communicate with X, ignoring $DISPLAY\n\ |
| 298 | --init-directory=DIR use DIR when looking for the Emacs init files.\n\ | ||
| 298 | ", | 299 | ", |
| 299 | "\ | 300 | "\ |
| 300 | --quick, -Q equivalent to:\n\ | 301 | --quick, -Q equivalent to:\n\ |
| @@ -1923,6 +1924,12 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem | |||
| 1923 | Vcoding_system_hash_table. */ | 1924 | Vcoding_system_hash_table. */ |
| 1924 | syms_of_coding (); /* This should be after syms_of_fileio. */ | 1925 | syms_of_coding (); /* This should be after syms_of_fileio. */ |
| 1925 | init_frame_once (); /* Before init_window_once. */ | 1926 | init_frame_once (); /* Before init_window_once. */ |
| 1927 | /* init_window_once calls make_initial_frame, which calls | ||
| 1928 | Fcurrent_time and bset_display_time, both of which allocate | ||
| 1929 | bignums. Without the following call to init_bignums, crashes | ||
| 1930 | happen on Windows 9X after dumping when GC tries to free a | ||
| 1931 | pointer allocated on the system heap. */ | ||
| 1932 | init_bignum (); | ||
| 1926 | init_window_once (); /* Init the window system. */ | 1933 | init_window_once (); /* Init the window system. */ |
| 1927 | #ifdef HAVE_WINDOW_SYSTEM | 1934 | #ifdef HAVE_WINDOW_SYSTEM |
| 1928 | init_fringe_once (); /* Swap bitmaps if necessary. */ | 1935 | init_fringe_once (); /* Swap bitmaps if necessary. */ |
diff --git a/src/fileio.c b/src/fileio.c index 92335b639cd..e7c2af81421 100644 --- a/src/fileio.c +++ b/src/fileio.c | |||
| @@ -134,6 +134,7 @@ static dev_t timestamp_file_system; | |||
| 134 | is added here. */ | 134 | is added here. */ |
| 135 | static Lisp_Object Vwrite_region_annotation_buffers; | 135 | static Lisp_Object Vwrite_region_annotation_buffers; |
| 136 | 136 | ||
| 137 | static Lisp_Object emacs_readlinkat (int, char const *); | ||
| 137 | static Lisp_Object file_name_directory (Lisp_Object); | 138 | static Lisp_Object file_name_directory (Lisp_Object); |
| 138 | static bool a_write (int, Lisp_Object, ptrdiff_t, ptrdiff_t, | 139 | static bool a_write (int, Lisp_Object, ptrdiff_t, ptrdiff_t, |
| 139 | Lisp_Object *, struct coding_system *); | 140 | Lisp_Object *, struct coding_system *); |
| @@ -2219,7 +2220,7 @@ permissions. */) | |||
| 2219 | report_file_error ("Copying permissions to", newname); | 2220 | report_file_error ("Copying permissions to", newname); |
| 2220 | } | 2221 | } |
| 2221 | #else /* not WINDOWSNT */ | 2222 | #else /* not WINDOWSNT */ |
| 2222 | ifd = emacs_open (SSDATA (encoded_file), O_RDONLY, 0); | 2223 | ifd = emacs_open (SSDATA (encoded_file), O_RDONLY | O_NONBLOCK, 0); |
| 2223 | 2224 | ||
| 2224 | if (ifd < 0) | 2225 | if (ifd < 0) |
| 2225 | report_file_error ("Opening input file", file); | 2226 | report_file_error ("Opening input file", file); |
| @@ -2427,16 +2428,11 @@ DEFUN ("make-directory-internal", Fmake_directory_internal, | |||
| 2427 | (Lisp_Object directory) | 2428 | (Lisp_Object directory) |
| 2428 | { | 2429 | { |
| 2429 | const char *dir; | 2430 | const char *dir; |
| 2430 | Lisp_Object handler; | ||
| 2431 | Lisp_Object encoded_dir; | 2431 | Lisp_Object encoded_dir; |
| 2432 | 2432 | ||
| 2433 | CHECK_STRING (directory); | 2433 | CHECK_STRING (directory); |
| 2434 | directory = Fexpand_file_name (directory, Qnil); | 2434 | directory = Fexpand_file_name (directory, Qnil); |
| 2435 | 2435 | ||
| 2436 | handler = Ffind_file_name_handler (directory, Qmake_directory_internal); | ||
| 2437 | if (!NILP (handler)) | ||
| 2438 | return call2 (handler, Qmake_directory_internal, directory); | ||
| 2439 | |||
| 2440 | encoded_dir = ENCODE_FILE (directory); | 2436 | encoded_dir = ENCODE_FILE (directory); |
| 2441 | 2437 | ||
| 2442 | dir = SSDATA (encoded_dir); | 2438 | dir = SSDATA (encoded_dir); |
| @@ -2710,31 +2706,19 @@ This is what happens in interactive use with M-x. */) | |||
| 2710 | } | 2706 | } |
| 2711 | if (dirp) | 2707 | if (dirp) |
| 2712 | call4 (Qcopy_directory, file, newname, Qt, Qnil); | 2708 | call4 (Qcopy_directory, file, newname, Qt, Qnil); |
| 2713 | else | 2709 | else if (S_ISREG (file_st.st_mode)) |
| 2714 | { | 2710 | Fcopy_file (file, newname, ok_if_already_exists, Qt, Qt, Qt); |
| 2715 | Lisp_Object symlink_target | 2711 | else if (S_ISLNK (file_st.st_mode)) |
| 2716 | = (S_ISLNK (file_st.st_mode) | 2712 | { |
| 2717 | ? check_emacs_readlinkat (AT_FDCWD, file, SSDATA (encoded_file)) | 2713 | Lisp_Object target = emacs_readlinkat (AT_FDCWD, |
| 2718 | : Qnil); | 2714 | SSDATA (encoded_file)); |
| 2719 | if (!NILP (symlink_target)) | 2715 | if (!NILP (target)) |
| 2720 | Fmake_symbolic_link (symlink_target, newname, ok_if_already_exists); | 2716 | Fmake_symbolic_link (target, newname, ok_if_already_exists); |
| 2721 | else if (S_ISFIFO (file_st.st_mode)) | ||
| 2722 | { | ||
| 2723 | /* If it's a FIFO, calling `copy-file' will hang if it's a | ||
| 2724 | inter-file system move, so do it here. (It will signal | ||
| 2725 | an error in that case, but it won't hang in any case.) */ | ||
| 2726 | if (!NILP (ok_if_already_exists)) | ||
| 2727 | barf_or_query_if_file_exists (newname, false, | ||
| 2728 | "rename to it", | ||
| 2729 | FIXNUMP (ok_if_already_exists), | ||
| 2730 | false); | ||
| 2731 | if (rename (SSDATA (encoded_file), SSDATA (encoded_newname)) != 0) | ||
| 2732 | report_file_errno ("Renaming", list2 (file, newname), errno); | ||
| 2733 | return Qnil; | ||
| 2734 | } | ||
| 2735 | else | 2717 | else |
| 2736 | Fcopy_file (file, newname, ok_if_already_exists, Qt, Qt, Qt); | 2718 | report_file_error ("Renaming", list2 (file, newname)); |
| 2737 | } | 2719 | } |
| 2720 | else | ||
| 2721 | report_file_errno ("Renaming", list2 (file, newname), rename_errno); | ||
| 2738 | 2722 | ||
| 2739 | specpdl_ref count = SPECPDL_INDEX (); | 2723 | specpdl_ref count = SPECPDL_INDEX (); |
| 2740 | specbind (Qdelete_by_moving_to_trash, Qnil); | 2724 | specbind (Qdelete_by_moving_to_trash, Qnil); |
| @@ -334,7 +334,9 @@ Letter-case is significant, but text properties are ignored. */) | |||
| 334 | DEFUN ("string-equal", Fstring_equal, Sstring_equal, 2, 2, 0, | 334 | DEFUN ("string-equal", Fstring_equal, Sstring_equal, 2, 2, 0, |
| 335 | doc: /* Return t if two strings have identical contents. | 335 | doc: /* Return t if two strings have identical contents. |
| 336 | Case is significant, but text properties are ignored. | 336 | Case is significant, but text properties are ignored. |
| 337 | Symbols are also allowed; their print names are used instead. */) | 337 | Symbols are also allowed; their print names are used instead. |
| 338 | |||
| 339 | See also `string-equal-ignore-case'. */) | ||
| 338 | (register Lisp_Object s1, Lisp_Object s2) | 340 | (register Lisp_Object s1, Lisp_Object s2) |
| 339 | { | 341 | { |
| 340 | if (SYMBOLP (s1)) | 342 | if (SYMBOLP (s1)) |
| @@ -495,8 +497,13 @@ Symbols are also allowed; their print names are used instead. */) | |||
| 495 | int ws = sizeof (word_t); | 497 | int ws = sizeof (word_t); |
| 496 | const word_t *w1 = (const word_t *) SDATA (string1); | 498 | const word_t *w1 = (const word_t *) SDATA (string1); |
| 497 | const word_t *w2 = (const word_t *) SDATA (string2); | 499 | const word_t *w2 = (const word_t *) SDATA (string2); |
| 498 | while (b < nb - ws + 1 && w1[b / ws] == w2[b / ws]) | 500 | while (b < nb - ws + 1) |
| 499 | b += ws; | 501 | { |
| 502 | if (UNALIGNED_LOAD_SIZE (w1, b / ws) | ||
| 503 | != UNALIGNED_LOAD_SIZE (w2, b / ws)) | ||
| 504 | break; | ||
| 505 | b += ws; | ||
| 506 | } | ||
| 500 | } | 507 | } |
| 501 | 508 | ||
| 502 | /* Scan forward to the differing byte. */ | 509 | /* Scan forward to the differing byte. */ |
| @@ -3936,7 +3943,7 @@ system. | |||
| 3936 | If the region can't be decoded, signal an error and don't modify the buffer. | 3943 | If the region can't be decoded, signal an error and don't modify the buffer. |
| 3937 | Optional third argument BASE64URL determines whether to use the URL variant | 3944 | Optional third argument BASE64URL determines whether to use the URL variant |
| 3938 | of the base 64 encoding, as defined in RFC 4648. | 3945 | of the base 64 encoding, as defined in RFC 4648. |
| 3939 | If optional fourth argument INGORE-INVALID is non-nil invalid characters | 3946 | If optional fourth argument IGNORE-INVALID is non-nil invalid characters |
| 3940 | are ignored instead of signaling an error. */) | 3947 | are ignored instead of signaling an error. */) |
| 3941 | (Lisp_Object beg, Lisp_Object end, Lisp_Object base64url, | 3948 | (Lisp_Object beg, Lisp_Object end, Lisp_Object base64url, |
| 3942 | Lisp_Object ignore_invalid) | 3949 | Lisp_Object ignore_invalid) |
diff --git a/src/frame.c b/src/frame.c index b57b296be54..7d902dabd4f 100644 --- a/src/frame.c +++ b/src/frame.c | |||
| @@ -1892,12 +1892,61 @@ other_frames (struct frame *f, bool invisible, bool force) | |||
| 1892 | 1892 | ||
| 1893 | if (f != f1) | 1893 | if (f != f1) |
| 1894 | { | 1894 | { |
| 1895 | /* The following code is defined out because it is | ||
| 1896 | responsible for a performance drop under X connections | ||
| 1897 | over a network, and its purpose is unclear. XSync does | ||
| 1898 | not handle events (or call any callbacks defined by | ||
| 1899 | Emacs), and as such it should not note any "recent change | ||
| 1900 | in visibility". | ||
| 1901 | |||
| 1902 | When writing new code, please try as hard as possible to | ||
| 1903 | avoid calls that require a roundtrip to the X server. | ||
| 1904 | When such calls are inevitable, use the XCB library to | ||
| 1905 | handle multiple consecutive requests with a data reply in | ||
| 1906 | a more asynchronous fashion. The following code | ||
| 1907 | demonstrates why: | ||
| 1908 | |||
| 1909 | rc = XGetWindowProperty (dpyinfo->display, window, ... | ||
| 1910 | status = XGrabKeyboard (dpyinfo->display, ... | ||
| 1911 | |||
| 1912 | here, `XGetWindowProperty' will wait for a reply from the | ||
| 1913 | X server before returning, and thus allowing Emacs to | ||
| 1914 | make the XGrabKeyboard request, which in itself also | ||
| 1915 | requires waiting a reply. When XCB is available, this | ||
| 1916 | code could be written: | ||
| 1917 | |||
| 1918 | #ifdef HAVE_XCB | ||
| 1919 | xcb_get_property_cookie_t cookie1; | ||
| 1920 | xcb_get_property_reply_t *reply1; | ||
| 1921 | xcb_grab_keyboard_cookie_t cookie2; | ||
| 1922 | xcb_grab_keyboard_reply_t *reply2; | ||
| 1923 | |||
| 1924 | cookie1 = xcb_get_property (dpyinfo->xcb_connection, window, ... | ||
| 1925 | cookie2 = xcb_grab_keyboard (dpyinfo->xcb_connection, ... | ||
| 1926 | reply1 = xcb_get_property_reply (dpyinfo->xcb_connection, | ||
| 1927 | cookie1); | ||
| 1928 | reply2 = xcb_grab_keyboard_reply (dpyinfo->xcb_connection, | ||
| 1929 | cookie2); | ||
| 1930 | #endif | ||
| 1931 | |||
| 1932 | In this code, the GetProperty and GrabKeyboard requests | ||
| 1933 | are made simultaneously, and replies are then obtained | ||
| 1934 | from the server at once, avoiding the extraneous | ||
| 1935 | roundtrip to the X server after the call to | ||
| 1936 | `XGetWindowProperty'. | ||
| 1937 | |||
| 1938 | However, please keep an alternative implementation | ||
| 1939 | available for use when Emacs is built without XCB. */ | ||
| 1940 | |||
| 1941 | #if 0 | ||
| 1895 | /* Verify that we can still talk to the frame's X window, and | 1942 | /* Verify that we can still talk to the frame's X window, and |
| 1896 | note any recent change in visibility. */ | 1943 | note any recent change in visibility. */ |
| 1897 | #ifdef HAVE_X_WINDOWS | 1944 | #ifdef HAVE_X_WINDOWS |
| 1898 | if (FRAME_WINDOW_P (f1)) | 1945 | if (FRAME_WINDOW_P (f1)) |
| 1899 | x_sync (f1); | 1946 | x_sync (f1); |
| 1900 | #endif | 1947 | #endif |
| 1948 | #endif | ||
| 1949 | |||
| 1901 | if (!FRAME_TOOLTIP_P (f1) | 1950 | if (!FRAME_TOOLTIP_P (f1) |
| 1902 | /* Tooltips and child frames count neither for | 1951 | /* Tooltips and child frames count neither for |
| 1903 | invisibility nor for deletions. */ | 1952 | invisibility nor for deletions. */ |
| @@ -2214,17 +2263,24 @@ delete_frame (Lisp_Object frame, Lisp_Object force) | |||
| 2214 | /* Since a similar behavior was observed on the Lucid and Motif | 2263 | /* Since a similar behavior was observed on the Lucid and Motif |
| 2215 | builds (see Bug#5802, Bug#21509, Bug#23499, Bug#27816), we now | 2264 | builds (see Bug#5802, Bug#21509, Bug#23499, Bug#27816), we now |
| 2216 | don't delete the terminal for these builds either. */ | 2265 | don't delete the terminal for these builds either. */ |
| 2217 | if (terminal->reference_count == 0 && | 2266 | if (terminal->reference_count == 0 |
| 2218 | (terminal->type == output_x_window || terminal->type == output_pgtk)) | 2267 | && (terminal->type == output_x_window |
| 2268 | || terminal->type == output_pgtk)) | ||
| 2219 | terminal->reference_count = 1; | 2269 | terminal->reference_count = 1; |
| 2220 | #endif /* USE_X_TOOLKIT || USE_GTK */ | 2270 | #endif /* USE_X_TOOLKIT || USE_GTK */ |
| 2271 | |||
| 2221 | if (terminal->reference_count == 0) | 2272 | if (terminal->reference_count == 0) |
| 2222 | { | 2273 | { |
| 2223 | Lisp_Object tmp; | 2274 | Lisp_Object tmp; |
| 2224 | XSETTERMINAL (tmp, terminal); | 2275 | XSETTERMINAL (tmp, terminal); |
| 2225 | 2276 | ||
| 2226 | kb = NULL; | 2277 | kb = NULL; |
| 2227 | Fdelete_terminal (tmp, NILP (force) ? Qt : force); | 2278 | |
| 2279 | /* If force is noelisp, the terminal is going away inside | ||
| 2280 | x_delete_terminal, and a recursive call to Fdelete_terminal | ||
| 2281 | is unsafe! */ | ||
| 2282 | if (!EQ (force, Qnoelisp)) | ||
| 2283 | Fdelete_terminal (tmp, NILP (force) ? Qt : force); | ||
| 2228 | } | 2284 | } |
| 2229 | else | 2285 | else |
| 2230 | kb = terminal->kboard; | 2286 | kb = terminal->kboard; |
diff --git a/src/frame.h b/src/frame.h index d6fd62b2ac2..dcd32036b86 100644 --- a/src/frame.h +++ b/src/frame.h | |||
| @@ -1718,7 +1718,6 @@ extern void x_wm_set_icon_position (struct frame *, int, int); | |||
| 1718 | #if !defined USE_X_TOOLKIT | 1718 | #if !defined USE_X_TOOLKIT |
| 1719 | extern const char *x_get_resource_string (const char *, const char *); | 1719 | extern const char *x_get_resource_string (const char *, const char *); |
| 1720 | #endif | 1720 | #endif |
| 1721 | extern void x_sync (struct frame *); | ||
| 1722 | #endif /* HAVE_X_WINDOWS */ | 1721 | #endif /* HAVE_X_WINDOWS */ |
| 1723 | 1722 | ||
| 1724 | #if !defined (HAVE_NS) && !defined (HAVE_PGTK) | 1723 | #if !defined (HAVE_NS) && !defined (HAVE_PGTK) |
diff --git a/src/gnutls.c b/src/gnutls.c index 7f0aaf85a41..4093865cae5 100644 --- a/src/gnutls.c +++ b/src/gnutls.c | |||
| @@ -2282,7 +2282,7 @@ gnutls_symmetric_aead (bool encrypting, gnutls_cipher_algorithm_t gca, | |||
| 2282 | Lisp_Object output; | 2282 | Lisp_Object output; |
| 2283 | if (GNUTLS_E_SUCCESS <= ret) | 2283 | if (GNUTLS_E_SUCCESS <= ret) |
| 2284 | output = make_unibyte_string (storage, storage_length); | 2284 | output = make_unibyte_string (storage, storage_length); |
| 2285 | explicit_bzero (storage, storage_length); | 2285 | memset_explicit (storage, 0, storage_length); |
| 2286 | gnutls_aead_cipher_deinit (acipher); | 2286 | gnutls_aead_cipher_deinit (acipher); |
| 2287 | 2287 | ||
| 2288 | if (ret < GNUTLS_E_SUCCESS) | 2288 | if (ret < GNUTLS_E_SUCCESS) |
diff --git a/src/gtkutil.c b/src/gtkutil.c index a6bba096a43..592bb497749 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c | |||
| @@ -2103,7 +2103,7 @@ xg_frame_restack (struct frame *f1, struct frame *f2, bool above_flag) | |||
| 2103 | 2103 | ||
| 2104 | gdk_window_restack (gwin1, gwin2, above_flag); | 2104 | gdk_window_restack (gwin1, gwin2, above_flag); |
| 2105 | #ifndef HAVE_PGTK | 2105 | #ifndef HAVE_PGTK |
| 2106 | x_sync (f1); | 2106 | XSync (FRAME_X_DISPLAY (f1), False); |
| 2107 | #else | 2107 | #else |
| 2108 | gdk_flush (); | 2108 | gdk_flush (); |
| 2109 | #endif | 2109 | #endif |
| @@ -4793,7 +4793,7 @@ xg_update_scrollbar_pos (struct frame *f, | |||
| 4793 | here to get some events. */ | 4793 | here to get some events. */ |
| 4794 | 4794 | ||
| 4795 | #ifndef HAVE_PGTK | 4795 | #ifndef HAVE_PGTK |
| 4796 | x_sync (f); | 4796 | XSync (FRAME_X_DISPLAY (f), False); |
| 4797 | #else | 4797 | #else |
| 4798 | gdk_flush (); | 4798 | gdk_flush (); |
| 4799 | #endif | 4799 | #endif |
| @@ -4894,7 +4894,7 @@ xg_update_horizontal_scrollbar_pos (struct frame *f, | |||
| 4894 | } | 4894 | } |
| 4895 | 4895 | ||
| 4896 | #ifndef HAVE_PGTK | 4896 | #ifndef HAVE_PGTK |
| 4897 | x_sync (f); | 4897 | XSync (FRAME_X_DISPLAY (f), False); |
| 4898 | #else | 4898 | #else |
| 4899 | gdk_flush (); | 4899 | gdk_flush (); |
| 4900 | #endif | 4900 | #endif |
diff --git a/src/image.c b/src/image.c index 600c32571e1..b881e43e951 100644 --- a/src/image.c +++ b/src/image.c | |||
| @@ -193,8 +193,8 @@ static void anim_prune_animation_cache (Lisp_Object); | |||
| 193 | #ifdef USE_CAIRO | 193 | #ifdef USE_CAIRO |
| 194 | 194 | ||
| 195 | static Emacs_Pix_Container | 195 | static Emacs_Pix_Container |
| 196 | image_create_pix_container (struct frame *f, unsigned int width, | 196 | image_create_pix_container (unsigned int width, unsigned int height, |
| 197 | unsigned int height, unsigned int depth) | 197 | unsigned int depth) |
| 198 | { | 198 | { |
| 199 | Emacs_Pix_Container pimg; | 199 | Emacs_Pix_Container pimg; |
| 200 | 200 | ||
| @@ -237,7 +237,7 @@ image_pix_container_create_from_bitmap_data (struct frame *f, | |||
| 237 | unsigned long fg, | 237 | unsigned long fg, |
| 238 | unsigned long bg) | 238 | unsigned long bg) |
| 239 | { | 239 | { |
| 240 | Emacs_Pix_Container pimg = image_create_pix_container (f, width, height, 0); | 240 | Emacs_Pix_Container pimg = image_create_pix_container (width, height, 0); |
| 241 | int bytes_per_line = (width + (CHAR_BIT - 1)) / CHAR_BIT; | 241 | int bytes_per_line = (width + (CHAR_BIT - 1)) / CHAR_BIT; |
| 242 | 242 | ||
| 243 | for (int y = 0; y < height; y++) | 243 | for (int y = 0; y < height; y++) |
| @@ -3342,7 +3342,7 @@ image_create_x_image_and_pixmap_1 (struct frame *f, int width, int height, int d | |||
| 3342 | eassert (input_blocked_p ()); | 3342 | eassert (input_blocked_p ()); |
| 3343 | 3343 | ||
| 3344 | /* Allocate a pixmap of the same size. */ | 3344 | /* Allocate a pixmap of the same size. */ |
| 3345 | *pixmap = image_create_pix_container (f, width, height, depth); | 3345 | *pixmap = image_create_pix_container (width, height, depth); |
| 3346 | if (*pixmap == NO_PIXMAP) | 3346 | if (*pixmap == NO_PIXMAP) |
| 3347 | { | 3347 | { |
| 3348 | *pimg = NULL; | 3348 | *pimg = NULL; |
| @@ -11309,6 +11309,15 @@ svg_load_image (struct frame *f, struct image *img, char *contents, | |||
| 11309 | img->face_font_size); | 11309 | img->face_font_size); |
| 11310 | viewbox_height = svg_css_length_to_pixels (iheight, dpi, | 11310 | viewbox_height = svg_css_length_to_pixels (iheight, dpi, |
| 11311 | img->face_font_size); | 11311 | img->face_font_size); |
| 11312 | |||
| 11313 | /* Here one dimension could be zero because in percent unit. | ||
| 11314 | So calculate this dimension with the other. */ | ||
| 11315 | if (! (0 < viewbox_width) && (iwidth.unit == RSVG_UNIT_PERCENT)) | ||
| 11316 | viewbox_width = (viewbox_height * viewbox.width / viewbox.height) | ||
| 11317 | * iwidth.length; | ||
| 11318 | else if (! (0 < viewbox_height) && (iheight.unit == RSVG_UNIT_PERCENT)) | ||
| 11319 | viewbox_height = (viewbox_width * viewbox.height / viewbox.width) | ||
| 11320 | * iheight.length; | ||
| 11312 | } | 11321 | } |
| 11313 | else if (has_width && has_viewbox) | 11322 | else if (has_width && has_viewbox) |
| 11314 | { | 11323 | { |
diff --git a/src/itree.c b/src/itree.c index 04fa9e827a2..688d5c82476 100644 --- a/src/itree.c +++ b/src/itree.c | |||
| @@ -15,7 +15,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| 15 | GNU General Public License for more details. | 15 | GNU General Public License for more details. |
| 16 | 16 | ||
| 17 | You should have received a copy of the GNU General Public License | 17 | You should have received a copy of the GNU General Public License |
| 18 | along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | 18 | along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ |
| 19 | 19 | ||
| 20 | #include <config.h> | 20 | #include <config.h> |
| 21 | #include <math.h> | 21 | #include <math.h> |
| @@ -1376,7 +1376,7 @@ itree_iterator_first_node (struct itree_tree *tree, | |||
| 1376 | return node; | 1376 | return node; |
| 1377 | } | 1377 | } |
| 1378 | 1378 | ||
| 1379 | /* Start a iterator enumerating all intervals in [BEGIN,END) in the | 1379 | /* Start an iterator enumerating all intervals in [BEGIN,END) in the |
| 1380 | given ORDER. */ | 1380 | given ORDER. */ |
| 1381 | 1381 | ||
| 1382 | struct itree_iterator * | 1382 | struct itree_iterator * |
diff --git a/src/itree.h b/src/itree.h index 291fa53fd30..f1e2bf3bfde 100644 --- a/src/itree.h +++ b/src/itree.h | |||
| @@ -15,7 +15,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| 15 | GNU General Public License for more details. | 15 | GNU General Public License for more details. |
| 16 | 16 | ||
| 17 | You should have received a copy of the GNU General Public License | 17 | You should have received a copy of the GNU General Public License |
| 18 | along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | 18 | along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ |
| 19 | 19 | ||
| 20 | #ifndef ITREE_H | 20 | #ifndef ITREE_H |
| 21 | #define ITREE_H | 21 | #define ITREE_H |
| @@ -25,6 +25,8 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 25 | 25 | ||
| 26 | #include "lisp.h" | 26 | #include "lisp.h" |
| 27 | 27 | ||
| 28 | INLINE_HEADER_BEGIN | ||
| 29 | |||
| 28 | /* The tree and node structs are mainly here, so they can be | 30 | /* The tree and node structs are mainly here, so they can be |
| 29 | allocated. | 31 | allocated. |
| 30 | 32 | ||
| @@ -114,6 +116,11 @@ extern void itree_node_set_region (struct itree_tree *, struct itree_node *, | |||
| 114 | ptrdiff_t, ptrdiff_t); | 116 | ptrdiff_t, ptrdiff_t); |
| 115 | extern struct itree_tree *itree_create (void); | 117 | extern struct itree_tree *itree_create (void); |
| 116 | extern void itree_destroy (struct itree_tree *); | 118 | extern void itree_destroy (struct itree_tree *); |
| 119 | INLINE bool | ||
| 120 | itree_empty_p (struct itree_tree *tree) | ||
| 121 | { | ||
| 122 | return !tree || !tree->root; | ||
| 123 | } | ||
| 117 | extern intmax_t itree_size (struct itree_tree *); | 124 | extern intmax_t itree_size (struct itree_tree *); |
| 118 | extern void itree_clear (struct itree_tree *); | 125 | extern void itree_clear (struct itree_tree *); |
| 119 | extern void itree_insert (struct itree_tree *, struct itree_node *, | 126 | extern void itree_insert (struct itree_tree *, struct itree_node *, |
| @@ -178,4 +185,6 @@ struct itree_iterator | |||
| 178 | #define ITREE_FOREACH_NARROW(beg, end) \ | 185 | #define ITREE_FOREACH_NARROW(beg, end) \ |
| 179 | itree_iterator_narrow (itree_iter_, beg, end) | 186 | itree_iterator_narrow (itree_iter_, beg, end) |
| 180 | 187 | ||
| 188 | INLINE_HEADER_END | ||
| 189 | |||
| 181 | #endif | 190 | #endif |
diff --git a/src/keyboard.c b/src/keyboard.c index 811998823cc..d68b50428a9 100644 --- a/src/keyboard.c +++ b/src/keyboard.c | |||
| @@ -1910,10 +1910,14 @@ safe_run_hooks_maybe_narrowed (Lisp_Object hook, struct window *w) | |||
| 1910 | 1910 | ||
| 1911 | specbind (Qinhibit_quit, Qt); | 1911 | specbind (Qinhibit_quit, Qt); |
| 1912 | 1912 | ||
| 1913 | if (current_buffer->long_line_optimizations_p) | 1913 | if (current_buffer->long_line_optimizations_p |
| 1914 | narrow_to_region_internal (make_fixnum (get_narrowed_begv (w, PT)), | 1914 | && long_line_locked_narrowing_region_size > 0) |
| 1915 | make_fixnum (get_narrowed_zv (w, PT)), | 1915 | { |
| 1916 | true); | 1916 | ptrdiff_t begv = get_locked_narrowing_begv (PT); |
| 1917 | ptrdiff_t zv = get_locked_narrowing_zv (PT); | ||
| 1918 | if (begv != BEG || zv != Z) | ||
| 1919 | narrow_to_region_locked (make_fixnum (begv), make_fixnum (zv), hook); | ||
| 1920 | } | ||
| 1917 | 1921 | ||
| 1918 | run_hook_with_args (2, ((Lisp_Object []) {hook, hook}), | 1922 | run_hook_with_args (2, ((Lisp_Object []) {hook, hook}), |
| 1919 | safe_run_hook_funcall); | 1923 | safe_run_hook_funcall); |
| @@ -12727,8 +12731,9 @@ the error might happen repeatedly and make Emacs nonfunctional. | |||
| 12727 | 12731 | ||
| 12728 | Note that, when the current buffer contains one or more lines whose | 12732 | Note that, when the current buffer contains one or more lines whose |
| 12729 | length is above `long-line-threshold', these hook functions are called | 12733 | length is above `long-line-threshold', these hook functions are called |
| 12730 | with the buffer narrowed to a small portion around point, and the | 12734 | with the buffer narrowed to a small portion around point (whose size |
| 12731 | narrowing is locked (see `narrow-to-region'), so that these hook | 12735 | is specified by `long-line-locked-narrowing-region-size'), and the |
| 12736 | narrowing is locked (see `narrowing-lock'), so that these hook | ||
| 12732 | functions cannot use `widen' to gain access to other portions of | 12737 | functions cannot use `widen' to gain access to other portions of |
| 12733 | buffer text. | 12738 | buffer text. |
| 12734 | 12739 | ||
| @@ -12748,8 +12753,9 @@ avoid making Emacs unresponsive while the user types. | |||
| 12748 | 12753 | ||
| 12749 | Note that, when the current buffer contains one or more lines whose | 12754 | Note that, when the current buffer contains one or more lines whose |
| 12750 | length is above `long-line-threshold', these hook functions are called | 12755 | length is above `long-line-threshold', these hook functions are called |
| 12751 | with the buffer narrowed to a small portion around point, and the | 12756 | with the buffer narrowed to a small portion around point (whose size |
| 12752 | narrowing is locked (see `narrow-to-region'), so that these hook | 12757 | is specified by `long-line-locked-narrowing-region-size'), and the |
| 12758 | narrowing is locked (see `narrowing-lock'), so that these hook | ||
| 12753 | functions cannot use `widen' to gain access to other portions of | 12759 | functions cannot use `widen' to gain access to other portions of |
| 12754 | buffer text. | 12760 | buffer text. |
| 12755 | 12761 | ||
diff --git a/src/lisp.h b/src/lisp.h index 3d9c48449aa..3791bf2b0c3 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -4728,7 +4728,8 @@ extern void save_restriction_restore (Lisp_Object); | |||
| 4728 | extern Lisp_Object make_buffer_string (ptrdiff_t, ptrdiff_t, bool); | 4728 | extern Lisp_Object make_buffer_string (ptrdiff_t, ptrdiff_t, bool); |
| 4729 | extern Lisp_Object make_buffer_string_both (ptrdiff_t, ptrdiff_t, ptrdiff_t, | 4729 | extern Lisp_Object make_buffer_string_both (ptrdiff_t, ptrdiff_t, ptrdiff_t, |
| 4730 | ptrdiff_t, bool); | 4730 | ptrdiff_t, bool); |
| 4731 | extern Lisp_Object narrow_to_region_internal (Lisp_Object, Lisp_Object, bool); | 4731 | extern void narrow_to_region_locked (Lisp_Object, Lisp_Object, Lisp_Object); |
| 4732 | extern void reset_outermost_narrowings (void); | ||
| 4732 | extern void init_editfns (void); | 4733 | extern void init_editfns (void); |
| 4733 | extern void syms_of_editfns (void); | 4734 | extern void syms_of_editfns (void); |
| 4734 | 4735 | ||
| @@ -5336,6 +5337,26 @@ __lsan_ignore_object (void const *p) | |||
| 5336 | } | 5337 | } |
| 5337 | #endif | 5338 | #endif |
| 5338 | 5339 | ||
| 5340 | /* If built with USE_SANITIZER_UNALIGNED_LOAD defined, use compiler | ||
| 5341 | provided ASan functions to perform unaligned loads, allowing ASan | ||
| 5342 | to catch bugs which it might otherwise miss. */ | ||
| 5343 | #if defined HAVE_SANITIZER_COMMON_INTERFACE_DEFS_H \ | ||
| 5344 | && defined ADDRESS_SANITIZER \ | ||
| 5345 | && defined USE_SANITIZER_UNALIGNED_LOAD | ||
| 5346 | # include <sanitizer/common_interface_defs.h> | ||
| 5347 | # if (SIZE_MAX == UINT64_MAX) | ||
| 5348 | # define UNALIGNED_LOAD_SIZE(a, i) \ | ||
| 5349 | (size_t) __sanitizer_unaligned_load64 ((void *) ((a) + (i))) | ||
| 5350 | # elif (SIZE_MAX == UINT32_MAX) | ||
| 5351 | # define UNALIGNED_LOAD_SIZE(a, i) \ | ||
| 5352 | (size_t) __sanitizer_unaligned_load32 ((void *) ((a) + (i))) | ||
| 5353 | # else | ||
| 5354 | # define UNALIGNED_LOAD_SIZE(a, i) *((a) + (i)) | ||
| 5355 | # endif | ||
| 5356 | #else | ||
| 5357 | # define UNALIGNED_LOAD_SIZE(a, i) *((a) + (i)) | ||
| 5358 | #endif | ||
| 5359 | |||
| 5339 | extern void xputenv (const char *); | 5360 | extern void xputenv (const char *); |
| 5340 | 5361 | ||
| 5341 | extern char *egetenv_internal (const char *, ptrdiff_t); | 5362 | extern char *egetenv_internal (const char *, ptrdiff_t); |
diff --git a/src/lread.c b/src/lread.c index 0a6e4201e40..d838a18de5a 100644 --- a/src/lread.c +++ b/src/lread.c | |||
| @@ -3375,7 +3375,7 @@ read_bool_vector (Lisp_Object readcharfun) | |||
| 3375 | break; | 3375 | break; |
| 3376 | } | 3376 | } |
| 3377 | if (INT_MULTIPLY_WRAPV (length, 10, &length) | 3377 | if (INT_MULTIPLY_WRAPV (length, 10, &length) |
| 3378 | | INT_ADD_WRAPV (length, c - '0', &length)) | 3378 | || INT_ADD_WRAPV (length, c - '0', &length)) |
| 3379 | invalid_syntax ("#&", readcharfun); | 3379 | invalid_syntax ("#&", readcharfun); |
| 3380 | } | 3380 | } |
| 3381 | 3381 | ||
| @@ -3421,7 +3421,7 @@ skip_lazy_string (Lisp_Object readcharfun) | |||
| 3421 | break; | 3421 | break; |
| 3422 | } | 3422 | } |
| 3423 | if (INT_MULTIPLY_WRAPV (nskip, 10, &nskip) | 3423 | if (INT_MULTIPLY_WRAPV (nskip, 10, &nskip) |
| 3424 | | INT_ADD_WRAPV (nskip, c - '0', &nskip)) | 3424 | || INT_ADD_WRAPV (nskip, c - '0', &nskip)) |
| 3425 | invalid_syntax ("#@", readcharfun); | 3425 | invalid_syntax ("#@", readcharfun); |
| 3426 | digits++; | 3426 | digits++; |
| 3427 | if (digits == 2 && nskip == 0) | 3427 | if (digits == 2 && nskip == 0) |
| @@ -5468,15 +5468,6 @@ to the specified file name if a suffix is allowed or required. */); | |||
| 5468 | Vload_suffixes = | 5468 | Vload_suffixes = |
| 5469 | Fcons (build_pure_c_string (MODULES_SECONDARY_SUFFIX), Vload_suffixes); | 5469 | Fcons (build_pure_c_string (MODULES_SECONDARY_SUFFIX), Vload_suffixes); |
| 5470 | #endif | 5470 | #endif |
| 5471 | |||
| 5472 | DEFVAR_LISP ("dynamic-library-suffixes", Vdynamic_library_suffixes, | ||
| 5473 | doc: /* A list of suffixes for loadable dynamic libraries. */); | ||
| 5474 | Vdynamic_library_suffixes = | ||
| 5475 | Fcons (build_pure_c_string (DYNAMIC_LIB_SECONDARY_SUFFIX), Qnil); | ||
| 5476 | Vdynamic_library_suffixes = | ||
| 5477 | Fcons (build_pure_c_string (DYNAMIC_LIB_SUFFIX), | ||
| 5478 | Vdynamic_library_suffixes); | ||
| 5479 | |||
| 5480 | #endif | 5471 | #endif |
| 5481 | DEFVAR_LISP ("module-file-suffix", Vmodule_file_suffix, | 5472 | DEFVAR_LISP ("module-file-suffix", Vmodule_file_suffix, |
| 5482 | doc: /* Suffix of loadable module file, or nil if modules are not supported. */); | 5473 | doc: /* Suffix of loadable module file, or nil if modules are not supported. */); |
| @@ -5485,6 +5476,20 @@ to the specified file name if a suffix is allowed or required. */); | |||
| 5485 | #else | 5476 | #else |
| 5486 | Vmodule_file_suffix = Qnil; | 5477 | Vmodule_file_suffix = Qnil; |
| 5487 | #endif | 5478 | #endif |
| 5479 | |||
| 5480 | DEFVAR_LISP ("dynamic-library-suffixes", Vdynamic_library_suffixes, | ||
| 5481 | doc: /* A list of suffixes for loadable dynamic libraries. */); | ||
| 5482 | |||
| 5483 | #ifndef MSDOS | ||
| 5484 | Vdynamic_library_suffixes | ||
| 5485 | = Fcons (build_pure_c_string (DYNAMIC_LIB_SECONDARY_SUFFIX), Qnil); | ||
| 5486 | Vdynamic_library_suffixes | ||
| 5487 | = Fcons (build_pure_c_string (DYNAMIC_LIB_SUFFIX), | ||
| 5488 | Vdynamic_library_suffixes); | ||
| 5489 | #else | ||
| 5490 | Vdynamic_library_suffixes = Qnil; | ||
| 5491 | #endif | ||
| 5492 | |||
| 5488 | DEFVAR_LISP ("load-file-rep-suffixes", Vload_file_rep_suffixes, | 5493 | DEFVAR_LISP ("load-file-rep-suffixes", Vload_file_rep_suffixes, |
| 5489 | doc: /* List of suffixes that indicate representations of \ | 5494 | doc: /* List of suffixes that indicate representations of \ |
| 5490 | the same file. | 5495 | the same file. |
diff --git a/src/module-env-29.h b/src/module-env-29.h index 6ca03773181..e69de29bb2d 100644 --- a/src/module-env-29.h +++ b/src/module-env-29.h | |||
| @@ -1,3 +0,0 @@ | |||
| 1 | /* Add module environment functions newly added in Emacs 29 here. | ||
| 2 | Before Emacs 29 is released, remove this comment and start | ||
| 3 | module-env-30.h on the master branch. */ | ||
diff --git a/src/module-env-30.h b/src/module-env-30.h new file mode 100644 index 00000000000..6ca03773181 --- /dev/null +++ b/src/module-env-30.h | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | /* Add module environment functions newly added in Emacs 29 here. | ||
| 2 | Before Emacs 29 is released, remove this comment and start | ||
| 3 | module-env-30.h on the master branch. */ | ||
diff --git a/src/pdumper.c b/src/pdumper.c index 75fe697dcd7..70f90f0d8aa 100644 --- a/src/pdumper.c +++ b/src/pdumper.c | |||
| @@ -2748,7 +2748,7 @@ dump_hash_table (struct dump_context *ctx, | |||
| 2748 | static dump_off | 2748 | static dump_off |
| 2749 | dump_buffer (struct dump_context *ctx, const struct buffer *in_buffer) | 2749 | dump_buffer (struct dump_context *ctx, const struct buffer *in_buffer) |
| 2750 | { | 2750 | { |
| 2751 | #if CHECK_STRUCTS && !defined HASH_buffer_193CAA5E45 | 2751 | #if CHECK_STRUCTS && !defined HASH_buffer_DB34E5D09F |
| 2752 | # error "buffer changed. See CHECK_STRUCTS comment in config.h." | 2752 | # error "buffer changed. See CHECK_STRUCTS comment in config.h." |
| 2753 | #endif | 2753 | #endif |
| 2754 | struct buffer munged_buffer = *in_buffer; | 2754 | struct buffer munged_buffer = *in_buffer; |
| @@ -2811,7 +2811,6 @@ dump_buffer (struct dump_context *ctx, const struct buffer *in_buffer) | |||
| 2811 | DUMP_FIELD_COPY (out, buffer, own_text.end_unchanged); | 2811 | DUMP_FIELD_COPY (out, buffer, own_text.end_unchanged); |
| 2812 | DUMP_FIELD_COPY (out, buffer, own_text.unchanged_modified); | 2812 | DUMP_FIELD_COPY (out, buffer, own_text.unchanged_modified); |
| 2813 | DUMP_FIELD_COPY (out, buffer, own_text.overlay_unchanged_modified); | 2813 | DUMP_FIELD_COPY (out, buffer, own_text.overlay_unchanged_modified); |
| 2814 | DUMP_FIELD_COPY (out, buffer, own_text.chars_unchanged_modified); | ||
| 2815 | if (buffer->own_text.intervals) | 2814 | if (buffer->own_text.intervals) |
| 2816 | dump_field_fixup_later (ctx, out, buffer, &buffer->own_text.intervals); | 2815 | dump_field_fixup_later (ctx, out, buffer, &buffer->own_text.intervals); |
| 2817 | dump_field_lv_rawptr (ctx, out, buffer, &buffer->own_text.markers, | 2816 | dump_field_lv_rawptr (ctx, out, buffer, &buffer->own_text.markers, |
| @@ -2863,7 +2862,7 @@ dump_buffer (struct dump_context *ctx, const struct buffer *in_buffer) | |||
| 2863 | DUMP_FIELD_COPY (out, buffer, inhibit_buffer_hooks); | 2862 | DUMP_FIELD_COPY (out, buffer, inhibit_buffer_hooks); |
| 2864 | DUMP_FIELD_COPY (out, buffer, long_line_optimizations_p); | 2863 | DUMP_FIELD_COPY (out, buffer, long_line_optimizations_p); |
| 2865 | 2864 | ||
| 2866 | if (buffer->overlays && buffer->overlays->root != NULL) | 2865 | if (!itree_empty_p (buffer->overlays)) |
| 2867 | /* We haven't implemented the code to dump overlays. */ | 2866 | /* We haven't implemented the code to dump overlays. */ |
| 2868 | emacs_abort (); | 2867 | emacs_abort (); |
| 2869 | else | 2868 | else |
| @@ -3003,7 +3002,7 @@ dump_vectorlike (struct dump_context *ctx, | |||
| 3003 | Lisp_Object lv, | 3002 | Lisp_Object lv, |
| 3004 | dump_off offset) | 3003 | dump_off offset) |
| 3005 | { | 3004 | { |
| 3006 | #if CHECK_STRUCTS && !defined HASH_pvec_type_AFF6FED5BD | 3005 | #if CHECK_STRUCTS && !defined HASH_pvec_type_5F2059C47E |
| 3007 | # error "pvec_type changed. See CHECK_STRUCTS comment in config.h." | 3006 | # error "pvec_type changed. See CHECK_STRUCTS comment in config.h." |
| 3008 | #endif | 3007 | #endif |
| 3009 | const struct Lisp_Vector *v = XVECTOR (lv); | 3008 | const struct Lisp_Vector *v = XVECTOR (lv); |
diff --git a/src/sqlite.c b/src/sqlite.c index ac860f55bcd..d9b9333fb3c 100644 --- a/src/sqlite.c +++ b/src/sqlite.c | |||
| @@ -55,6 +55,7 @@ DEF_DLL_FN (SQLITE_API const char*, sqlite3_errmsg, (sqlite3*)); | |||
| 55 | #if SQLITE_VERSION_NUMBER >= 3007015 | 55 | #if SQLITE_VERSION_NUMBER >= 3007015 |
| 56 | DEF_DLL_FN (SQLITE_API const char*, sqlite3_errstr, (int)); | 56 | DEF_DLL_FN (SQLITE_API const char*, sqlite3_errstr, (int)); |
| 57 | #endif | 57 | #endif |
| 58 | DEF_DLL_FN (SQLITE_API const char*, sqlite3_libversion, (void)); | ||
| 58 | DEF_DLL_FN (SQLITE_API int, sqlite3_step, (sqlite3_stmt*)); | 59 | DEF_DLL_FN (SQLITE_API int, sqlite3_step, (sqlite3_stmt*)); |
| 59 | DEF_DLL_FN (SQLITE_API int, sqlite3_changes, (sqlite3*)); | 60 | DEF_DLL_FN (SQLITE_API int, sqlite3_changes, (sqlite3*)); |
| 60 | DEF_DLL_FN (SQLITE_API int, sqlite3_column_count, (sqlite3_stmt*)); | 61 | DEF_DLL_FN (SQLITE_API int, sqlite3_column_count, (sqlite3_stmt*)); |
| @@ -96,6 +97,7 @@ DEF_DLL_FN (SQLITE_API int, sqlite3_load_extension, | |||
| 96 | # if SQLITE_VERSION_NUMBER >= 3007015 | 97 | # if SQLITE_VERSION_NUMBER >= 3007015 |
| 97 | # undef sqlite3_errstr | 98 | # undef sqlite3_errstr |
| 98 | # endif | 99 | # endif |
| 100 | # undef sqlite3_libversion | ||
| 99 | # undef sqlite3_step | 101 | # undef sqlite3_step |
| 100 | # undef sqlite3_changes | 102 | # undef sqlite3_changes |
| 101 | # undef sqlite3_column_count | 103 | # undef sqlite3_column_count |
| @@ -124,6 +126,7 @@ DEF_DLL_FN (SQLITE_API int, sqlite3_load_extension, | |||
| 124 | # if SQLITE_VERSION_NUMBER >= 3007015 | 126 | # if SQLITE_VERSION_NUMBER >= 3007015 |
| 125 | # define sqlite3_errstr fn_sqlite3_errstr | 127 | # define sqlite3_errstr fn_sqlite3_errstr |
| 126 | # endif | 128 | # endif |
| 129 | # define sqlite3_libversion fn_sqlite3_libversion | ||
| 127 | # define sqlite3_step fn_sqlite3_step | 130 | # define sqlite3_step fn_sqlite3_step |
| 128 | # define sqlite3_changes fn_sqlite3_changes | 131 | # define sqlite3_changes fn_sqlite3_changes |
| 129 | # define sqlite3_column_count fn_sqlite3_column_count | 132 | # define sqlite3_column_count fn_sqlite3_column_count |
| @@ -155,6 +158,7 @@ load_dll_functions (HMODULE library) | |||
| 155 | #if SQLITE_VERSION_NUMBER >= 3007015 | 158 | #if SQLITE_VERSION_NUMBER >= 3007015 |
| 156 | LOAD_DLL_FN (library, sqlite3_errstr); | 159 | LOAD_DLL_FN (library, sqlite3_errstr); |
| 157 | #endif | 160 | #endif |
| 161 | LOAD_DLL_FN (library, sqlite3_libversion); | ||
| 158 | LOAD_DLL_FN (library, sqlite3_step); | 162 | LOAD_DLL_FN (library, sqlite3_step); |
| 159 | LOAD_DLL_FN (library, sqlite3_changes); | 163 | LOAD_DLL_FN (library, sqlite3_changes); |
| 160 | LOAD_DLL_FN (library, sqlite3_column_count); | 164 | LOAD_DLL_FN (library, sqlite3_column_count); |
| @@ -763,6 +767,16 @@ This will free the resources held by SET. */) | |||
| 763 | return Qt; | 767 | return Qt; |
| 764 | } | 768 | } |
| 765 | 769 | ||
| 770 | DEFUN ("sqlite-version", Fsqlite_version, Ssqlite_version, 0, 0, 0, | ||
| 771 | doc: /* Return the version string of the SQLite library. | ||
| 772 | Signal an error if SQLite support is not available. */) | ||
| 773 | (void) | ||
| 774 | { | ||
| 775 | if (!init_sqlite_functions ()) | ||
| 776 | error ("sqlite support is not available"); | ||
| 777 | return build_string (sqlite3_libversion ()); | ||
| 778 | } | ||
| 779 | |||
| 766 | #endif /* HAVE_SQLITE3 */ | 780 | #endif /* HAVE_SQLITE3 */ |
| 767 | 781 | ||
| 768 | DEFUN ("sqlitep", Fsqlitep, Ssqlitep, 1, 1, 0, | 782 | DEFUN ("sqlitep", Fsqlitep, Ssqlitep, 1, 1, 0, |
| @@ -814,6 +828,7 @@ syms_of_sqlite (void) | |||
| 814 | defsubr (&Ssqlite_columns); | 828 | defsubr (&Ssqlite_columns); |
| 815 | defsubr (&Ssqlite_more_p); | 829 | defsubr (&Ssqlite_more_p); |
| 816 | defsubr (&Ssqlite_finalize); | 830 | defsubr (&Ssqlite_finalize); |
| 831 | defsubr (&Ssqlite_version); | ||
| 817 | DEFSYM (Qset, "set"); | 832 | DEFSYM (Qset, "set"); |
| 818 | DEFSYM (Qfull, "full"); | 833 | DEFSYM (Qfull, "full"); |
| 819 | #endif | 834 | #endif |
diff --git a/src/sysdep.c b/src/sysdep.c index 736723bdf3d..8402ffe308c 100644 --- a/src/sysdep.c +++ b/src/sysdep.c | |||
| @@ -2653,10 +2653,11 @@ emacs_perror (char const *message) | |||
| 2653 | int | 2653 | int |
| 2654 | renameat_noreplace (int srcfd, char const *src, int dstfd, char const *dst) | 2654 | renameat_noreplace (int srcfd, char const *src, int dstfd, char const *dst) |
| 2655 | { | 2655 | { |
| 2656 | #if defined SYS_renameat2 && defined RENAME_NOREPLACE | 2656 | #if HAVE_RENAMEAT2 && defined RENAME_NOREPLACE |
| 2657 | return syscall (SYS_renameat2, srcfd, src, dstfd, dst, RENAME_NOREPLACE); | ||
| 2658 | #elif defined CYGWIN && defined RENAME_NOREPLACE | ||
| 2659 | return renameat2 (srcfd, src, dstfd, dst, RENAME_NOREPLACE); | 2657 | return renameat2 (srcfd, src, dstfd, dst, RENAME_NOREPLACE); |
| 2658 | #elif defined SYS_renameat2 && defined RENAME_NOREPLACE | ||
| 2659 | /* Linux kernel 3.15 (2014) or later, with glibc 2.27 (2018) or earlier. */ | ||
| 2660 | return syscall (SYS_renameat2, srcfd, src, dstfd, dst, RENAME_NOREPLACE); | ||
| 2660 | #elif defined RENAME_EXCL | 2661 | #elif defined RENAME_EXCL |
| 2661 | return renameatx_np (srcfd, src, dstfd, dst, RENAME_EXCL); | 2662 | return renameatx_np (srcfd, src, dstfd, dst, RENAME_EXCL); |
| 2662 | #else | 2663 | #else |
diff --git a/src/timefns.c b/src/timefns.c index eed2edf1cc0..dcc6403fd9d 100644 --- a/src/timefns.c +++ b/src/timefns.c | |||
| @@ -40,6 +40,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 40 | #include <stdio.h> | 40 | #include <stdio.h> |
| 41 | #include <stdlib.h> | 41 | #include <stdlib.h> |
| 42 | 42 | ||
| 43 | #ifdef WINDOWSNT | ||
| 44 | extern clock_t sys_clock (void); | ||
| 45 | #endif | ||
| 46 | |||
| 43 | #ifdef HAVE_TIMEZONE_T | 47 | #ifdef HAVE_TIMEZONE_T |
| 44 | # include <sys/param.h> | 48 | # include <sys/param.h> |
| 45 | # if defined __NetBSD_Version__ && __NetBSD_Version__ < 700000000 | 49 | # if defined __NetBSD_Version__ && __NetBSD_Version__ < 700000000 |
| @@ -1194,7 +1198,7 @@ For example, nil stands for the current time. */) | |||
| 1194 | quicker while we're at it. This means (time-subtract X X) does | 1198 | quicker while we're at it. This means (time-subtract X X) does |
| 1195 | not signal an error if X is not a valid time value, but that's OK. */ | 1199 | not signal an error if X is not a valid time value, but that's OK. */ |
| 1196 | if (BASE_EQ (a, b)) | 1200 | if (BASE_EQ (a, b)) |
| 1197 | return timespec_to_lisp ((struct timespec) {0}); | 1201 | return make_lisp_time ((struct timespec) {0}); |
| 1198 | 1202 | ||
| 1199 | return time_arith (a, b, true); | 1203 | return time_arith (a, b, true); |
| 1200 | } | 1204 | } |
diff --git a/src/treesit.c b/src/treesit.c index 21e1f866f78..c882d455137 100644 --- a/src/treesit.c +++ b/src/treesit.c | |||
| @@ -52,7 +52,6 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 52 | #undef ts_node_named_descendant_for_byte_range | 52 | #undef ts_node_named_descendant_for_byte_range |
| 53 | #undef ts_node_next_named_sibling | 53 | #undef ts_node_next_named_sibling |
| 54 | #undef ts_node_next_sibling | 54 | #undef ts_node_next_sibling |
| 55 | #undef ts_node_parent | ||
| 56 | #undef ts_node_prev_named_sibling | 55 | #undef ts_node_prev_named_sibling |
| 57 | #undef ts_node_prev_sibling | 56 | #undef ts_node_prev_sibling |
| 58 | #undef ts_node_start_byte | 57 | #undef ts_node_start_byte |
| @@ -76,7 +75,9 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 76 | #undef ts_query_predicates_for_pattern | 75 | #undef ts_query_predicates_for_pattern |
| 77 | #undef ts_query_string_value_for_id | 76 | #undef ts_query_string_value_for_id |
| 78 | #undef ts_set_allocator | 77 | #undef ts_set_allocator |
| 78 | #undef ts_tree_cursor_copy | ||
| 79 | #undef ts_tree_cursor_current_node | 79 | #undef ts_tree_cursor_current_node |
| 80 | #undef ts_tree_cursor_delete | ||
| 80 | #undef ts_tree_cursor_goto_first_child | 81 | #undef ts_tree_cursor_goto_first_child |
| 81 | #undef ts_tree_cursor_goto_next_sibling | 82 | #undef ts_tree_cursor_goto_next_sibling |
| 82 | #undef ts_tree_cursor_goto_parent | 83 | #undef ts_tree_cursor_goto_parent |
| @@ -109,7 +110,6 @@ DEF_DLL_FN (TSNode, ts_node_named_descendant_for_byte_range, | |||
| 109 | (TSNode, uint32_t, uint32_t)); | 110 | (TSNode, uint32_t, uint32_t)); |
| 110 | DEF_DLL_FN (TSNode, ts_node_next_named_sibling, (TSNode)); | 111 | DEF_DLL_FN (TSNode, ts_node_next_named_sibling, (TSNode)); |
| 111 | DEF_DLL_FN (TSNode, ts_node_next_sibling, (TSNode)); | 112 | DEF_DLL_FN (TSNode, ts_node_next_sibling, (TSNode)); |
| 112 | DEF_DLL_FN (TSNode, ts_node_parent, (TSNode)); | ||
| 113 | DEF_DLL_FN (TSNode, ts_node_prev_named_sibling, (TSNode)); | 113 | DEF_DLL_FN (TSNode, ts_node_prev_named_sibling, (TSNode)); |
| 114 | DEF_DLL_FN (TSNode, ts_node_prev_sibling, (TSNode)); | 114 | DEF_DLL_FN (TSNode, ts_node_prev_sibling, (TSNode)); |
| 115 | DEF_DLL_FN (uint32_t, ts_node_start_byte, (TSNode)); | 115 | DEF_DLL_FN (uint32_t, ts_node_start_byte, (TSNode)); |
| @@ -143,7 +143,9 @@ DEF_DLL_FN (const char *, ts_query_string_value_for_id, | |||
| 143 | (const TSQuery *, uint32_t, uint32_t *)); | 143 | (const TSQuery *, uint32_t, uint32_t *)); |
| 144 | DEF_DLL_FN (void, ts_set_allocator, | 144 | DEF_DLL_FN (void, ts_set_allocator, |
| 145 | (void *(*)(size_t), void *(*)(size_t, size_t), void *(*)(void *, size_t), void (*)(void *))); | 145 | (void *(*)(size_t), void *(*)(size_t, size_t), void *(*)(void *, size_t), void (*)(void *))); |
| 146 | DEF_DLL_FN (TSTreeCursor, ts_tree_cursor_copy, (const TSTreeCursor *)); | ||
| 146 | DEF_DLL_FN (TSNode, ts_tree_cursor_current_node, (const TSTreeCursor *)); | 147 | DEF_DLL_FN (TSNode, ts_tree_cursor_current_node, (const TSTreeCursor *)); |
| 148 | DEF_DLL_FN (void, ts_tree_cursor_delete, (const TSTreeCursor *)); | ||
| 147 | DEF_DLL_FN (bool, ts_tree_cursor_goto_first_child, (TSTreeCursor *)); | 149 | DEF_DLL_FN (bool, ts_tree_cursor_goto_first_child, (TSTreeCursor *)); |
| 148 | DEF_DLL_FN (bool, ts_tree_cursor_goto_next_sibling, (TSTreeCursor *)); | 150 | DEF_DLL_FN (bool, ts_tree_cursor_goto_next_sibling, (TSTreeCursor *)); |
| 149 | DEF_DLL_FN (bool, ts_tree_cursor_goto_parent, (TSTreeCursor *)); | 151 | DEF_DLL_FN (bool, ts_tree_cursor_goto_parent, (TSTreeCursor *)); |
| @@ -182,7 +184,6 @@ init_treesit_functions (void) | |||
| 182 | LOAD_DLL_FN (library, ts_node_named_descendant_for_byte_range); | 184 | LOAD_DLL_FN (library, ts_node_named_descendant_for_byte_range); |
| 183 | LOAD_DLL_FN (library, ts_node_next_named_sibling); | 185 | LOAD_DLL_FN (library, ts_node_next_named_sibling); |
| 184 | LOAD_DLL_FN (library, ts_node_next_sibling); | 186 | LOAD_DLL_FN (library, ts_node_next_sibling); |
| 185 | LOAD_DLL_FN (library, ts_node_parent); | ||
| 186 | LOAD_DLL_FN (library, ts_node_prev_named_sibling); | 187 | LOAD_DLL_FN (library, ts_node_prev_named_sibling); |
| 187 | LOAD_DLL_FN (library, ts_node_prev_sibling); | 188 | LOAD_DLL_FN (library, ts_node_prev_sibling); |
| 188 | LOAD_DLL_FN (library, ts_node_start_byte); | 189 | LOAD_DLL_FN (library, ts_node_start_byte); |
| @@ -206,7 +207,9 @@ init_treesit_functions (void) | |||
| 206 | LOAD_DLL_FN (library, ts_query_predicates_for_pattern); | 207 | LOAD_DLL_FN (library, ts_query_predicates_for_pattern); |
| 207 | LOAD_DLL_FN (library, ts_query_string_value_for_id); | 208 | LOAD_DLL_FN (library, ts_query_string_value_for_id); |
| 208 | LOAD_DLL_FN (library, ts_set_allocator); | 209 | LOAD_DLL_FN (library, ts_set_allocator); |
| 210 | LOAD_DLL_FN (library, ts_tree_cursor_copy); | ||
| 209 | LOAD_DLL_FN (library, ts_tree_cursor_current_node); | 211 | LOAD_DLL_FN (library, ts_tree_cursor_current_node); |
| 212 | LOAD_DLL_FN (library, ts_tree_cursor_delete); | ||
| 210 | LOAD_DLL_FN (library, ts_tree_cursor_goto_first_child); | 213 | LOAD_DLL_FN (library, ts_tree_cursor_goto_first_child); |
| 211 | LOAD_DLL_FN (library, ts_tree_cursor_goto_next_sibling); | 214 | LOAD_DLL_FN (library, ts_tree_cursor_goto_next_sibling); |
| 212 | LOAD_DLL_FN (library, ts_tree_cursor_goto_parent); | 215 | LOAD_DLL_FN (library, ts_tree_cursor_goto_parent); |
| @@ -239,7 +242,6 @@ init_treesit_functions (void) | |||
| 239 | #define ts_node_named_descendant_for_byte_range fn_ts_node_named_descendant_for_byte_range | 242 | #define ts_node_named_descendant_for_byte_range fn_ts_node_named_descendant_for_byte_range |
| 240 | #define ts_node_next_named_sibling fn_ts_node_next_named_sibling | 243 | #define ts_node_next_named_sibling fn_ts_node_next_named_sibling |
| 241 | #define ts_node_next_sibling fn_ts_node_next_sibling | 244 | #define ts_node_next_sibling fn_ts_node_next_sibling |
| 242 | #define ts_node_parent fn_ts_node_parent | ||
| 243 | #define ts_node_prev_named_sibling fn_ts_node_prev_named_sibling | 245 | #define ts_node_prev_named_sibling fn_ts_node_prev_named_sibling |
| 244 | #define ts_node_prev_sibling fn_ts_node_prev_sibling | 246 | #define ts_node_prev_sibling fn_ts_node_prev_sibling |
| 245 | #define ts_node_start_byte fn_ts_node_start_byte | 247 | #define ts_node_start_byte fn_ts_node_start_byte |
| @@ -263,7 +265,9 @@ init_treesit_functions (void) | |||
| 263 | #define ts_query_predicates_for_pattern fn_ts_query_predicates_for_pattern | 265 | #define ts_query_predicates_for_pattern fn_ts_query_predicates_for_pattern |
| 264 | #define ts_query_string_value_for_id fn_ts_query_string_value_for_id | 266 | #define ts_query_string_value_for_id fn_ts_query_string_value_for_id |
| 265 | #define ts_set_allocator fn_ts_set_allocator | 267 | #define ts_set_allocator fn_ts_set_allocator |
| 268 | #define ts_tree_cursor_copy fn_ts_tree_cursor_copy | ||
| 266 | #define ts_tree_cursor_current_node fn_ts_tree_cursor_current_node | 269 | #define ts_tree_cursor_current_node fn_ts_tree_cursor_current_node |
| 270 | #define ts_tree_cursor_delete fn_ts_tree_cursor_delete | ||
| 267 | #define ts_tree_cursor_goto_first_child fn_ts_tree_cursor_goto_first_child | 271 | #define ts_tree_cursor_goto_first_child fn_ts_tree_cursor_goto_first_child |
| 268 | #define ts_tree_cursor_goto_next_sibling fn_ts_tree_cursor_goto_next_sibling | 272 | #define ts_tree_cursor_goto_next_sibling fn_ts_tree_cursor_goto_next_sibling |
| 269 | #define ts_tree_cursor_goto_parent fn_ts_tree_cursor_goto_parent | 273 | #define ts_tree_cursor_goto_parent fn_ts_tree_cursor_goto_parent |
| @@ -288,7 +292,7 @@ init_treesit_functions (void) | |||
| 288 | slow enough to make insignificant any performance advantages from | 292 | slow enough to make insignificant any performance advantages from |
| 289 | using the cursor. Not exposing the cursor also minimizes the | 293 | using the cursor. Not exposing the cursor also minimizes the |
| 290 | number of new types this adds to Emacs Lisp; currently, this adds | 294 | number of new types this adds to Emacs Lisp; currently, this adds |
| 291 | only the parser and node types. | 295 | only the parser, node, and compiled query types. |
| 292 | 296 | ||
| 293 | - Because updating the change is handled on the C level as each | 297 | - Because updating the change is handled on the C level as each |
| 294 | change is made in the buffer, there is no way for Lisp to update | 298 | change is made in the buffer, there is no way for Lisp to update |
| @@ -314,7 +318,7 @@ init_treesit_functions (void) | |||
| 314 | See: https://github.com/tree-sitter/tree-sitter/issues/445 | 318 | See: https://github.com/tree-sitter/tree-sitter/issues/445 |
| 315 | 319 | ||
| 316 | treesit.h has some commentary on the two main data structure for | 320 | treesit.h has some commentary on the two main data structure for |
| 317 | the parser and node. treesit_ensure_position_synced has some | 321 | the parser and node. treesit_sync_visible_region has some |
| 318 | commentary on how we make tree-sitter play well with narrowing (the | 322 | commentary on how we make tree-sitter play well with narrowing (the |
| 319 | tree-sitter parser only sees the visible region, so we need to | 323 | tree-sitter parser only sees the visible region, so we need to |
| 320 | translate positions back and forth). Most action happens in | 324 | translate positions back and forth). Most action happens in |
| @@ -384,7 +388,18 @@ init_treesit_functions (void) | |||
| 384 | mysteriously drops. 3) what if a user uses so many stuff that the | 388 | mysteriously drops. 3) what if a user uses so many stuff that the |
| 385 | default cache size (20) is not enough and we end up thrashing? | 389 | default cache size (20) is not enough and we end up thrashing? |
| 386 | These are all imaginary scenarios but they are not impossible | 390 | These are all imaginary scenarios but they are not impossible |
| 387 | :-) */ | 391 | :-) |
| 392 | |||
| 393 | Parsers in indirect buffers: We make indirect buffers to share the | ||
| 394 | parser of its base buffer. Indirect buffers and their base buffer | ||
| 395 | share the same buffer content but not other buffer attributes. If | ||
| 396 | they have separate parser lists, changes made in an indirect buffer | ||
| 397 | will only update parsers of that indirect buffer, and not parsers | ||
| 398 | in the base buffer or other indirect buffers, and vice versa. We | ||
| 399 | could keep track of all the base and indirect buffers, and update | ||
| 400 | all of their parsers, but ultimately decide to take a simpler | ||
| 401 | approach, which is to make indirect buffers share their base | ||
| 402 | buffer's parser list. The discussion can be found in bug#59693. */ | ||
| 388 | 403 | ||
| 389 | 404 | ||
| 390 | /*** Initialization */ | 405 | /*** Initialization */ |
| @@ -697,9 +712,10 @@ void | |||
| 697 | treesit_record_change (ptrdiff_t start_byte, ptrdiff_t old_end_byte, | 712 | treesit_record_change (ptrdiff_t start_byte, ptrdiff_t old_end_byte, |
| 698 | ptrdiff_t new_end_byte) | 713 | ptrdiff_t new_end_byte) |
| 699 | { | 714 | { |
| 700 | Lisp_Object parser_list; | 715 | struct buffer *base_buffer = current_buffer; |
| 701 | 716 | if (current_buffer->base_buffer) | |
| 702 | parser_list = BVAR (current_buffer, ts_parser_list); | 717 | base_buffer = current_buffer->base_buffer; |
| 718 | Lisp_Object parser_list = BVAR (base_buffer, ts_parser_list); | ||
| 703 | 719 | ||
| 704 | FOR_EACH_TAIL_SAFE (parser_list) | 720 | FOR_EACH_TAIL_SAFE (parser_list) |
| 705 | { | 721 | { |
| @@ -707,6 +723,8 @@ treesit_record_change (ptrdiff_t start_byte, ptrdiff_t old_end_byte, | |||
| 707 | Lisp_Object lisp_parser = XCAR (parser_list); | 723 | Lisp_Object lisp_parser = XCAR (parser_list); |
| 708 | treesit_check_parser (lisp_parser); | 724 | treesit_check_parser (lisp_parser); |
| 709 | TSTree *tree = XTS_PARSER (lisp_parser)->tree; | 725 | TSTree *tree = XTS_PARSER (lisp_parser)->tree; |
| 726 | /* See comment (ref:visible-beg-null) if you wonder why we don't | ||
| 727 | update visible_beg/end when tree is NULL. */ | ||
| 710 | if (tree != NULL) | 728 | if (tree != NULL) |
| 711 | { | 729 | { |
| 712 | eassert (start_byte <= old_end_byte); | 730 | eassert (start_byte <= old_end_byte); |
| @@ -742,7 +760,7 @@ treesit_record_change (ptrdiff_t start_byte, ptrdiff_t old_end_byte, | |||
| 742 | XTS_PARSER (lisp_parser)->timestamp++; | 760 | XTS_PARSER (lisp_parser)->timestamp++; |
| 743 | 761 | ||
| 744 | /* VISIBLE_BEG/END records tree-sitter's range of view in | 762 | /* VISIBLE_BEG/END records tree-sitter's range of view in |
| 745 | the buffer. Ee need to adjust them when tree-sitter's | 763 | the buffer. We need to adjust them when tree-sitter's |
| 746 | view changes. */ | 764 | view changes. */ |
| 747 | ptrdiff_t visi_beg_delta; | 765 | ptrdiff_t visi_beg_delta; |
| 748 | if (old_end_byte > new_end_byte) | 766 | if (old_end_byte > new_end_byte) |
| @@ -765,20 +783,68 @@ treesit_record_change (ptrdiff_t start_byte, ptrdiff_t old_end_byte, | |||
| 765 | } | 783 | } |
| 766 | } | 784 | } |
| 767 | 785 | ||
| 768 | /* Make sure PARSER's visible_beg and visible_end are in sync with | 786 | /* Comment (ref:visible-beg-null) The purpose of visible_beg/end is to |
| 769 | BUF_BEGV_BYTE and BUG_ZV_BYTE. When calling this function you must | 787 | keep track of "which part of the buffer does the tree-sitter tree |
| 770 | make sure the current buffer's size is not larger than UINT32_MAX. | 788 | see", in order to update the tree correctly. Visible_beg/end have |
| 771 | Basically always call treesit_check_buffer_size before this | 789 | two purposes: they "clip" buffer changes within them, and they |
| 772 | function. */ | 790 | translate positions in the buffer to positions in the tree |
| 791 | (buffer position - visible_beg = tree position). | ||
| 792 | |||
| 793 | Conceptually, visible_beg/end hold the visible region of the buffer | ||
| 794 | when we last reparsed. In-between two reparses, we don't really | ||
| 795 | care if the visible region of the buffer changes. | ||
| 796 | |||
| 797 | Right before we reparse, we make tree-sitter's visible region | ||
| 798 | match that of the buffer, and update visible_beg/end. | ||
| 799 | |||
| 800 | That is, the whole purpose of visible_beg/end (and also of | ||
| 801 | treesit_record_change and treesit_sync_visible_region) is to update | ||
| 802 | the tree (by ts_tree_edit). So if the tree is NULL, | ||
| 803 | visible_beg/end are considered uninitialized. Only when we already | ||
| 804 | have a tree, do we need to keep track of position changes and | ||
| 805 | update it correctly, so it can be fed into ts_parser_parse as the | ||
| 806 | old tree, so that tree-sitter will only parse the changed part, | ||
| 807 | incrementally. | ||
| 808 | |||
| 809 | In a nutshell, tree-sitter incremental parsing in Emacs looks like: | ||
| 810 | |||
| 811 | treesit_record_change (tree) \ | ||
| 812 | treesit_record_change (tree) | user edits buffer | ||
| 813 | ... / | ||
| 814 | |||
| 815 | treesit_sync_visible_region (tree) \ treesit_ensure_parsed | ||
| 816 | ts_parser_parse(tree) -> tree / | ||
| 817 | |||
| 818 | treesit_record_change (tree) \ | ||
| 819 | treesit_record_change (tree) | user edits buffer | ||
| 820 | ... / | ||
| 821 | |||
| 822 | and so on. */ | ||
| 823 | |||
| 824 | /* Make sure the tree's visible range is in sync with the buffer's | ||
| 825 | visible range, and PARSER's visible_beg and visible_end are in sync | ||
| 826 | with BUF_BEGV_BYTE and BUG_ZV_BYTE. When calling this function you | ||
| 827 | must make sure the current buffer's size in bytes is not larger than | ||
| 828 | UINT32_MAX. Basically, always call treesit_check_buffer_size before | ||
| 829 | this function. | ||
| 830 | |||
| 831 | If buffer range changed since last parse (visible_beg/end doesn't | ||
| 832 | match buffer visible beginning/end), set need_reparse to true. */ | ||
| 773 | static void | 833 | static void |
| 774 | treesit_ensure_position_synced (Lisp_Object parser) | 834 | treesit_sync_visible_region (Lisp_Object parser) |
| 775 | { | 835 | { |
| 776 | TSTree *tree = XTS_PARSER (parser)->tree; | 836 | TSTree *tree = XTS_PARSER (parser)->tree; |
| 837 | struct buffer *buffer = XBUFFER (XTS_PARSER (parser)->buffer); | ||
| 777 | 838 | ||
| 839 | /* If we are setting visible_beg/end for the first time, we can skip | ||
| 840 | the offset acrobatics and updating the tree below. */ | ||
| 778 | if (tree == NULL) | 841 | if (tree == NULL) |
| 779 | return; | 842 | { |
| 843 | XTS_PARSER (parser)->visible_beg = BUF_BEGV_BYTE (buffer); | ||
| 844 | XTS_PARSER (parser)->visible_end = BUF_ZV_BYTE (buffer); | ||
| 845 | return; | ||
| 846 | } | ||
| 780 | 847 | ||
| 781 | struct buffer *buffer = XBUFFER (XTS_PARSER (parser)->buffer); | ||
| 782 | ptrdiff_t visible_beg = XTS_PARSER (parser)->visible_beg; | 848 | ptrdiff_t visible_beg = XTS_PARSER (parser)->visible_beg; |
| 783 | ptrdiff_t visible_end = XTS_PARSER (parser)->visible_end; | 849 | ptrdiff_t visible_end = XTS_PARSER (parser)->visible_end; |
| 784 | eassert (0 <= visible_beg); | 850 | eassert (0 <= visible_beg); |
| @@ -787,6 +853,12 @@ treesit_ensure_position_synced (Lisp_Object parser) | |||
| 787 | eassert (BUF_BEGV_BYTE (buffer) <= UINT32_MAX); | 853 | eassert (BUF_BEGV_BYTE (buffer) <= UINT32_MAX); |
| 788 | eassert (BUF_ZV_BYTE (buffer) <= UINT32_MAX); | 854 | eassert (BUF_ZV_BYTE (buffer) <= UINT32_MAX); |
| 789 | 855 | ||
| 856 | /* If buffer restriction changed and user requests for a node (hence | ||
| 857 | this function is called), we need to reparse. */ | ||
| 858 | if (visible_beg != BUF_BEGV_BYTE (buffer) | ||
| 859 | || visible_end != BUF_ZV_BYTE (buffer)) | ||
| 860 | XTS_PARSER (parser)->need_reparse = true; | ||
| 861 | |||
| 790 | /* Before we parse or set ranges, catch up with the narrowing | 862 | /* Before we parse or set ranges, catch up with the narrowing |
| 791 | situation. We change visible_beg and visible_end to match | 863 | situation. We change visible_beg and visible_end to match |
| 792 | BUF_BEGV_BYTE and BUF_ZV_BYTE, and inform tree-sitter of the | 864 | BUF_BEGV_BYTE and BUF_ZV_BYTE, and inform tree-sitter of the |
| @@ -832,6 +904,8 @@ treesit_ensure_position_synced (Lisp_Object parser) | |||
| 832 | } | 904 | } |
| 833 | eassert (0 <= visible_beg); | 905 | eassert (0 <= visible_beg); |
| 834 | eassert (visible_beg <= visible_end); | 906 | eassert (visible_beg <= visible_end); |
| 907 | eassert (visible_beg == BUF_BEGV_BYTE (buffer)); | ||
| 908 | eassert (visible_end == BUF_ZV_BYTE (buffer)); | ||
| 835 | 909 | ||
| 836 | XTS_PARSER (parser)->visible_beg = visible_beg; | 910 | XTS_PARSER (parser)->visible_beg = visible_beg; |
| 837 | XTS_PARSER (parser)->visible_end = visible_end; | 911 | XTS_PARSER (parser)->visible_end = visible_end; |
| @@ -875,16 +949,19 @@ treesit_call_after_change_functions (TSTree *old_tree, TSTree *new_tree, | |||
| 875 | static void | 949 | static void |
| 876 | treesit_ensure_parsed (Lisp_Object parser) | 950 | treesit_ensure_parsed (Lisp_Object parser) |
| 877 | { | 951 | { |
| 952 | struct buffer *buffer = XBUFFER (XTS_PARSER (parser)->buffer); | ||
| 953 | |||
| 954 | /* Before we parse, catch up with the narrowing situation. */ | ||
| 955 | treesit_check_buffer_size (buffer); | ||
| 956 | /* This function has to run before we check for need_reparse flag, | ||
| 957 | because it might set the flag to true. */ | ||
| 958 | treesit_sync_visible_region (parser); | ||
| 959 | |||
| 878 | if (!XTS_PARSER (parser)->need_reparse) | 960 | if (!XTS_PARSER (parser)->need_reparse) |
| 879 | return; | 961 | return; |
| 880 | TSParser *treesit_parser = XTS_PARSER (parser)->parser; | 962 | TSParser *treesit_parser = XTS_PARSER (parser)->parser; |
| 881 | TSTree *tree = XTS_PARSER (parser)->tree; | 963 | TSTree *tree = XTS_PARSER (parser)->tree; |
| 882 | TSInput input = XTS_PARSER (parser)->input; | 964 | TSInput input = XTS_PARSER (parser)->input; |
| 883 | struct buffer *buffer = XBUFFER (XTS_PARSER (parser)->buffer); | ||
| 884 | |||
| 885 | /* Before we parse, catch up with the narrowing situation. */ | ||
| 886 | treesit_check_buffer_size (buffer); | ||
| 887 | treesit_ensure_position_synced (parser); | ||
| 888 | 965 | ||
| 889 | TSTree *new_tree = ts_parser_parse (treesit_parser, tree, input); | 966 | TSTree *new_tree = ts_parser_parse (treesit_parser, tree, input); |
| 890 | /* This should be very rare (impossible, really): it only happens | 967 | /* This should be very rare (impossible, really): it only happens |
| @@ -983,8 +1060,8 @@ make_treesit_parser (Lisp_Object buffer, TSParser *parser, | |||
| 983 | TSInput input = {lisp_parser, treesit_read_buffer, TSInputEncodingUTF8}; | 1060 | TSInput input = {lisp_parser, treesit_read_buffer, TSInputEncodingUTF8}; |
| 984 | lisp_parser->input = input; | 1061 | lisp_parser->input = input; |
| 985 | lisp_parser->need_reparse = true; | 1062 | lisp_parser->need_reparse = true; |
| 986 | lisp_parser->visible_beg = BUF_BEGV (XBUFFER (buffer)); | 1063 | lisp_parser->visible_beg = BUF_BEGV_BYTE (XBUFFER (buffer)); |
| 987 | lisp_parser->visible_end = BUF_ZV (XBUFFER (buffer)); | 1064 | lisp_parser->visible_end = BUF_ZV_BYTE (XBUFFER (buffer)); |
| 988 | lisp_parser->timestamp = 0; | 1065 | lisp_parser->timestamp = 0; |
| 989 | lisp_parser->deleted = false; | 1066 | lisp_parser->deleted = false; |
| 990 | lisp_parser->has_range = false; | 1067 | lisp_parser->has_range = false; |
| @@ -1191,12 +1268,16 @@ DEFUN ("treesit-parser-create", | |||
| 1191 | 1, 3, 0, | 1268 | 1, 3, 0, |
| 1192 | doc: /* Create and return a parser in BUFFER for LANGUAGE. | 1269 | doc: /* Create and return a parser in BUFFER for LANGUAGE. |
| 1193 | 1270 | ||
| 1194 | The parser is automatically added to BUFFER's parser list, as | 1271 | The parser is automatically added to BUFFER's parser list, as returned |
| 1195 | returned by `treesit-parser-list'. | 1272 | by `treesit-parser-list'. LANGUAGE is a language symbol. If BUFFER |
| 1196 | LANGUAGE is a language symbol. If BUFFER is nil or omitted, it | 1273 | is nil or omitted, it defaults to the current buffer. If BUFFER |
| 1197 | defaults to the current buffer. If BUFFER already has a parser for | 1274 | already has a parser for LANGUAGE, return that parser, but if NO-REUSE |
| 1198 | LANGUAGE, return that parser, but if NO-REUSE is non-nil, always | 1275 | is non-nil, always create a new parser. |
| 1199 | create a new parser. */) | 1276 | |
| 1277 | If that buffer is an indirect buffer, its base buffer is used instead. | ||
| 1278 | That is, indirect buffers use their base buffer's parsers. Lisp | ||
| 1279 | programs should widen as necessary should they want to use a parser in | ||
| 1280 | an indirect buffer. */) | ||
| 1200 | (Lisp_Object language, Lisp_Object buffer, Lisp_Object no_reuse) | 1281 | (Lisp_Object language, Lisp_Object buffer, Lisp_Object no_reuse) |
| 1201 | { | 1282 | { |
| 1202 | treesit_initialize (); | 1283 | treesit_initialize (); |
| @@ -1210,16 +1291,21 @@ create a new parser. */) | |||
| 1210 | CHECK_BUFFER (buffer); | 1291 | CHECK_BUFFER (buffer); |
| 1211 | buf = XBUFFER (buffer); | 1292 | buf = XBUFFER (buffer); |
| 1212 | } | 1293 | } |
| 1294 | if (buf->base_buffer) | ||
| 1295 | buf = buf->base_buffer; | ||
| 1296 | |||
| 1213 | treesit_check_buffer_size (buf); | 1297 | treesit_check_buffer_size (buf); |
| 1214 | 1298 | ||
| 1215 | /* See if we can reuse a parser. */ | 1299 | /* See if we can reuse a parser. */ |
| 1216 | for (Lisp_Object tail = BVAR (buf, ts_parser_list); | 1300 | if (NILP (no_reuse)) |
| 1217 | NILP (no_reuse) && !NILP (tail); | ||
| 1218 | tail = XCDR (tail)) | ||
| 1219 | { | 1301 | { |
| 1220 | struct Lisp_TS_Parser *parser = XTS_PARSER (XCAR (tail)); | 1302 | Lisp_Object tail = BVAR (buf, ts_parser_list); |
| 1221 | if (EQ (parser->language_symbol, language)) | 1303 | FOR_EACH_TAIL (tail) |
| 1222 | return XCAR (tail); | 1304 | { |
| 1305 | struct Lisp_TS_Parser *parser = XTS_PARSER (XCAR (tail)); | ||
| 1306 | if (EQ (parser->language_symbol, language)) | ||
| 1307 | return XCAR (tail); | ||
| 1308 | } | ||
| 1223 | } | 1309 | } |
| 1224 | 1310 | ||
| 1225 | /* Load language. */ | 1311 | /* Load language. */ |
| @@ -1268,7 +1354,10 @@ DEFUN ("treesit-parser-list", | |||
| 1268 | Ftreesit_parser_list, Streesit_parser_list, | 1354 | Ftreesit_parser_list, Streesit_parser_list, |
| 1269 | 0, 1, 0, | 1355 | 0, 1, 0, |
| 1270 | doc: /* Return BUFFER's parser list. | 1356 | doc: /* Return BUFFER's parser list. |
| 1271 | BUFFER defaults to the current buffer. */) | 1357 | |
| 1358 | BUFFER defaults to the current buffer. If that buffer is an indirect | ||
| 1359 | buffer, its base buffer is used instead. That is, indirect buffers | ||
| 1360 | use their base buffer's parsers. */) | ||
| 1272 | (Lisp_Object buffer) | 1361 | (Lisp_Object buffer) |
| 1273 | { | 1362 | { |
| 1274 | struct buffer *buf; | 1363 | struct buffer *buf; |
| @@ -1279,6 +1368,9 @@ BUFFER defaults to the current buffer. */) | |||
| 1279 | CHECK_BUFFER (buffer); | 1368 | CHECK_BUFFER (buffer); |
| 1280 | buf = XBUFFER (buffer); | 1369 | buf = XBUFFER (buffer); |
| 1281 | } | 1370 | } |
| 1371 | if (buf->base_buffer) | ||
| 1372 | buf = buf->base_buffer; | ||
| 1373 | |||
| 1282 | /* Return a fresh list so messing with that list doesn't affect our | 1374 | /* Return a fresh list so messing with that list doesn't affect our |
| 1283 | internal data. */ | 1375 | internal data. */ |
| 1284 | Lisp_Object return_list = Qnil; | 1376 | Lisp_Object return_list = Qnil; |
| @@ -1365,9 +1457,10 @@ treesit_check_range_argument (Lisp_Object ranges) | |||
| 1365 | CHECK_LIST_END (tail, ranges); | 1457 | CHECK_LIST_END (tail, ranges); |
| 1366 | } | 1458 | } |
| 1367 | 1459 | ||
| 1368 | /* Generate a list of ranges in Lisp from RANGES. This function | 1460 | /* Generate a list of ranges in Lisp from RANGES. Assumes tree-sitter |
| 1369 | doesn't take ownership of RANGES. BUFFER is used to convert | 1461 | tree and the buffer has the same visible region (wrt narrowing). |
| 1370 | between tree-sitter buffer offset and buffer position. */ | 1462 | This function doesn't take ownership of RANGES. BUFFER is used to |
| 1463 | convert between tree-sitter buffer offset and buffer position. */ | ||
| 1371 | static Lisp_Object | 1464 | static Lisp_Object |
| 1372 | treesit_make_ranges (const TSRange *ranges, uint32_t len, | 1465 | treesit_make_ranges (const TSRange *ranges, uint32_t len, |
| 1373 | struct buffer *buffer) | 1466 | struct buffer *buffer) |
| @@ -1412,7 +1505,7 @@ buffer. */) | |||
| 1412 | treesit_initialize (); | 1505 | treesit_initialize (); |
| 1413 | /* Before we parse, catch up with narrowing/widening. */ | 1506 | /* Before we parse, catch up with narrowing/widening. */ |
| 1414 | treesit_check_buffer_size (XBUFFER (XTS_PARSER (parser)->buffer)); | 1507 | treesit_check_buffer_size (XBUFFER (XTS_PARSER (parser)->buffer)); |
| 1415 | treesit_ensure_position_synced (parser); | 1508 | treesit_sync_visible_region (parser); |
| 1416 | 1509 | ||
| 1417 | bool success; | 1510 | bool success; |
| 1418 | if (NILP (ranges)) | 1511 | if (NILP (ranges)) |
| @@ -1439,9 +1532,9 @@ buffer. */) | |||
| 1439 | for (int idx = 0; !NILP (ranges); idx++, ranges = XCDR (ranges)) | 1532 | for (int idx = 0; !NILP (ranges); idx++, ranges = XCDR (ranges)) |
| 1440 | { | 1533 | { |
| 1441 | Lisp_Object range = XCAR (ranges); | 1534 | Lisp_Object range = XCAR (ranges); |
| 1442 | EMACS_INT beg_byte = buf_charpos_to_bytepos (buffer, | 1535 | ptrdiff_t beg_byte = buf_charpos_to_bytepos (buffer, |
| 1443 | XFIXNUM (XCAR (range))); | 1536 | XFIXNUM (XCAR (range))); |
| 1444 | EMACS_INT end_byte = buf_charpos_to_bytepos (buffer, | 1537 | ptrdiff_t end_byte = buf_charpos_to_bytepos (buffer, |
| 1445 | XFIXNUM (XCDR (range))); | 1538 | XFIXNUM (XCDR (range))); |
| 1446 | /* Shouldn't violate assertion since we just checked for | 1539 | /* Shouldn't violate assertion since we just checked for |
| 1447 | buffer size at the beginning of this function. */ | 1540 | buffer size at the beginning of this function. */ |
| @@ -1476,8 +1569,8 @@ DEFUN ("treesit-parser-included-ranges", | |||
| 1476 | Streesit_parser_included_ranges, | 1569 | Streesit_parser_included_ranges, |
| 1477 | 1, 1, 0, | 1570 | 1, 1, 0, |
| 1478 | doc: /* Return the ranges set for PARSER. | 1571 | doc: /* Return the ranges set for PARSER. |
| 1479 | See `treesit-parser-set-ranges'. If no ranges are set for PARSER, | 1572 | If no ranges are set for PARSER, return nil. |
| 1480 | return nil. */) | 1573 | See also `treesit-parser-set-included-ranges'. */) |
| 1481 | (Lisp_Object parser) | 1574 | (Lisp_Object parser) |
| 1482 | { | 1575 | { |
| 1483 | treesit_check_parser (parser); | 1576 | treesit_check_parser (parser); |
| @@ -1498,7 +1591,7 @@ return nil. */) | |||
| 1498 | /* Our return value depends on the buffer state (BUF_BEGV_BYTE, | 1591 | /* Our return value depends on the buffer state (BUF_BEGV_BYTE, |
| 1499 | etc), so we need to sync up. */ | 1592 | etc), so we need to sync up. */ |
| 1500 | treesit_check_buffer_size (XBUFFER (XTS_PARSER (parser)->buffer)); | 1593 | treesit_check_buffer_size (XBUFFER (XTS_PARSER (parser)->buffer)); |
| 1501 | treesit_ensure_position_synced (parser); | 1594 | treesit_sync_visible_region (parser); |
| 1502 | 1595 | ||
| 1503 | struct buffer *buffer = XBUFFER (XTS_PARSER (parser)->buffer); | 1596 | struct buffer *buffer = XBUFFER (XTS_PARSER (parser)->buffer); |
| 1504 | return treesit_make_ranges (ranges, len, buffer); | 1597 | return treesit_make_ranges (ranges, len, buffer); |
| @@ -1580,6 +1673,17 @@ treesit_check_node (Lisp_Object obj) | |||
| 1580 | xsignal1 (Qtreesit_node_outdated, obj); | 1673 | xsignal1 (Qtreesit_node_outdated, obj); |
| 1581 | } | 1674 | } |
| 1582 | 1675 | ||
| 1676 | /* Checks that OBJ is a positive integer and it is within the visible | ||
| 1677 | portion of BUF. */ | ||
| 1678 | static void | ||
| 1679 | treesit_check_position (Lisp_Object obj, struct buffer *buf) | ||
| 1680 | { | ||
| 1681 | treesit_check_positive_integer (obj); | ||
| 1682 | ptrdiff_t pos = XFIXNUM (obj); | ||
| 1683 | if (pos < BUF_BEGV (buf) || pos > BUF_ZV (buf)) | ||
| 1684 | xsignal1 (Qargs_out_of_range, obj); | ||
| 1685 | } | ||
| 1686 | |||
| 1583 | bool | 1687 | bool |
| 1584 | treesit_node_uptodate_p (Lisp_Object obj) | 1688 | treesit_node_uptodate_p (Lisp_Object obj) |
| 1585 | { | 1689 | { |
| @@ -1658,6 +1762,8 @@ If NODE is nil, return nil. */) | |||
| 1658 | return build_string (string); | 1762 | return build_string (string); |
| 1659 | } | 1763 | } |
| 1660 | 1764 | ||
| 1765 | static TSTreeCursor treesit_cursor_helper (TSNode, Lisp_Object); | ||
| 1766 | |||
| 1661 | DEFUN ("treesit-node-parent", | 1767 | DEFUN ("treesit-node-parent", |
| 1662 | Ftreesit_node_parent, Streesit_node_parent, 1, 1, 0, | 1768 | Ftreesit_node_parent, Streesit_node_parent, 1, 1, 0, |
| 1663 | doc: /* Return the immediate parent of NODE. | 1769 | doc: /* Return the immediate parent of NODE. |
| @@ -1668,13 +1774,18 @@ Return nil if NODE has no parent. If NODE is nil, return nil. */) | |||
| 1668 | treesit_check_node (node); | 1774 | treesit_check_node (node); |
| 1669 | treesit_initialize (); | 1775 | treesit_initialize (); |
| 1670 | 1776 | ||
| 1671 | TSNode treesit_node = XTS_NODE (node)->node; | 1777 | Lisp_Object return_value = Qnil; |
| 1672 | TSNode parent = ts_node_parent (treesit_node); | ||
| 1673 | 1778 | ||
| 1674 | if (ts_node_is_null (parent)) | 1779 | TSNode treesit_node = XTS_NODE (node)->node; |
| 1675 | return Qnil; | 1780 | Lisp_Object parser = XTS_NODE (node)->parser; |
| 1676 | 1781 | TSTreeCursor cursor = treesit_cursor_helper (treesit_node, parser); | |
| 1677 | return make_treesit_node (XTS_NODE (node)->parser, parent); | 1782 | if (ts_tree_cursor_goto_parent (&cursor)) |
| 1783 | { | ||
| 1784 | TSNode parent = ts_tree_cursor_current_node (&cursor); | ||
| 1785 | return_value = make_treesit_node (parser, parent); | ||
| 1786 | } | ||
| 1787 | ts_tree_cursor_delete (&cursor); | ||
| 1788 | return return_value; | ||
| 1678 | } | 1789 | } |
| 1679 | 1790 | ||
| 1680 | DEFUN ("treesit-node-child", | 1791 | DEFUN ("treesit-node-child", |
| @@ -1928,14 +2039,12 @@ Note that this function returns an immediate child, not the smallest | |||
| 1928 | if (NILP (node)) | 2039 | if (NILP (node)) |
| 1929 | return Qnil; | 2040 | return Qnil; |
| 1930 | treesit_check_node (node); | 2041 | treesit_check_node (node); |
| 1931 | treesit_check_positive_integer (pos); | ||
| 1932 | 2042 | ||
| 1933 | struct buffer *buf = XBUFFER (XTS_PARSER (XTS_NODE (node)->parser)->buffer); | 2043 | struct buffer *buf = XBUFFER (XTS_PARSER (XTS_NODE (node)->parser)->buffer); |
| 1934 | ptrdiff_t visible_beg = XTS_PARSER (XTS_NODE (node)->parser)->visible_beg; | 2044 | ptrdiff_t visible_beg = XTS_PARSER (XTS_NODE (node)->parser)->visible_beg; |
| 1935 | ptrdiff_t byte_pos = buf_charpos_to_bytepos (buf, XFIXNUM (pos)); | 2045 | ptrdiff_t byte_pos = buf_charpos_to_bytepos (buf, XFIXNUM (pos)); |
| 1936 | 2046 | ||
| 1937 | if (byte_pos < BUF_BEGV_BYTE (buf) || byte_pos > BUF_ZV_BYTE (buf)) | 2047 | treesit_check_position (pos, buf); |
| 1938 | xsignal1 (Qargs_out_of_range, pos); | ||
| 1939 | 2048 | ||
| 1940 | treesit_initialize (); | 2049 | treesit_initialize (); |
| 1941 | 2050 | ||
| @@ -1966,19 +2075,14 @@ If NODE is nil, return nil. */) | |||
| 1966 | { | 2075 | { |
| 1967 | if (NILP (node)) return Qnil; | 2076 | if (NILP (node)) return Qnil; |
| 1968 | treesit_check_node (node); | 2077 | treesit_check_node (node); |
| 1969 | CHECK_INTEGER (beg); | ||
| 1970 | CHECK_INTEGER (end); | ||
| 1971 | 2078 | ||
| 1972 | struct buffer *buf = XBUFFER (XTS_PARSER (XTS_NODE (node)->parser)->buffer); | 2079 | struct buffer *buf = XBUFFER (XTS_PARSER (XTS_NODE (node)->parser)->buffer); |
| 1973 | ptrdiff_t visible_beg = XTS_PARSER (XTS_NODE (node)->parser)->visible_beg; | 2080 | ptrdiff_t visible_beg = XTS_PARSER (XTS_NODE (node)->parser)->visible_beg; |
| 1974 | ptrdiff_t byte_beg = buf_charpos_to_bytepos (buf, XFIXNUM (beg)); | 2081 | ptrdiff_t byte_beg = buf_charpos_to_bytepos (buf, XFIXNUM (beg)); |
| 1975 | ptrdiff_t byte_end = buf_charpos_to_bytepos (buf, XFIXNUM (end)); | 2082 | ptrdiff_t byte_end = buf_charpos_to_bytepos (buf, XFIXNUM (end)); |
| 1976 | 2083 | ||
| 1977 | /* Checks for BUFFER_BEG <= BEG <= END <= BUFFER_END. */ | 2084 | treesit_check_position (beg, buf); |
| 1978 | if (!(BUF_BEGV_BYTE (buf) <= byte_beg | 2085 | treesit_check_position (end, buf); |
| 1979 | && byte_beg <= byte_end | ||
| 1980 | && byte_end <= BUF_ZV_BYTE (buf))) | ||
| 1981 | xsignal2 (Qargs_out_of_range, beg, end); | ||
| 1982 | 2086 | ||
| 1983 | treesit_initialize (); | 2087 | treesit_initialize (); |
| 1984 | 2088 | ||
| @@ -2229,8 +2333,6 @@ treesit_predicate_match (Lisp_Object args, struct capture_range captures) | |||
| 2229 | 2333 | ||
| 2230 | Lisp_Object regexp = XCAR (args); | 2334 | Lisp_Object regexp = XCAR (args); |
| 2231 | Lisp_Object capture_name = XCAR (XCDR (args)); | 2335 | Lisp_Object capture_name = XCAR (XCDR (args)); |
| 2232 | Lisp_Object text = treesit_predicate_capture_name_to_text (capture_name, | ||
| 2233 | captures); | ||
| 2234 | 2336 | ||
| 2235 | /* It's probably common to get the argument order backwards. Catch | 2337 | /* It's probably common to get the argument order backwards. Catch |
| 2236 | this mistake early and show helpful explanation, because Emacs | 2338 | this mistake early and show helpful explanation, because Emacs |
| @@ -2245,6 +2347,9 @@ treesit_predicate_match (Lisp_Object args, struct capture_range captures) | |||
| 2245 | build_pure_c_string ("The second argument to `match' should " | 2347 | build_pure_c_string ("The second argument to `match' should " |
| 2246 | "be a capture name, not a string")); | 2348 | "be a capture name, not a string")); |
| 2247 | 2349 | ||
| 2350 | Lisp_Object text = treesit_predicate_capture_name_to_text (capture_name, | ||
| 2351 | captures); | ||
| 2352 | |||
| 2248 | if (fast_string_match (regexp, text) >= 0) | 2353 | if (fast_string_match (regexp, text) >= 0) |
| 2249 | return true; | 2354 | return true; |
| 2250 | else | 2355 | else |
| @@ -2363,21 +2468,24 @@ the query. */) | |||
| 2363 | (Lisp_Object node, Lisp_Object query, | 2468 | (Lisp_Object node, Lisp_Object query, |
| 2364 | Lisp_Object beg, Lisp_Object end, Lisp_Object node_only) | 2469 | Lisp_Object beg, Lisp_Object end, Lisp_Object node_only) |
| 2365 | { | 2470 | { |
| 2366 | if (!NILP (beg)) | ||
| 2367 | CHECK_INTEGER (beg); | ||
| 2368 | if (!NILP (end)) | ||
| 2369 | CHECK_INTEGER (end); | ||
| 2370 | |||
| 2371 | if (!(TS_COMPILED_QUERY_P (query) | 2471 | if (!(TS_COMPILED_QUERY_P (query) |
| 2372 | || CONSP (query) || STRINGP (query))) | 2472 | || CONSP (query) || STRINGP (query))) |
| 2373 | wrong_type_argument (Qtreesit_query_p, query); | 2473 | wrong_type_argument (Qtreesit_query_p, query); |
| 2374 | 2474 | ||
| 2475 | treesit_initialize (); | ||
| 2476 | |||
| 2375 | /* Resolve NODE into an actual node. */ | 2477 | /* Resolve NODE into an actual node. */ |
| 2376 | Lisp_Object lisp_node; | 2478 | Lisp_Object lisp_node; |
| 2377 | if (TS_NODEP (node)) | 2479 | if (TS_NODEP (node)) |
| 2378 | lisp_node = node; | 2480 | { |
| 2481 | treesit_check_node (node); /* Check if up-to-date. */ | ||
| 2482 | lisp_node = node; | ||
| 2483 | } | ||
| 2379 | else if (TS_PARSERP (node)) | 2484 | else if (TS_PARSERP (node)) |
| 2380 | lisp_node = Ftreesit_parser_root_node (node); | 2485 | { |
| 2486 | treesit_check_parser (node); /* Check if deleted. */ | ||
| 2487 | lisp_node = Ftreesit_parser_root_node (node); | ||
| 2488 | } | ||
| 2381 | else if (SYMBOLP (node)) | 2489 | else if (SYMBOLP (node)) |
| 2382 | { | 2490 | { |
| 2383 | Lisp_Object parser | 2491 | Lisp_Object parser |
| @@ -2389,8 +2497,6 @@ the query. */) | |||
| 2389 | list4 (Qor, Qtreesit_node_p, Qtreesit_parser_p, Qsymbolp), | 2497 | list4 (Qor, Qtreesit_node_p, Qtreesit_parser_p, Qsymbolp), |
| 2390 | node); | 2498 | node); |
| 2391 | 2499 | ||
| 2392 | treesit_initialize (); | ||
| 2393 | |||
| 2394 | /* Extract C values from Lisp objects. */ | 2500 | /* Extract C values from Lisp objects. */ |
| 2395 | TSNode treesit_node | 2501 | TSNode treesit_node |
| 2396 | = XTS_NODE (lisp_node)->node; | 2502 | = XTS_NODE (lisp_node)->node; |
| @@ -2401,6 +2507,13 @@ the query. */) | |||
| 2401 | const TSLanguage *lang | 2507 | const TSLanguage *lang |
| 2402 | = ts_parser_language (XTS_PARSER (lisp_parser)->parser); | 2508 | = ts_parser_language (XTS_PARSER (lisp_parser)->parser); |
| 2403 | 2509 | ||
| 2510 | /* Check BEG and END. */ | ||
| 2511 | struct buffer *buf = XBUFFER (XTS_PARSER (lisp_parser)->buffer); | ||
| 2512 | if (!NILP (beg)) | ||
| 2513 | treesit_check_position (beg, buf); | ||
| 2514 | if (!NILP (end)) | ||
| 2515 | treesit_check_position (end, buf); | ||
| 2516 | |||
| 2404 | /* Initialize query objects. At the end of this block, we should | 2517 | /* Initialize query objects. At the end of this block, we should |
| 2405 | have a working TSQuery and a TSQueryCursor. */ | 2518 | have a working TSQuery and a TSQueryCursor. */ |
| 2406 | TSQuery *treesit_query; | 2519 | TSQuery *treesit_query; |
| @@ -2444,14 +2557,15 @@ the query. */) | |||
| 2444 | /* Set query range. */ | 2557 | /* Set query range. */ |
| 2445 | if (!NILP (beg) && !NILP (end)) | 2558 | if (!NILP (beg) && !NILP (end)) |
| 2446 | { | 2559 | { |
| 2447 | EMACS_INT beg_byte = XFIXNUM (beg); | 2560 | ptrdiff_t beg_byte = CHAR_TO_BYTE (XFIXNUM (beg)); |
| 2448 | EMACS_INT end_byte = XFIXNUM (end); | 2561 | ptrdiff_t end_byte = CHAR_TO_BYTE (XFIXNUM (end)); |
| 2449 | /* We never let tree-sitter run on buffers too large, so these | 2562 | /* We never let tree-sitter run on buffers too large, so these |
| 2450 | assertion should never hit. */ | 2563 | assertion should never hit. */ |
| 2451 | eassert (beg_byte - visible_beg <= UINT32_MAX); | 2564 | eassert (beg_byte - visible_beg <= UINT32_MAX); |
| 2452 | eassert (end_byte - visible_beg <= UINT32_MAX); | 2565 | eassert (end_byte - visible_beg <= UINT32_MAX); |
| 2453 | ts_query_cursor_set_byte_range (cursor, (uint32_t) beg_byte - visible_beg, | 2566 | ts_query_cursor_set_byte_range (cursor, |
| 2454 | (uint32_t) end_byte - visible_beg); | 2567 | (uint32_t) (beg_byte - visible_beg), |
| 2568 | (uint32_t) (end_byte - visible_beg)); | ||
| 2455 | } | 2569 | } |
| 2456 | 2570 | ||
| 2457 | /* Execute query. */ | 2571 | /* Execute query. */ |
| @@ -2517,62 +2631,210 @@ the query. */) | |||
| 2517 | 2631 | ||
| 2518 | /*** Navigation */ | 2632 | /*** Navigation */ |
| 2519 | 2633 | ||
| 2520 | /* Return the next/previous named/unnamed sibling of NODE. FORWARD | 2634 | static inline void |
| 2521 | controls the direction and NAMED controls the nameness. */ | 2635 | treesit_assume_true (bool val) |
| 2522 | static TSNode | 2636 | { |
| 2523 | treesit_traverse_sibling_helper (TSNode node, bool forward, bool named) | 2637 | eassert (val == true); |
| 2638 | } | ||
| 2639 | |||
| 2640 | /* Create a TSTreeCursor pointing at NODE. PARSER is the lisp parser | ||
| 2641 | that produced NODE. | ||
| 2642 | |||
| 2643 | The reason we need this instead of simply using ts_tree_cursor_new | ||
| 2644 | is that we have to create the cursor on the root node and traverse | ||
| 2645 | down to NODE, in order to record the correct stack of parent nodes. | ||
| 2646 | Otherwise going to sibling or parent of NODE wouldn't work. | ||
| 2647 | |||
| 2648 | (Wow perfect filling.) */ | ||
| 2649 | static TSTreeCursor | ||
| 2650 | treesit_cursor_helper (TSNode node, Lisp_Object parser) | ||
| 2651 | { | ||
| 2652 | uint32_t end_pos = ts_node_end_byte (node); | ||
| 2653 | TSNode root = ts_tree_root_node (XTS_PARSER (parser)->tree); | ||
| 2654 | TSTreeCursor cursor = ts_tree_cursor_new (root); | ||
| 2655 | TSNode cursor_node = ts_tree_cursor_current_node (&cursor); | ||
| 2656 | /* This is like treesit-node-at. We go down from the root node, | ||
| 2657 | either to first child or next sibling, repeatedly, and finally | ||
| 2658 | arrive at NODE. */ | ||
| 2659 | while (!ts_node_eq (node, cursor_node)) | ||
| 2660 | { | ||
| 2661 | treesit_assume_true (ts_tree_cursor_goto_first_child (&cursor)); | ||
| 2662 | cursor_node = ts_tree_cursor_current_node (&cursor); | ||
| 2663 | /* ts_tree_cursor_goto_first_child_for_byte is not reliable, so | ||
| 2664 | we just go through each sibling. */ | ||
| 2665 | while (ts_node_is_missing (cursor_node) | ||
| 2666 | || ts_node_end_byte (cursor_node) < end_pos) | ||
| 2667 | { | ||
| 2668 | /* A "missing" node has zero width, so it's possible that | ||
| 2669 | its end = NODE.end but it's not NODE, so we skip them. | ||
| 2670 | But we need to make sure this missing node is not the | ||
| 2671 | node we are looking for before skipping it. */ | ||
| 2672 | if (ts_node_is_missing (cursor_node) | ||
| 2673 | && ts_node_eq (node, cursor_node)) | ||
| 2674 | return cursor; | ||
| 2675 | treesit_assume_true (ts_tree_cursor_goto_next_sibling (&cursor)); | ||
| 2676 | cursor_node = ts_tree_cursor_current_node (&cursor); | ||
| 2677 | } | ||
| 2678 | /* Right now CURSOR.end >= NODE.end. But what if CURSOR.end = | ||
| 2679 | NODE.end, and there are missing nodes after CURSOR, and the | ||
| 2680 | missing node after CURSOR is the NODE we are looking for?? | ||
| 2681 | Well, create a probe and look ahead. (This is tested by | ||
| 2682 | treesit-cursor-helper-with-missing-node.) */ | ||
| 2683 | TSTreeCursor probe = ts_tree_cursor_copy (&cursor); | ||
| 2684 | TSNode probe_node; | ||
| 2685 | while (ts_tree_cursor_goto_next_sibling (&probe)) | ||
| 2686 | { | ||
| 2687 | probe_node = ts_tree_cursor_current_node (&probe); | ||
| 2688 | if (!ts_node_is_missing (probe_node)) | ||
| 2689 | break; | ||
| 2690 | if (ts_node_eq (probe_node, node)) | ||
| 2691 | { | ||
| 2692 | ts_tree_cursor_delete (&cursor); | ||
| 2693 | return probe; | ||
| 2694 | } | ||
| 2695 | } | ||
| 2696 | ts_tree_cursor_delete (&probe); | ||
| 2697 | } | ||
| 2698 | return cursor; | ||
| 2699 | } | ||
| 2700 | |||
| 2701 | /* Move CURSOR to the next/previous sibling. FORWARD controls the | ||
| 2702 | direction. NAMED controls the namedness. If there is a valid | ||
| 2703 | sibling, move CURSOR to it and return true, otherwise return false. | ||
| 2704 | When false is returned, CURSOR points to a sibling node of the node | ||
| 2705 | we started at, but exactly which is undefined. */ | ||
| 2706 | static bool | ||
| 2707 | treesit_traverse_sibling_helper (TSTreeCursor *cursor, | ||
| 2708 | bool forward, bool named) | ||
| 2524 | { | 2709 | { |
| 2525 | if (forward) | 2710 | if (forward) |
| 2526 | { | 2711 | { |
| 2527 | if (named) | 2712 | if (!named) |
| 2528 | return ts_node_next_named_sibling (node); | 2713 | return ts_tree_cursor_goto_next_sibling (cursor); |
| 2529 | else | 2714 | /* Else named... */ |
| 2530 | return ts_node_next_sibling (node); | 2715 | while (ts_tree_cursor_goto_next_sibling (cursor)) |
| 2716 | { | ||
| 2717 | if (ts_node_is_named (ts_tree_cursor_current_node (cursor))) | ||
| 2718 | return true; | ||
| 2719 | } | ||
| 2720 | return false; | ||
| 2531 | } | 2721 | } |
| 2532 | else | 2722 | else /* Backward. */ |
| 2533 | { | 2723 | { |
| 2534 | if (named) | 2724 | /* Go to first child and go through each sibling, until we find |
| 2535 | return ts_node_prev_named_sibling (node); | 2725 | the one just before the starting node. */ |
| 2536 | else | 2726 | TSNode start = ts_tree_cursor_current_node (cursor); |
| 2537 | return ts_node_prev_sibling (node); | 2727 | if (!ts_tree_cursor_goto_parent (cursor)) |
| 2728 | return false; | ||
| 2729 | treesit_assume_true (ts_tree_cursor_goto_first_child (cursor)); | ||
| 2730 | |||
| 2731 | /* Now CURSOR is at the first child. If we started at the first | ||
| 2732 | child, then there is no further siblings. */ | ||
| 2733 | TSNode first_child = ts_tree_cursor_current_node (cursor); | ||
| 2734 | if (ts_node_eq (first_child, start)) | ||
| 2735 | return false; | ||
| 2736 | |||
| 2737 | /* PROBE is always DELTA siblings ahead of CURSOR. */ | ||
| 2738 | TSTreeCursor probe = ts_tree_cursor_copy (cursor); | ||
| 2739 | /* This is position of PROBE minus position of CURSOR. */ | ||
| 2740 | ptrdiff_t delta = 0; | ||
| 2741 | TSNode probe_node; | ||
| 2742 | TSNode cursor_node; | ||
| 2743 | while (ts_tree_cursor_goto_next_sibling (&probe)) | ||
| 2744 | { | ||
| 2745 | /* Move PROBE forward, if it equals to the starting node, | ||
| 2746 | CURSOR points to the node we want (prev valid sibling of | ||
| 2747 | the starting node). */ | ||
| 2748 | delta++; | ||
| 2749 | probe_node = ts_tree_cursor_current_node (&probe); | ||
| 2750 | |||
| 2751 | /* PROBE matched, depending on NAMED, return true/false. */ | ||
| 2752 | if (ts_node_eq (probe_node, start)) | ||
| 2753 | { | ||
| 2754 | ts_tree_cursor_delete (&probe); | ||
| 2755 | cursor_node = ts_tree_cursor_current_node (cursor); | ||
| 2756 | ts_tree_cursor_delete (&probe); | ||
| 2757 | return (!named || (named && ts_node_is_named (cursor_node))); | ||
| 2758 | } | ||
| 2759 | |||
| 2760 | /* PROBE didn't match, move CURSOR forward to PROBE's | ||
| 2761 | position, but if we are looking for named nodes, only | ||
| 2762 | move CURSOR to PROBE if PROBE is at a named node. */ | ||
| 2763 | if (!named || (named && ts_node_is_named (probe_node))) | ||
| 2764 | for (; delta > 0; delta--) | ||
| 2765 | treesit_assume_true (ts_tree_cursor_goto_next_sibling (cursor)); | ||
| 2766 | } | ||
| 2767 | ts_tree_cursor_delete (&probe); | ||
| 2768 | return false; | ||
| 2538 | } | 2769 | } |
| 2539 | } | 2770 | } |
| 2540 | 2771 | ||
| 2541 | /* Return the first/last named/unnamed child of NODE. FORWARD controls | 2772 | /* Move CURSOR to the first/last child. FORWARD controls the |
| 2542 | the direction and NAMED controls the nameness. */ | 2773 | direction. NAMED controls the namedness. If there is a valid |
| 2543 | static TSNode | 2774 | child, move CURSOR to it and return true, otherwise don't move |
| 2544 | treesit_traverse_child_helper (TSNode node, bool forward, bool named) | 2775 | CURSOR and return false. */ |
| 2776 | static bool | ||
| 2777 | treesit_traverse_child_helper (TSTreeCursor *cursor, | ||
| 2778 | bool forward, bool named) | ||
| 2545 | { | 2779 | { |
| 2546 | if (forward) | 2780 | if (forward) |
| 2547 | { | 2781 | { |
| 2548 | if (named) | 2782 | if (!named) |
| 2549 | return ts_node_named_child (node, 0); | 2783 | return ts_tree_cursor_goto_first_child (cursor); |
| 2550 | else | 2784 | else |
| 2551 | return ts_node_child (node, 0); | ||
| 2552 | } | ||
| 2553 | else | ||
| 2554 | { | ||
| 2555 | if (named) | ||
| 2556 | { | 2785 | { |
| 2557 | uint32_t count = ts_node_named_child_count (node); | 2786 | if (!ts_tree_cursor_goto_first_child (cursor)) |
| 2558 | uint32_t idx = count == 0 ? 0 : count - 1; | 2787 | return false; |
| 2559 | return ts_node_named_child (node, idx); | 2788 | /* After this point, if you return false, make sure to go |
| 2789 | back to parent. */ | ||
| 2790 | TSNode first_child = ts_tree_cursor_current_node (cursor); | ||
| 2791 | if (ts_node_is_named (first_child)) | ||
| 2792 | return true; | ||
| 2793 | |||
| 2794 | if (treesit_traverse_sibling_helper (cursor, true, true)) | ||
| 2795 | return true; | ||
| 2796 | else | ||
| 2797 | { | ||
| 2798 | treesit_assume_true (ts_tree_cursor_goto_parent (cursor)); | ||
| 2799 | return false; | ||
| 2800 | } | ||
| 2560 | } | 2801 | } |
| 2802 | } | ||
| 2803 | else /* Backward. */ | ||
| 2804 | { | ||
| 2805 | if (!ts_tree_cursor_goto_first_child (cursor)) | ||
| 2806 | return false; | ||
| 2807 | /* After this point, if you return false, make sure to go | ||
| 2808 | back to parent. */ | ||
| 2809 | |||
| 2810 | /* First go to the last child. */ | ||
| 2811 | while (ts_tree_cursor_goto_next_sibling (cursor)); | ||
| 2812 | |||
| 2813 | if (!named) | ||
| 2814 | return true; | ||
| 2815 | /* Else named... */ | ||
| 2816 | if (treesit_traverse_sibling_helper(cursor, false, true)) | ||
| 2817 | return true; | ||
| 2561 | else | 2818 | else |
| 2562 | { | 2819 | { |
| 2563 | uint32_t count = ts_node_child_count (node); | 2820 | treesit_assume_true (ts_tree_cursor_goto_parent (cursor)); |
| 2564 | uint32_t idx = count == 0 ? 0 : count - 1; | 2821 | return false; |
| 2565 | return ts_node_child (node, idx); | ||
| 2566 | } | 2822 | } |
| 2567 | } | 2823 | } |
| 2568 | } | 2824 | } |
| 2569 | 2825 | ||
| 2570 | /* Return true if NODE matches PRED. PRED can be a string or a | 2826 | /* Return true if the node at CURSOR matches PRED. PRED can be a |
| 2571 | function. This function doesn't check for PRED's type. */ | 2827 | string or a function. This function assumes PRED is either a |
| 2828 | string or a function. If NAMED is true, also check that the node | ||
| 2829 | is named. */ | ||
| 2572 | static bool | 2830 | static bool |
| 2573 | treesit_traverse_match_predicate (TSNode node, Lisp_Object pred, | 2831 | treesit_traverse_match_predicate (TSTreeCursor *cursor, Lisp_Object pred, |
| 2574 | Lisp_Object parser) | 2832 | Lisp_Object parser, bool named) |
| 2575 | { | 2833 | { |
| 2834 | TSNode node = ts_tree_cursor_current_node (cursor); | ||
| 2835 | if (named && !ts_node_is_named (node)) | ||
| 2836 | return false; | ||
| 2837 | |||
| 2576 | if (STRINGP (pred)) | 2838 | if (STRINGP (pred)) |
| 2577 | { | 2839 | { |
| 2578 | const char *type = ts_node_type (node); | 2840 | const char *type = ts_node_type (node); |
| @@ -2583,74 +2845,60 @@ treesit_traverse_match_predicate (TSNode node, Lisp_Object pred, | |||
| 2583 | Lisp_Object lisp_node = make_treesit_node (parser, node); | 2845 | Lisp_Object lisp_node = make_treesit_node (parser, node); |
| 2584 | return !NILP (CALLN (Ffuncall, pred, lisp_node)); | 2846 | return !NILP (CALLN (Ffuncall, pred, lisp_node)); |
| 2585 | } | 2847 | } |
| 2586 | |||
| 2587 | } | 2848 | } |
| 2588 | 2849 | ||
| 2589 | /* Traverse the parse tree starting from ROOT (but ROOT is not | 2850 | /* Traverse the parse tree starting from CURSOR. PRED can be a |
| 2590 | matches against PRED). PRED can be a function (takes a node and | 2851 | function (takes a node and returns nil/non-nil), or a string |
| 2591 | returns nil/non-nil),or a string (treated as regexp matching the | 2852 | (treated as regexp matching the node's type, must be all single |
| 2592 | node's type, ignores case, must be all single byte characters). If | 2853 | byte characters). If the node satisfies PRED, leave CURSOR on that |
| 2593 | the node satisfies PRED , terminate, set ROOT to that node, and | 2854 | node and return true. If no node satisfies PRED, move CURSOR back |
| 2594 | return true. If no node satisfies PRED, return FALSE. PARSER is | 2855 | to starting position and return false. |
| 2595 | the parser of ROOT. | ||
| 2596 | 2856 | ||
| 2597 | LIMIT is the number of levels we descend in the tree. If NO_LIMIT | 2857 | LIMIT is the number of levels we descend in the tree. FORWARD |
| 2598 | is true, LIMIT is ignored. FORWARD controls the direction in which | 2858 | controls the direction in which we traverse the tree, true means |
| 2599 | we traverse the tree, true means forward, false backward. If NAMED | 2859 | forward, false backward. If SKIP_ROOT is true, don't match ROOT. |
| 2600 | is true, only traverse named nodes, if false, all nodes. If | 2860 | */ |
| 2601 | SKIP_ROOT is true, don't match ROOT. */ | ||
| 2602 | static bool | 2861 | static bool |
| 2603 | treesit_search_dfs (TSNode *root, Lisp_Object pred, Lisp_Object parser, | 2862 | treesit_search_dfs (TSTreeCursor *cursor, |
| 2604 | bool named, bool forward, ptrdiff_t limit, bool no_limit, | 2863 | Lisp_Object pred, Lisp_Object parser, |
| 2864 | bool forward, bool named, ptrdiff_t limit, | ||
| 2605 | bool skip_root) | 2865 | bool skip_root) |
| 2606 | { | 2866 | { |
| 2607 | /* TSTreeCursor doesn't allow us to move backward, so we can't use | 2867 | if (!skip_root |
| 2608 | it. We could use limit == -1 to indicate no_limit == true, but | 2868 | && treesit_traverse_match_predicate (cursor, pred, parser, named)) |
| 2609 | separating them is safer. */ | 2869 | return true; |
| 2610 | TSNode node = *root; | ||
| 2611 | 2870 | ||
| 2612 | if (!skip_root && treesit_traverse_match_predicate (node, pred, parser)) | 2871 | if (limit == 0) |
| 2613 | { | 2872 | return false; |
| 2614 | *root = node; | ||
| 2615 | return true; | ||
| 2616 | } | ||
| 2617 | 2873 | ||
| 2618 | if (!no_limit && limit <= 0) | 2874 | if (!treesit_traverse_child_helper (cursor, forward, named)) |
| 2619 | return false; | 2875 | return false; |
| 2620 | else | 2876 | /* After this point, if you return false, make sure to go back to |
| 2877 | parent. */ | ||
| 2878 | |||
| 2879 | do /* Iterate through each child. */ | ||
| 2621 | { | 2880 | { |
| 2622 | int count = (named | 2881 | if (treesit_search_dfs (cursor, pred, parser, forward, |
| 2623 | ? ts_node_named_child_count (node) | 2882 | named, limit - 1, false)) |
| 2624 | : ts_node_child_count (node)); | 2883 | return true; |
| 2625 | for (int offset = 0; offset < count; offset++) | ||
| 2626 | { | ||
| 2627 | uint32_t idx = forward ? offset : count - offset - 1; | ||
| 2628 | TSNode child = (named | ||
| 2629 | ? ts_node_named_child (node, idx) | ||
| 2630 | : ts_node_child (node, idx)); | ||
| 2631 | |||
| 2632 | if (!ts_node_is_null (child) | ||
| 2633 | && treesit_search_dfs (&child, pred, parser, named, | ||
| 2634 | forward, limit - 1, no_limit, false)) | ||
| 2635 | { | ||
| 2636 | *root = child; | ||
| 2637 | return true; | ||
| 2638 | } | ||
| 2639 | } | ||
| 2640 | return false; | ||
| 2641 | } | 2884 | } |
| 2885 | while (treesit_traverse_sibling_helper (cursor, forward, false)); | ||
| 2886 | |||
| 2887 | /* No match in any child's subtree, go back to starting node. */ | ||
| 2888 | treesit_assume_true (ts_tree_cursor_goto_parent (cursor)); | ||
| 2889 | return false; | ||
| 2642 | } | 2890 | } |
| 2643 | 2891 | ||
| 2644 | /* Go through the whole tree linearly, leaf-first, starting from | 2892 | /* Go through the whole tree linearly, leaf-first, starting from |
| 2645 | START. PRED, PARSER, NAMED, FORWARD are the same as in | 2893 | START. PRED, PARSER, NAMED, FORWARD are the same as in |
| 2646 | ts_search_subtre. If UP_ONLY is true, never go to children, only | 2894 | ts_search_subtree. If a match is found, leave CURSOR at that node, |
| 2647 | sibling and parents. */ | 2895 | and return true, if no match is found, return false, and CURSOR's |
| 2896 | position is undefined. */ | ||
| 2648 | static bool | 2897 | static bool |
| 2649 | treesit_search_forward (TSNode *start, Lisp_Object pred, Lisp_Object parser, | 2898 | treesit_search_forward (TSTreeCursor *cursor, |
| 2650 | bool named, bool forward) | 2899 | Lisp_Object pred, Lisp_Object parser, |
| 2900 | bool forward, bool named) | ||
| 2651 | { | 2901 | { |
| 2652 | TSNode node = *start; | ||
| 2653 | |||
| 2654 | /* We don't search for subtree and always search from the leaf | 2902 | /* We don't search for subtree and always search from the leaf |
| 2655 | nodes. This way repeated call of this function traverses each | 2903 | nodes. This way repeated call of this function traverses each |
| 2656 | node in the tree once and only once: | 2904 | node in the tree once and only once: |
| @@ -2660,39 +2908,26 @@ treesit_search_forward (TSNode *start, Lisp_Object pred, Lisp_Object parser, | |||
| 2660 | bool initial = true; | 2908 | bool initial = true; |
| 2661 | while (true) | 2909 | while (true) |
| 2662 | { | 2910 | { |
| 2663 | if (!initial /* We don't match START. */ | 2911 | if (!initial /* We don't match the starting node. */ |
| 2664 | && treesit_traverse_match_predicate (node, pred, parser)) | 2912 | && treesit_traverse_match_predicate (cursor, pred, parser, named)) |
| 2665 | { | 2913 | return true; |
| 2666 | *start = node; | ||
| 2667 | return true; | ||
| 2668 | } | ||
| 2669 | initial = false; | 2914 | initial = false; |
| 2670 | 2915 | ||
| 2671 | TSNode next = treesit_traverse_sibling_helper (node, forward, named); | 2916 | /* Try going to the next sibling, if there is no next sibling, |
| 2672 | while (ts_node_is_null (next)) | 2917 | go to parent and try again. */ |
| 2918 | while (!treesit_traverse_sibling_helper (cursor, forward, named)) | ||
| 2673 | { | 2919 | { |
| 2674 | /* There is no next sibling, go to parent. */ | 2920 | /* There is no next sibling, go to parent. */ |
| 2675 | node = ts_node_parent (node); | 2921 | if (!ts_tree_cursor_goto_parent (cursor)) |
| 2676 | if (ts_node_is_null (node)) | ||
| 2677 | return false; | 2922 | return false; |
| 2678 | 2923 | ||
| 2679 | if (treesit_traverse_match_predicate (node, pred, parser)) | 2924 | if (treesit_traverse_match_predicate (cursor, pred, parser, named)) |
| 2680 | { | ||
| 2681 | *start = node; | ||
| 2682 | return true; | 2925 | return true; |
| 2683 | } | ||
| 2684 | next = treesit_traverse_sibling_helper (node, forward, named); | ||
| 2685 | } | 2926 | } |
| 2686 | /* We are at the next sibling, deep dive into the first leaf | 2927 | /* We are at the next sibling, deep dive into the first leaf |
| 2687 | node. */ | 2928 | node. */ |
| 2688 | TSNode next_next = treesit_traverse_child_helper (next, forward, named); | 2929 | while (treesit_traverse_child_helper (cursor, forward, false)); |
| 2689 | while (!ts_node_is_null (next_next)) | 2930 | /* At this point CURSOR is at a leaf node. */ |
| 2690 | { | ||
| 2691 | next = next_next; | ||
| 2692 | next_next = treesit_traverse_child_helper (next, forward, named); | ||
| 2693 | } | ||
| 2694 | /* At this point NEXT is a leaf node. */ | ||
| 2695 | node = next; | ||
| 2696 | } | 2931 | } |
| 2697 | } | 2932 | } |
| 2698 | 2933 | ||
| @@ -2707,7 +2942,8 @@ node's type, or a function that takes a node and returns nil/non-nil. | |||
| 2707 | 2942 | ||
| 2708 | By default, only traverse named nodes, but if ALL is non-nil, traverse | 2943 | By default, only traverse named nodes, but if ALL is non-nil, traverse |
| 2709 | all nodes. If BACKWARD is non-nil, traverse backwards. If LIMIT is | 2944 | all nodes. If BACKWARD is non-nil, traverse backwards. If LIMIT is |
| 2710 | non-nil, only traverse nodes up to that number of levels down in the tree. | 2945 | non-nil, only traverse nodes up to that number of levels down in the |
| 2946 | tree. If LIMIT is nil, default to 1000. | ||
| 2711 | 2947 | ||
| 2712 | Return the first matched node, or nil if none matches. */) | 2948 | Return the first matched node, or nil if none matches. */) |
| 2713 | (Lisp_Object node, Lisp_Object predicate, Lisp_Object backward, | 2949 | (Lisp_Object node, Lisp_Object predicate, Lisp_Object backward, |
| @@ -2719,11 +2955,10 @@ Return the first matched node, or nil if none matches. */) | |||
| 2719 | CHECK_SYMBOL (all); | 2955 | CHECK_SYMBOL (all); |
| 2720 | CHECK_SYMBOL (backward); | 2956 | CHECK_SYMBOL (backward); |
| 2721 | 2957 | ||
| 2722 | ptrdiff_t the_limit = 0; | 2958 | /* We use a default limit of 1000. See bug#59426 for the |
| 2723 | bool no_limit = false; | 2959 | discussion. */ |
| 2724 | if (NILP (limit)) | 2960 | ptrdiff_t the_limit = 1000; |
| 2725 | no_limit = true; | 2961 | if (!NILP (limit)) |
| 2726 | else | ||
| 2727 | { | 2962 | { |
| 2728 | CHECK_FIXNUM (limit); | 2963 | CHECK_FIXNUM (limit); |
| 2729 | the_limit = XFIXNUM (limit); | 2964 | the_limit = XFIXNUM (limit); |
| @@ -2731,13 +2966,17 @@ Return the first matched node, or nil if none matches. */) | |||
| 2731 | 2966 | ||
| 2732 | treesit_initialize (); | 2967 | treesit_initialize (); |
| 2733 | 2968 | ||
| 2734 | TSNode treesit_node = XTS_NODE (node)->node; | ||
| 2735 | Lisp_Object parser = XTS_NODE (node)->parser; | 2969 | Lisp_Object parser = XTS_NODE (node)->parser; |
| 2736 | if (treesit_search_dfs (&treesit_node, predicate, parser, NILP (all), | 2970 | Lisp_Object return_value = Qnil; |
| 2737 | NILP (backward), the_limit, no_limit, false)) | 2971 | TSTreeCursor cursor = treesit_cursor_helper (XTS_NODE (node)->node, parser); |
| 2738 | return make_treesit_node (parser, treesit_node); | 2972 | if (treesit_search_dfs (&cursor, predicate, parser, NILP (backward), |
| 2739 | else | 2973 | NILP (all), the_limit, false)) |
| 2740 | return Qnil; | 2974 | { |
| 2975 | TSNode node = ts_tree_cursor_current_node (&cursor); | ||
| 2976 | return_value = make_treesit_node (parser, node); | ||
| 2977 | } | ||
| 2978 | ts_tree_cursor_delete (&cursor); | ||
| 2979 | return return_value; | ||
| 2741 | } | 2980 | } |
| 2742 | 2981 | ||
| 2743 | DEFUN ("treesit-search-forward", | 2982 | DEFUN ("treesit-search-forward", |
| @@ -2781,13 +3020,17 @@ always traverse leaf nodes first, then upwards. */) | |||
| 2781 | 3020 | ||
| 2782 | treesit_initialize (); | 3021 | treesit_initialize (); |
| 2783 | 3022 | ||
| 2784 | TSNode treesit_start = XTS_NODE (start)->node; | ||
| 2785 | Lisp_Object parser = XTS_NODE (start)->parser; | 3023 | Lisp_Object parser = XTS_NODE (start)->parser; |
| 2786 | if (treesit_search_forward (&treesit_start, predicate, parser, NILP (all), | 3024 | Lisp_Object return_value = Qnil; |
| 2787 | NILP (backward))) | 3025 | TSTreeCursor cursor = treesit_cursor_helper (XTS_NODE (start)->node, parser); |
| 2788 | return make_treesit_node (parser, treesit_start); | 3026 | if (treesit_search_forward (&cursor, predicate, parser, |
| 2789 | else | 3027 | NILP (backward), NILP (all))) |
| 2790 | return Qnil; | 3028 | { |
| 3029 | TSNode node = ts_tree_cursor_current_node (&cursor); | ||
| 3030 | return_value = make_treesit_node (parser, node); | ||
| 3031 | } | ||
| 3032 | ts_tree_cursor_delete (&cursor); | ||
| 3033 | return return_value; | ||
| 2791 | } | 3034 | } |
| 2792 | 3035 | ||
| 2793 | /* Recursively traverse the tree under CURSOR, and append the result | 3036 | /* Recursively traverse the tree under CURSOR, and append the result |
| @@ -2797,15 +3040,14 @@ always traverse leaf nodes first, then upwards. */) | |||
| 2797 | static void | 3040 | static void |
| 2798 | treesit_build_sparse_tree (TSTreeCursor *cursor, Lisp_Object parent, | 3041 | treesit_build_sparse_tree (TSTreeCursor *cursor, Lisp_Object parent, |
| 2799 | Lisp_Object pred, Lisp_Object process_fn, | 3042 | Lisp_Object pred, Lisp_Object process_fn, |
| 2800 | ptrdiff_t limit, bool no_limit, Lisp_Object parser) | 3043 | ptrdiff_t limit, Lisp_Object parser) |
| 2801 | { | 3044 | { |
| 2802 | 3045 | bool match = treesit_traverse_match_predicate (cursor, pred, parser, false); | |
| 2803 | TSNode node = ts_tree_cursor_current_node (cursor); | ||
| 2804 | bool match = treesit_traverse_match_predicate (node, pred, parser); | ||
| 2805 | if (match) | 3046 | if (match) |
| 2806 | { | 3047 | { |
| 2807 | /* If this node matches pred, add a new node to the parent's | 3048 | /* If this node matches pred, add a new node to the parent's |
| 2808 | children list. */ | 3049 | children list. */ |
| 3050 | TSNode node = ts_tree_cursor_current_node (cursor); | ||
| 2809 | Lisp_Object lisp_node = make_treesit_node (parser, node); | 3051 | Lisp_Object lisp_node = make_treesit_node (parser, node); |
| 2810 | if (!NILP (process_fn)) | 3052 | if (!NILP (process_fn)) |
| 2811 | lisp_node = CALLN (Ffuncall, process_fn, lisp_node); | 3053 | lisp_node = CALLN (Ffuncall, process_fn, lisp_node); |
| @@ -2816,8 +3058,7 @@ treesit_build_sparse_tree (TSTreeCursor *cursor, Lisp_Object parent, | |||
| 2816 | parent = this; | 3058 | parent = this; |
| 2817 | } | 3059 | } |
| 2818 | /* Go through each child. */ | 3060 | /* Go through each child. */ |
| 2819 | if ((no_limit || limit > 0) | 3061 | if (limit > 0 && ts_tree_cursor_goto_first_child (cursor)) |
| 2820 | && ts_tree_cursor_goto_first_child (cursor)) | ||
| 2821 | { | 3062 | { |
| 2822 | do | 3063 | do |
| 2823 | { | 3064 | { |
| @@ -2825,7 +3066,7 @@ treesit_build_sparse_tree (TSTreeCursor *cursor, Lisp_Object parent, | |||
| 2825 | Then C compilers should be smart enough not to copy NODE | 3066 | Then C compilers should be smart enough not to copy NODE |
| 2826 | to stack. */ | 3067 | to stack. */ |
| 2827 | treesit_build_sparse_tree (cursor, parent, pred, process_fn, | 3068 | treesit_build_sparse_tree (cursor, parent, pred, process_fn, |
| 2828 | limit - 1, no_limit, parser); | 3069 | limit - 1, parser); |
| 2829 | } | 3070 | } |
| 2830 | while (ts_tree_cursor_goto_next_sibling (cursor)); | 3071 | while (ts_tree_cursor_goto_next_sibling (cursor)); |
| 2831 | /* Don't forget to come back to this node. */ | 3072 | /* Don't forget to come back to this node. */ |
| @@ -2866,7 +3107,8 @@ If PROCESS-FN is non-nil, it should be a function of one argument. In | |||
| 2866 | that case, instead of returning the matched nodes, pass each node to | 3107 | that case, instead of returning the matched nodes, pass each node to |
| 2867 | PROCESS-FN, and use its return value instead. | 3108 | PROCESS-FN, and use its return value instead. |
| 2868 | 3109 | ||
| 2869 | If non-nil, LIMIT is the number of levels to go down the tree from ROOT. | 3110 | If non-nil, LIMIT is the number of levels to go down the tree from |
| 3111 | ROOT. If LIMIT is nil or omitted, it defaults to 1000. | ||
| 2870 | 3112 | ||
| 2871 | Each node in the returned tree looks like (NODE . (CHILD ...)). The | 3113 | Each node in the returned tree looks like (NODE . (CHILD ...)). The |
| 2872 | root of this tree might be nil, if ROOT doesn't match PREDICATE. | 3114 | root of this tree might be nil, if ROOT doesn't match PREDICATE. |
| @@ -2885,11 +3127,11 @@ a regexp. */) | |||
| 2885 | 3127 | ||
| 2886 | if (!NILP (process_fn)) | 3128 | if (!NILP (process_fn)) |
| 2887 | CHECK_TYPE (FUNCTIONP (process_fn), Qfunctionp, process_fn); | 3129 | CHECK_TYPE (FUNCTIONP (process_fn), Qfunctionp, process_fn); |
| 2888 | ptrdiff_t the_limit = 0; | 3130 | |
| 2889 | bool no_limit = false; | 3131 | /* We use a default limit of 1000. See bug#59426 for the |
| 2890 | if (NILP (limit)) | 3132 | discussion. */ |
| 2891 | no_limit = true; | 3133 | ptrdiff_t the_limit = 1000; |
| 2892 | else | 3134 | if (!NILP (limit)) |
| 2893 | { | 3135 | { |
| 2894 | CHECK_FIXNUM (limit); | 3136 | CHECK_FIXNUM (limit); |
| 2895 | the_limit = XFIXNUM (limit); | 3137 | the_limit = XFIXNUM (limit); |
| @@ -2897,11 +3139,12 @@ a regexp. */) | |||
| 2897 | 3139 | ||
| 2898 | treesit_initialize (); | 3140 | treesit_initialize (); |
| 2899 | 3141 | ||
| 2900 | TSTreeCursor cursor = ts_tree_cursor_new (XTS_NODE (root)->node); | ||
| 2901 | Lisp_Object parser = XTS_NODE (root)->parser; | 3142 | Lisp_Object parser = XTS_NODE (root)->parser; |
| 2902 | Lisp_Object parent = Fcons (Qnil, Qnil); | 3143 | Lisp_Object parent = Fcons (Qnil, Qnil); |
| 3144 | TSTreeCursor cursor = treesit_cursor_helper (XTS_NODE (root)->node, parser); | ||
| 2903 | treesit_build_sparse_tree (&cursor, parent, predicate, process_fn, | 3145 | treesit_build_sparse_tree (&cursor, parent, predicate, process_fn, |
| 2904 | the_limit, no_limit, parser); | 3146 | the_limit, parser); |
| 3147 | ts_tree_cursor_delete (&cursor); | ||
| 2905 | Fsetcdr (parent, Fnreverse (Fcdr (parent))); | 3148 | Fsetcdr (parent, Fnreverse (Fcdr (parent))); |
| 2906 | if (NILP (Fcdr (parent))) | 3149 | if (NILP (Fcdr (parent))) |
| 2907 | return Qnil; | 3150 | return Qnil; |
diff --git a/src/treesit.h b/src/treesit.h index 1473126c5bc..6f6423ff472 100644 --- a/src/treesit.h +++ b/src/treesit.h | |||
| @@ -56,13 +56,12 @@ struct Lisp_TS_Parser | |||
| 56 | this field to true to force tree-sitter to re-parse. */ | 56 | this field to true to force tree-sitter to re-parse. */ |
| 57 | bool need_reparse; | 57 | bool need_reparse; |
| 58 | /* These two positions record the buffer byte position (1-based) of | 58 | /* These two positions record the buffer byte position (1-based) of |
| 59 | the "visible region" that tree-sitter sees. Unlike markers, | 59 | the "visible region" that tree-sitter sees. Before re-parse, we |
| 60 | These two positions do not change as the user inserts and deletes | 60 | move these positions to match BUF_BEGV_BYTE and BUF_ZV_BYTE. |
| 61 | text around them. Before re-parse, we move these positions to | 61 | Note that we don't need to synchronize these positions when |
| 62 | match BUF_BEGV_BYTE and BUF_ZV_BYTE. Note that we don't need to | 62 | retrieving them in a function that involves a node: if the node |
| 63 | synchronize these positions when retrieving them in a function | 63 | is not outdated, these positions are synchronized. See comment |
| 64 | that involves a node: if the node is not outdated, these | 64 | (ref:visible-beg-null) in treesit.c for more explanation. */ |
| 65 | positions are synchronized. */ | ||
| 66 | ptrdiff_t visible_beg; | 65 | ptrdiff_t visible_beg; |
| 67 | ptrdiff_t visible_end; | 66 | ptrdiff_t visible_end; |
| 68 | /* This counter is incremented every time a change is made to the | 67 | /* This counter is incremented every time a change is made to the |
diff --git a/src/window.c b/src/window.c index f116b9a9d72..90fa6ac2dfe 100644 --- a/src/window.c +++ b/src/window.c | |||
| @@ -2639,7 +2639,7 @@ window_list (void) | |||
| 2639 | Lisp_Object arglist = Qnil; | 2639 | Lisp_Object arglist = Qnil; |
| 2640 | 2640 | ||
| 2641 | /* We are visiting windows in canonical order, and add | 2641 | /* We are visiting windows in canonical order, and add |
| 2642 | new windows at the front of args[1], which means we | 2642 | new windows at the front of arglist, which means we |
| 2643 | have to reverse this list at the end. */ | 2643 | have to reverse this list at the end. */ |
| 2644 | foreach_window (XFRAME (frame), add_window_to_list, &arglist); | 2644 | foreach_window (XFRAME (frame), add_window_to_list, &arglist); |
| 2645 | arglist = Fnreverse (arglist); | 2645 | arglist = Fnreverse (arglist); |
| @@ -7329,6 +7329,14 @@ the return value is nil. Otherwise the value is t. */) | |||
| 7329 | last_selected_window) | 7329 | last_selected_window) |
| 7330 | = selected_window; | 7330 | = selected_window; |
| 7331 | 7331 | ||
| 7332 | /* We may have deleted windows above. Then again, maybe we | ||
| 7333 | haven't: the functions we call to maybe delete windows can | ||
| 7334 | decide a window cannot be deleted. Force recalculation of | ||
| 7335 | Vwindow_list next time it is needed, to make sure stale | ||
| 7336 | windows with no buffers don't escape into the wild, which | ||
| 7337 | will cause crashes elsewhere. */ | ||
| 7338 | Vwindow_list = Qnil; | ||
| 7339 | |||
| 7332 | if (NILP (data->focus_frame) | 7340 | if (NILP (data->focus_frame) |
| 7333 | || (FRAMEP (data->focus_frame) | 7341 | || (FRAMEP (data->focus_frame) |
| 7334 | && FRAME_LIVE_P (XFRAME (data->focus_frame)))) | 7342 | && FRAME_LIVE_P (XFRAME (data->focus_frame)))) |
diff --git a/src/xdisp.c b/src/xdisp.c index 5dcf21dc4ce..e8df230ef89 100644 --- a/src/xdisp.c +++ b/src/xdisp.c | |||
| @@ -3533,6 +3533,33 @@ get_closer_narrowed_begv (struct window *w, ptrdiff_t pos) | |||
| 3533 | return max ((pos / len - 1) * len, BEGV); | 3533 | return max ((pos / len - 1) * len, BEGV); |
| 3534 | } | 3534 | } |
| 3535 | 3535 | ||
| 3536 | ptrdiff_t | ||
| 3537 | get_locked_narrowing_begv (ptrdiff_t pos) | ||
| 3538 | { | ||
| 3539 | if (long_line_locked_narrowing_region_size <= 0) | ||
| 3540 | return BEGV; | ||
| 3541 | int len = long_line_locked_narrowing_region_size / 2; | ||
| 3542 | int begv = max (pos - len, BEGV); | ||
| 3543 | int limit = long_line_locked_narrowing_bol_search_limit; | ||
| 3544 | while (limit > 0) | ||
| 3545 | { | ||
| 3546 | if (begv == BEGV || FETCH_BYTE (CHAR_TO_BYTE (begv) - 1) == '\n') | ||
| 3547 | return begv; | ||
| 3548 | begv--; | ||
| 3549 | limit--; | ||
| 3550 | } | ||
| 3551 | return begv; | ||
| 3552 | } | ||
| 3553 | |||
| 3554 | ptrdiff_t | ||
| 3555 | get_locked_narrowing_zv (ptrdiff_t pos) | ||
| 3556 | { | ||
| 3557 | if (long_line_locked_narrowing_region_size <= 0) | ||
| 3558 | return ZV; | ||
| 3559 | int len = long_line_locked_narrowing_region_size / 2; | ||
| 3560 | return min (pos + len, ZV); | ||
| 3561 | } | ||
| 3562 | |||
| 3536 | static void | 3563 | static void |
| 3537 | unwind_narrowed_begv (Lisp_Object point_min) | 3564 | unwind_narrowed_begv (Lisp_Object point_min) |
| 3538 | { | 3565 | { |
| @@ -4366,18 +4393,20 @@ handle_fontified_prop (struct it *it) | |||
| 4366 | 4393 | ||
| 4367 | eassert (it->end_charpos == ZV); | 4394 | eassert (it->end_charpos == ZV); |
| 4368 | 4395 | ||
| 4369 | if (current_buffer->long_line_optimizations_p) | 4396 | if (current_buffer->long_line_optimizations_p |
| 4397 | && long_line_locked_narrowing_region_size > 0) | ||
| 4370 | { | 4398 | { |
| 4371 | ptrdiff_t begv = it->narrowed_begv; | 4399 | ptrdiff_t begv = it->locked_narrowing_begv; |
| 4372 | ptrdiff_t zv = it->narrowed_zv; | 4400 | ptrdiff_t zv = it->locked_narrowing_zv; |
| 4373 | ptrdiff_t charpos = IT_CHARPOS (*it); | 4401 | ptrdiff_t charpos = IT_CHARPOS (*it); |
| 4374 | if (charpos < begv || charpos > zv) | 4402 | if (charpos < begv || charpos > zv) |
| 4375 | { | 4403 | { |
| 4376 | begv = get_narrowed_begv (it->w, charpos); | 4404 | begv = get_locked_narrowing_begv (charpos); |
| 4377 | zv = get_narrowed_zv (it->w, charpos); | 4405 | zv = get_locked_narrowing_zv (charpos); |
| 4378 | } | 4406 | } |
| 4379 | narrow_to_region_internal (make_fixnum (begv), make_fixnum (zv), true); | 4407 | if (begv != BEG || zv != Z) |
| 4380 | specbind (Qrestrictions_locked, Qt); | 4408 | narrow_to_region_locked (make_fixnum (begv), make_fixnum (zv), |
| 4409 | Qfontification_functions); | ||
| 4381 | } | 4410 | } |
| 4382 | 4411 | ||
| 4383 | /* Don't allow Lisp that runs from 'fontification-functions' | 4412 | /* Don't allow Lisp that runs from 'fontification-functions' |
| @@ -6252,13 +6281,16 @@ static ptrdiff_t | |||
| 6252 | string_buffer_position (Lisp_Object string, ptrdiff_t around_charpos) | 6281 | string_buffer_position (Lisp_Object string, ptrdiff_t around_charpos) |
| 6253 | { | 6282 | { |
| 6254 | const int MAX_DISTANCE = 1000; | 6283 | const int MAX_DISTANCE = 1000; |
| 6284 | ptrdiff_t forward_limit = min (around_charpos + MAX_DISTANCE, ZV); | ||
| 6255 | ptrdiff_t found = string_buffer_position_lim (string, around_charpos, | 6285 | ptrdiff_t found = string_buffer_position_lim (string, around_charpos, |
| 6256 | around_charpos + MAX_DISTANCE, | 6286 | forward_limit, false); |
| 6257 | false); | ||
| 6258 | 6287 | ||
| 6259 | if (!found) | 6288 | if (!found) |
| 6260 | found = string_buffer_position_lim (string, around_charpos, | 6289 | { |
| 6261 | around_charpos - MAX_DISTANCE, true); | 6290 | ptrdiff_t backward_limit = max (around_charpos - MAX_DISTANCE, BEGV); |
| 6291 | found = string_buffer_position_lim (string, around_charpos, | ||
| 6292 | backward_limit, true); | ||
| 6293 | } | ||
| 6262 | return found; | 6294 | return found; |
| 6263 | } | 6295 | } |
| 6264 | 6296 | ||
| @@ -7435,12 +7467,20 @@ reseat (struct it *it, struct text_pos pos, bool force_p) | |||
| 7435 | { | 7467 | { |
| 7436 | it->narrowed_begv = get_narrowed_begv (it->w, window_point (it->w)); | 7468 | it->narrowed_begv = get_narrowed_begv (it->w, window_point (it->w)); |
| 7437 | it->narrowed_zv = get_narrowed_zv (it->w, window_point (it->w)); | 7469 | it->narrowed_zv = get_narrowed_zv (it->w, window_point (it->w)); |
| 7470 | it->locked_narrowing_begv | ||
| 7471 | = get_locked_narrowing_begv (window_point (it->w)); | ||
| 7472 | it->locked_narrowing_zv | ||
| 7473 | = get_locked_narrowing_zv (window_point (it->w)); | ||
| 7438 | } | 7474 | } |
| 7439 | else if ((pos.charpos < it->narrowed_begv || pos.charpos > it->narrowed_zv) | 7475 | else if ((pos.charpos < it->narrowed_begv || pos.charpos > it->narrowed_zv) |
| 7440 | && (!redisplaying_p || it->line_wrap == TRUNCATE)) | 7476 | && (!redisplaying_p || it->line_wrap == TRUNCATE)) |
| 7441 | { | 7477 | { |
| 7442 | it->narrowed_begv = get_narrowed_begv (it->w, pos.charpos); | 7478 | it->narrowed_begv = get_narrowed_begv (it->w, pos.charpos); |
| 7443 | it->narrowed_zv = get_narrowed_zv (it->w, pos.charpos); | 7479 | it->narrowed_zv = get_narrowed_zv (it->w, pos.charpos); |
| 7480 | it->locked_narrowing_begv | ||
| 7481 | = get_locked_narrowing_begv (window_point (it->w)); | ||
| 7482 | it->locked_narrowing_zv | ||
| 7483 | = get_locked_narrowing_zv (window_point (it->w)); | ||
| 7444 | } | 7484 | } |
| 7445 | } | 7485 | } |
| 7446 | 7486 | ||
| @@ -16266,7 +16306,6 @@ do { if (! polling_stopped_here) stop_polling (); \ | |||
| 16266 | do { if (polling_stopped_here) start_polling (); \ | 16306 | do { if (polling_stopped_here) start_polling (); \ |
| 16267 | polling_stopped_here = false; } while (false) | 16307 | polling_stopped_here = false; } while (false) |
| 16268 | 16308 | ||
| 16269 | |||
| 16270 | /* Perhaps in the future avoid recentering windows if it | 16309 | /* Perhaps in the future avoid recentering windows if it |
| 16271 | is not necessary; currently that causes some problems. */ | 16310 | is not necessary; currently that causes some problems. */ |
| 16272 | 16311 | ||
| @@ -16352,6 +16391,8 @@ redisplay_internal (void) | |||
| 16352 | FOR_EACH_FRAME (tail, frame) | 16391 | FOR_EACH_FRAME (tail, frame) |
| 16353 | XFRAME (frame)->already_hscrolled_p = false; | 16392 | XFRAME (frame)->already_hscrolled_p = false; |
| 16354 | 16393 | ||
| 16394 | reset_outermost_narrowings (); | ||
| 16395 | |||
| 16355 | retry: | 16396 | retry: |
| 16356 | /* Remember the currently selected window. */ | 16397 | /* Remember the currently selected window. */ |
| 16357 | sw = w; | 16398 | sw = w; |
| @@ -17226,7 +17267,6 @@ mark_window_display_accurate_1 (struct window *w, bool accurate_p) | |||
| 17226 | 17267 | ||
| 17227 | BUF_UNCHANGED_MODIFIED (b) = BUF_MODIFF (b); | 17268 | BUF_UNCHANGED_MODIFIED (b) = BUF_MODIFF (b); |
| 17228 | BUF_OVERLAY_UNCHANGED_MODIFIED (b) = BUF_OVERLAY_MODIFF (b); | 17269 | BUF_OVERLAY_UNCHANGED_MODIFIED (b) = BUF_OVERLAY_MODIFF (b); |
| 17229 | BUF_CHARS_UNCHANGED_MODIFIED (b) = BUF_CHARS_MODIFF (b); | ||
| 17230 | BUF_BEG_UNCHANGED (b) = BUF_GPT (b) - BUF_BEG (b); | 17270 | BUF_BEG_UNCHANGED (b) = BUF_GPT (b) - BUF_BEG (b); |
| 17231 | BUF_END_UNCHANGED (b) = BUF_Z (b) - BUF_GPT (b); | 17271 | BUF_END_UNCHANGED (b) = BUF_Z (b) - BUF_GPT (b); |
| 17232 | 17272 | ||
| @@ -19392,6 +19432,13 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) | |||
| 19392 | blank_row (w, row, y); | 19432 | blank_row (w, row, y); |
| 19393 | goto finish_scroll_bars; | 19433 | goto finish_scroll_bars; |
| 19394 | } | 19434 | } |
| 19435 | else if (minibuf_level >= 1) | ||
| 19436 | { | ||
| 19437 | /* We could have a message produced by set-minibuffer-message | ||
| 19438 | displayed in the mini-window as an overlay, so resize the | ||
| 19439 | mini-window if needed. */ | ||
| 19440 | resize_mini_window (w, false); | ||
| 19441 | } | ||
| 19395 | 19442 | ||
| 19396 | clear_glyph_matrix (w->desired_matrix); | 19443 | clear_glyph_matrix (w->desired_matrix); |
| 19397 | } | 19444 | } |
| @@ -19497,7 +19544,8 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) | |||
| 19497 | /* Check whether the buffer to be displayed contains long lines. */ | 19544 | /* Check whether the buffer to be displayed contains long lines. */ |
| 19498 | if (!NILP (Vlong_line_threshold) | 19545 | if (!NILP (Vlong_line_threshold) |
| 19499 | && !current_buffer->long_line_optimizations_p | 19546 | && !current_buffer->long_line_optimizations_p |
| 19500 | && CHARS_MODIFF - CHARS_UNCHANGED_MODIFIED > 8) | 19547 | && (CHARS_MODIFF - UNCHANGED_MODIFIED > 8 |
| 19548 | || current_buffer->clip_changed)) | ||
| 19501 | { | 19549 | { |
| 19502 | ptrdiff_t cur, next, found, max = 0, threshold; | 19550 | ptrdiff_t cur, next, found, max = 0, threshold; |
| 19503 | threshold = XFIXNUM (Vlong_line_threshold); | 19551 | threshold = XFIXNUM (Vlong_line_threshold); |
| @@ -22578,7 +22626,8 @@ usage: (trace-to-stderr STRING &rest OBJECTS) */) | |||
| 22578 | ***********************************************************************/ | 22626 | ***********************************************************************/ |
| 22579 | 22627 | ||
| 22580 | /* Return a temporary glyph row holding the glyphs of an overlay arrow. | 22628 | /* Return a temporary glyph row holding the glyphs of an overlay arrow. |
| 22581 | Used for non-window-redisplay windows, and for windows w/o left fringe. */ | 22629 | Used for non-window-redisplay windows, and for windows without left |
| 22630 | fringe. */ | ||
| 22582 | 22631 | ||
| 22583 | static struct glyph_row * | 22632 | static struct glyph_row * |
| 22584 | get_overlay_arrow_glyph_row (struct window *w, Lisp_Object overlay_arrow_string) | 22633 | get_overlay_arrow_glyph_row (struct window *w, Lisp_Object overlay_arrow_string) |
| @@ -26272,16 +26321,17 @@ display_menu_bar (struct window *w) | |||
| 26272 | it.first_visible_x = 0; | 26321 | it.first_visible_x = 0; |
| 26273 | it.last_visible_x = FRAME_PIXEL_WIDTH (f); | 26322 | it.last_visible_x = FRAME_PIXEL_WIDTH (f); |
| 26274 | #elif defined (HAVE_X_WINDOWS) /* X without toolkit. */ | 26323 | #elif defined (HAVE_X_WINDOWS) /* X without toolkit. */ |
| 26324 | struct window *menu_window = NULL; | ||
| 26325 | struct face *face = FACE_FROM_ID (f, MENU_FACE_ID); | ||
| 26326 | |||
| 26275 | if (FRAME_WINDOW_P (f)) | 26327 | if (FRAME_WINDOW_P (f)) |
| 26276 | { | 26328 | { |
| 26277 | /* Menu bar lines are displayed in the desired matrix of the | 26329 | /* Menu bar lines are displayed in the desired matrix of the |
| 26278 | dummy window menu_bar_window. */ | 26330 | dummy window menu_bar_window. */ |
| 26279 | struct window *menu_w; | 26331 | menu_window = XWINDOW (f->menu_bar_window); |
| 26280 | menu_w = XWINDOW (f->menu_bar_window); | 26332 | init_iterator (&it, menu_window, -1, -1, |
| 26281 | init_iterator (&it, menu_w, -1, -1, menu_w->desired_matrix->rows, | 26333 | menu_window->desired_matrix->rows, |
| 26282 | MENU_FACE_ID); | 26334 | MENU_FACE_ID); |
| 26283 | it.first_visible_x = 0; | ||
| 26284 | it.last_visible_x = FRAME_PIXEL_WIDTH (f); | ||
| 26285 | } | 26335 | } |
| 26286 | else | 26336 | else |
| 26287 | #endif /* not USE_X_TOOLKIT and not USE_GTK */ | 26337 | #endif /* not USE_X_TOOLKIT and not USE_GTK */ |
| @@ -26335,6 +26385,50 @@ display_menu_bar (struct window *w) | |||
| 26335 | 26385 | ||
| 26336 | /* Compute the total height of the lines. */ | 26386 | /* Compute the total height of the lines. */ |
| 26337 | compute_line_metrics (&it); | 26387 | compute_line_metrics (&it); |
| 26388 | it.glyph_row->full_width_p = true; | ||
| 26389 | it.glyph_row->continued_p = false; | ||
| 26390 | it.glyph_row->truncated_on_left_p = false; | ||
| 26391 | it.glyph_row->truncated_on_right_p = false; | ||
| 26392 | |||
| 26393 | #if defined (HAVE_X_WINDOWS) && !defined (USE_X_TOOLKIT) && !defined (USE_GTK) | ||
| 26394 | /* Make a 3D menu bar have a shadow at its right end. */ | ||
| 26395 | extend_face_to_end_of_line (&it); | ||
| 26396 | if (face->box != FACE_NO_BOX) | ||
| 26397 | { | ||
| 26398 | struct glyph *last = (it.glyph_row->glyphs[TEXT_AREA] | ||
| 26399 | + it.glyph_row->used[TEXT_AREA] - 1); | ||
| 26400 | int box_thickness = face->box_vertical_line_width; | ||
| 26401 | last->right_box_line_p = true; | ||
| 26402 | /* Add back the space for the right box line we subtracted in | ||
| 26403 | init_iterator, since the right_box_line_p flag will make the | ||
| 26404 | glyph wider. We actually add only as much space as is | ||
| 26405 | available for the last glyph of the menu bar and whatever | ||
| 26406 | space is left beyond it, since that glyph could be only | ||
| 26407 | partially visible. */ | ||
| 26408 | if (box_thickness > 0) | ||
| 26409 | last->pixel_width += max (0, (box_thickness | ||
| 26410 | - (it.current_x - it.last_visible_x))); | ||
| 26411 | } | ||
| 26412 | |||
| 26413 | /* With the non-toolkit version, modify the menu bar window height | ||
| 26414 | accordingly. */ | ||
| 26415 | if (FRAME_WINDOW_P (it.f) && menu_window) | ||
| 26416 | { | ||
| 26417 | struct glyph_row *row; | ||
| 26418 | int delta_height; | ||
| 26419 | |||
| 26420 | row = it.glyph_row; | ||
| 26421 | delta_height | ||
| 26422 | = ((row->y + row->height) | ||
| 26423 | - WINDOW_BOX_HEIGHT_NO_MODE_LINE (menu_window)); | ||
| 26424 | |||
| 26425 | if (delta_height != 0) | ||
| 26426 | { | ||
| 26427 | FRAME_MENU_BAR_HEIGHT (it.f) += delta_height; | ||
| 26428 | adjust_frame_size (it.f, -1, -1, 3, false, Qmenu_bar_lines); | ||
| 26429 | } | ||
| 26430 | } | ||
| 26431 | #endif | ||
| 26338 | } | 26432 | } |
| 26339 | 26433 | ||
| 26340 | /* Deep copy of a glyph row, including the glyphs. */ | 26434 | /* Deep copy of a glyph row, including the glyphs. */ |
| @@ -36711,10 +36805,11 @@ fontify a region starting at POS in the current buffer, and give | |||
| 36711 | fontified regions the property `fontified' with a non-nil value. | 36805 | fontified regions the property `fontified' with a non-nil value. |
| 36712 | 36806 | ||
| 36713 | Note that, when the buffer contains one or more lines whose length is | 36807 | Note that, when the buffer contains one or more lines whose length is |
| 36714 | above `long-line-threshold', these functions are called with the buffer | 36808 | above `long-line-threshold', these functions are called with the |
| 36715 | narrowed to a small portion around POS, and the narrowing is locked (see | 36809 | buffer narrowed to a small portion around POS (whose size is specified |
| 36716 | `narrow-to-region'), so that these functions cannot use `widen' to gain | 36810 | by `long-line-locked-narrowing-region-size'), and the narrowing is |
| 36717 | access to other portions of buffer text. */); | 36811 | locked (see `narrowing-lock'), so that these functions cannot use |
| 36812 | `widen' to gain access to other portions of buffer text. */); | ||
| 36718 | Vfontification_functions = Qnil; | 36813 | Vfontification_functions = Qnil; |
| 36719 | Fmake_variable_buffer_local (Qfontification_functions); | 36814 | Fmake_variable_buffer_local (Qfontification_functions); |
| 36720 | 36815 | ||
diff --git a/src/xfaces.c b/src/xfaces.c index df078227c8a..be4a7ca71cc 100644 --- a/src/xfaces.c +++ b/src/xfaces.c | |||
| @@ -6014,6 +6014,23 @@ realize_non_ascii_face (struct frame *f, Lisp_Object font_object, | |||
| 6014 | } | 6014 | } |
| 6015 | #endif /* HAVE_WINDOW_SYSTEM */ | 6015 | #endif /* HAVE_WINDOW_SYSTEM */ |
| 6016 | 6016 | ||
| 6017 | /* Remove the attribute at INDEX from the font object if SYMBOL | ||
| 6018 | appears in `font-fallback-ignored-attributes'. */ | ||
| 6019 | |||
| 6020 | static void | ||
| 6021 | font_maybe_unset_attribute (Lisp_Object font_object, | ||
| 6022 | enum font_property_index index, Lisp_Object symbol) | ||
| 6023 | { | ||
| 6024 | Lisp_Object tail = Vface_font_lax_matched_attributes; | ||
| 6025 | |||
| 6026 | eassert (CONSP (tail)); | ||
| 6027 | |||
| 6028 | FOR_EACH_TAIL_SAFE (tail) | ||
| 6029 | { | ||
| 6030 | if (EQ (XCAR (tail), symbol)) | ||
| 6031 | ASET (font_object, index, Qnil); | ||
| 6032 | } | ||
| 6033 | } | ||
| 6017 | 6034 | ||
| 6018 | /* Realize the fully-specified face with attributes ATTRS in face | 6035 | /* Realize the fully-specified face with attributes ATTRS in face |
| 6019 | cache CACHE for ASCII characters. Do it for GUI frame CACHE->f. | 6036 | cache CACHE for ASCII characters. Do it for GUI frame CACHE->f. |
| @@ -6071,8 +6088,48 @@ realize_gui_face (struct face_cache *cache, Lisp_Object attrs[LFACE_VECTOR_SIZE] | |||
| 6071 | emacs_abort (); | 6088 | emacs_abort (); |
| 6072 | } | 6089 | } |
| 6073 | if (! FONT_OBJECT_P (attrs[LFACE_FONT_INDEX])) | 6090 | if (! FONT_OBJECT_P (attrs[LFACE_FONT_INDEX])) |
| 6074 | attrs[LFACE_FONT_INDEX] | 6091 | { |
| 6075 | = font_load_for_lface (f, attrs, attrs[LFACE_FONT_INDEX]); | 6092 | Lisp_Object spec = copy_font_spec (attrs[LFACE_FONT_INDEX]); |
| 6093 | |||
| 6094 | /* Maybe unset several values in SPEC, usually the width, | ||
| 6095 | slant, and weight. The best possible values for these | ||
| 6096 | attributes are determined in font_find_for_lface, called | ||
| 6097 | by font_load_for_lface, when the list of candidate fonts | ||
| 6098 | returned by font_list_entities is sorted by font_select_entity | ||
| 6099 | (which calls font_sort_entities, which calls font_score). | ||
| 6100 | If these attributes are not unset here, the candidate | ||
| 6101 | font list returned by font_list_entities only contains | ||
| 6102 | fonts that are exact matches for these weight, slant, and | ||
| 6103 | width attributes, which could lead to suboptimal or wrong | ||
| 6104 | font selection. (bug#5934) */ | ||
| 6105 | if (EQ (Vface_font_lax_matched_attributes, Qt)) | ||
| 6106 | { | ||
| 6107 | /* The default case: clear the font attributes that | ||
| 6108 | affect its appearance the least, to try to find some | ||
| 6109 | font that is close, if not exact, match. */ | ||
| 6110 | ASET (spec, FONT_WEIGHT_INDEX, Qnil); | ||
| 6111 | ASET (spec, FONT_SLANT_INDEX, Qnil); | ||
| 6112 | ASET (spec, FONT_WIDTH_INDEX, Qnil); | ||
| 6113 | } | ||
| 6114 | else if (!NILP (Vface_font_lax_matched_attributes)) | ||
| 6115 | { | ||
| 6116 | /* Also allow unsetting specific attributes for | ||
| 6117 | debugging purposes. */ | ||
| 6118 | font_maybe_unset_attribute (spec, FONT_WEIGHT_INDEX, QCweight); | ||
| 6119 | font_maybe_unset_attribute (spec, FONT_SLANT_INDEX, QCslant); | ||
| 6120 | font_maybe_unset_attribute (spec, FONT_WIDTH_INDEX, QCwidth); | ||
| 6121 | font_maybe_unset_attribute (spec, FONT_FAMILY_INDEX, QCfamily); | ||
| 6122 | font_maybe_unset_attribute (spec, FONT_FOUNDRY_INDEX, QCfoundry); | ||
| 6123 | font_maybe_unset_attribute (spec, FONT_REGISTRY_INDEX, QCregistry); | ||
| 6124 | font_maybe_unset_attribute (spec, FONT_ADSTYLE_INDEX, QCadstyle); | ||
| 6125 | font_maybe_unset_attribute (spec, FONT_SIZE_INDEX, QCsize); | ||
| 6126 | font_maybe_unset_attribute (spec, FONT_DPI_INDEX, QCdpi); | ||
| 6127 | font_maybe_unset_attribute (spec, FONT_SPACING_INDEX, QCspacing); | ||
| 6128 | font_maybe_unset_attribute (spec, FONT_AVGWIDTH_INDEX, QCavgwidth); | ||
| 6129 | } | ||
| 6130 | |||
| 6131 | attrs[LFACE_FONT_INDEX] = font_load_for_lface (f, attrs, spec); | ||
| 6132 | } | ||
| 6076 | if (FONT_OBJECT_P (attrs[LFACE_FONT_INDEX])) | 6133 | if (FONT_OBJECT_P (attrs[LFACE_FONT_INDEX])) |
| 6077 | { | 6134 | { |
| 6078 | face->font = XFONT_OBJECT (attrs[LFACE_FONT_INDEX]); | 6135 | face->font = XFONT_OBJECT (attrs[LFACE_FONT_INDEX]); |
| @@ -7360,6 +7417,35 @@ Lisp programs that change the value of this variable should also | |||
| 7360 | clear the face cache, see `clear-face-cache'. */); | 7417 | clear the face cache, see `clear-face-cache'. */); |
| 7361 | face_near_same_color_threshold = 30000; | 7418 | face_near_same_color_threshold = 30000; |
| 7362 | 7419 | ||
| 7420 | DEFVAR_LISP ("face-font-lax-matched-attributes", | ||
| 7421 | Vface_font_lax_matched_attributes, | ||
| 7422 | doc: /* Whether to match some face attributes in lax manner when realizing faces. | ||
| 7423 | |||
| 7424 | If non-nil, some font-related face attributes will be matched in a lax | ||
| 7425 | manner when looking for candidate fonts. | ||
| 7426 | If the value is t, the default, the search for fonts will not insist | ||
| 7427 | on exact match for 3 font attributes: weight, width, and slant. | ||
| 7428 | Instead, it will examine the available fonts with various values of | ||
| 7429 | these attributes, and select the font that is the closest possible | ||
| 7430 | match. (If an exact match is available, it will still be selected, | ||
| 7431 | as that is the closest match.) For example, looking for a semi-bold | ||
| 7432 | font might select a bold or a medium-weight font if no semi-bold font | ||
| 7433 | matching other attributes can be found. This is especially important | ||
| 7434 | when the `default' face specifies unusual values for one or more of | ||
| 7435 | these 3 attributes, which other installed fonts don't support. | ||
| 7436 | |||
| 7437 | The value can also be a list of font-related face attribute symbols; | ||
| 7438 | see `set-face-attribute' for the full list of attributes. Then the | ||
| 7439 | corresponding face attributes will be treated as "soft" constraints | ||
| 7440 | in the manner described above, instead of the default 3 attributes. | ||
| 7441 | |||
| 7442 | If the value is nil, candidate fonts might be rejected if the don't | ||
| 7443 | have exactly the same values of attributes as the face requests. | ||
| 7444 | |||
| 7445 | This variable exists for debugging of the font-selection process, | ||
| 7446 | and we advise not to change it otherwise. */); | ||
| 7447 | Vface_font_lax_matched_attributes = Qt; | ||
| 7448 | |||
| 7363 | #ifdef HAVE_WINDOW_SYSTEM | 7449 | #ifdef HAVE_WINDOW_SYSTEM |
| 7364 | defsubr (&Sbitmap_spec_p); | 7450 | defsubr (&Sbitmap_spec_p); |
| 7365 | defsubr (&Sx_list_fonts); | 7451 | defsubr (&Sx_list_fonts); |
diff --git a/src/xfns.c b/src/xfns.c index fa2c0751d90..668f711bdb5 100644 --- a/src/xfns.c +++ b/src/xfns.c | |||
| @@ -43,7 +43,6 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 43 | #ifdef USE_XCB | 43 | #ifdef USE_XCB |
| 44 | #include <xcb/xcb.h> | 44 | #include <xcb/xcb.h> |
| 45 | #include <xcb/xproto.h> | 45 | #include <xcb/xproto.h> |
| 46 | #include <xcb/xcb_aux.h> | ||
| 47 | #endif | 46 | #endif |
| 48 | 47 | ||
| 49 | #include "bitmaps/gray.xbm" | 48 | #include "bitmaps/gray.xbm" |
| @@ -1368,7 +1367,7 @@ x_set_mouse_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval) | |||
| 1368 | XCreateFontCursor is not a request that waits for a reply, | 1367 | XCreateFontCursor is not a request that waits for a reply, |
| 1369 | and as such can return IDs that will not actually be used by | 1368 | and as such can return IDs that will not actually be used by |
| 1370 | the server. */ | 1369 | the server. */ |
| 1371 | x_ignore_errors_for_next_request (FRAME_DISPLAY_INFO (f)); | 1370 | x_ignore_errors_for_next_request (FRAME_DISPLAY_INFO (f), 0); |
| 1372 | 1371 | ||
| 1373 | /* Free any successfully created cursors. */ | 1372 | /* Free any successfully created cursors. */ |
| 1374 | for (i = 0; i < mouse_cursor_max; i++) | 1373 | for (i = 0; i < mouse_cursor_max; i++) |
| @@ -2643,12 +2642,18 @@ append_wm_protocols (struct x_display_info *dpyinfo, | |||
| 2643 | if (existing) | 2642 | if (existing) |
| 2644 | XFree (existing); | 2643 | XFree (existing); |
| 2645 | 2644 | ||
| 2646 | if (!found_wm_ping) | 2645 | if (!dpyinfo->untrusted) |
| 2647 | protos[num_protos++] = dpyinfo->Xatom_net_wm_ping; | 2646 | { |
| 2647 | /* Untrusted clients cannot use these protocols which require | ||
| 2648 | communicating with the window manager. */ | ||
| 2649 | |||
| 2650 | if (!found_wm_ping) | ||
| 2651 | protos[num_protos++] = dpyinfo->Xatom_net_wm_ping; | ||
| 2648 | #if !defined HAVE_GTK3 && defined HAVE_XSYNC | 2652 | #if !defined HAVE_GTK3 && defined HAVE_XSYNC |
| 2649 | if (!found_wm_sync_request && dpyinfo->xsync_supported_p) | 2653 | if (!found_wm_sync_request && dpyinfo->xsync_supported_p) |
| 2650 | protos[num_protos++] = dpyinfo->Xatom_net_wm_sync_request; | 2654 | protos[num_protos++] = dpyinfo->Xatom_net_wm_sync_request; |
| 2651 | #endif | 2655 | #endif |
| 2656 | } | ||
| 2652 | 2657 | ||
| 2653 | if (num_protos) | 2658 | if (num_protos) |
| 2654 | XChangeProperty (dpyinfo->display, | 2659 | XChangeProperty (dpyinfo->display, |
| @@ -5723,13 +5728,13 @@ x_get_net_workarea (struct x_display_info *dpyinfo, XRectangle *rect) | |||
| 5723 | = xcb_get_property (dpyinfo->xcb_connection, 0, | 5728 | = xcb_get_property (dpyinfo->xcb_connection, 0, |
| 5724 | (xcb_window_t) dpyinfo->root_window, | 5729 | (xcb_window_t) dpyinfo->root_window, |
| 5725 | (xcb_atom_t) dpyinfo->Xatom_net_current_desktop, | 5730 | (xcb_atom_t) dpyinfo->Xatom_net_current_desktop, |
| 5726 | XCB_ATOM_CARDINAL, 0, 1); | 5731 | XA_CARDINAL, 0, 1); |
| 5727 | 5732 | ||
| 5728 | workarea_cookie | 5733 | workarea_cookie |
| 5729 | = xcb_get_property (dpyinfo->xcb_connection, 0, | 5734 | = xcb_get_property (dpyinfo->xcb_connection, 0, |
| 5730 | (xcb_window_t) dpyinfo->root_window, | 5735 | (xcb_window_t) dpyinfo->root_window, |
| 5731 | (xcb_atom_t) dpyinfo->Xatom_net_workarea, | 5736 | (xcb_atom_t) dpyinfo->Xatom_net_workarea, |
| 5732 | XCB_ATOM_CARDINAL, 0, UINT32_MAX); | 5737 | XA_CARDINAL, 0, UINT32_MAX); |
| 5733 | 5738 | ||
| 5734 | reply = xcb_get_property_reply (dpyinfo->xcb_connection, | 5739 | reply = xcb_get_property_reply (dpyinfo->xcb_connection, |
| 5735 | current_desktop_cookie, &error); | 5740 | current_desktop_cookie, &error); |
| @@ -5740,7 +5745,7 @@ x_get_net_workarea (struct x_display_info *dpyinfo, XRectangle *rect) | |||
| 5740 | else | 5745 | else |
| 5741 | { | 5746 | { |
| 5742 | if (xcb_get_property_value_length (reply) != 4 | 5747 | if (xcb_get_property_value_length (reply) != 4 |
| 5743 | || reply->type != XCB_ATOM_CARDINAL || reply->format != 32) | 5748 | || reply->type != XA_CARDINAL || reply->format != 32) |
| 5744 | rc = false; | 5749 | rc = false; |
| 5745 | else | 5750 | else |
| 5746 | current_workspace = *(uint32_t *) xcb_get_property_value (reply); | 5751 | current_workspace = *(uint32_t *) xcb_get_property_value (reply); |
| @@ -5755,7 +5760,7 @@ x_get_net_workarea (struct x_display_info *dpyinfo, XRectangle *rect) | |||
| 5755 | free (error), rc = false; | 5760 | free (error), rc = false; |
| 5756 | else | 5761 | else |
| 5757 | { | 5762 | { |
| 5758 | if (rc && reply->type == XCB_ATOM_CARDINAL && reply->format == 32 | 5763 | if (rc && reply->type == XA_CARDINAL && reply->format == 32 |
| 5759 | && (xcb_get_property_value_length (reply) / sizeof (uint32_t) | 5764 | && (xcb_get_property_value_length (reply) / sizeof (uint32_t) |
| 5760 | >= current_workspace + 4)) | 5765 | >= current_workspace + 4)) |
| 5761 | { | 5766 | { |
| @@ -7079,8 +7084,8 @@ that mouse buttons are being held down, such as immediately after a | |||
| 7079 | /* Catch errors since interning lots of targets can potentially | 7084 | /* Catch errors since interning lots of targets can potentially |
| 7080 | generate a BadAlloc error. */ | 7085 | generate a BadAlloc error. */ |
| 7081 | x_catch_errors (FRAME_X_DISPLAY (f)); | 7086 | x_catch_errors (FRAME_X_DISPLAY (f)); |
| 7082 | XInternAtoms (FRAME_X_DISPLAY (f), target_names, | 7087 | x_intern_atoms (FRAME_DISPLAY_INFO (f), target_names, |
| 7083 | ntargets, False, target_atoms); | 7088 | ntargets, target_atoms); |
| 7084 | x_check_errors (FRAME_X_DISPLAY (f), | 7089 | x_check_errors (FRAME_X_DISPLAY (f), |
| 7085 | "Failed to intern target atoms: %s"); | 7090 | "Failed to intern target atoms: %s"); |
| 7086 | x_uncatch_errors_after_check (); | 7091 | x_uncatch_errors_after_check (); |
| @@ -7377,20 +7382,6 @@ If TERMINAL is omitted or nil, that stands for the selected frame's display. */ | |||
| 7377 | return Qnil; | 7382 | return Qnil; |
| 7378 | } | 7383 | } |
| 7379 | 7384 | ||
| 7380 | /* Wait for responses to all X commands issued so far for frame F. */ | ||
| 7381 | |||
| 7382 | void | ||
| 7383 | x_sync (struct frame *f) | ||
| 7384 | { | ||
| 7385 | block_input (); | ||
| 7386 | #ifndef USE_XCB | ||
| 7387 | XSync (FRAME_X_DISPLAY (f), False); | ||
| 7388 | #else | ||
| 7389 | xcb_aux_sync (FRAME_DISPLAY_INFO (f)->xcb_connection); | ||
| 7390 | #endif | ||
| 7391 | unblock_input (); | ||
| 7392 | } | ||
| 7393 | |||
| 7394 | 7385 | ||
| 7395 | /*********************************************************************** | 7386 | /*********************************************************************** |
| 7396 | Window properties | 7387 | Window properties |
| @@ -7484,7 +7475,7 @@ silently ignored. */) | |||
| 7484 | elsize = element_format == 32 ? sizeof (long) : element_format >> 3; | 7475 | elsize = element_format == 32 ? sizeof (long) : element_format >> 3; |
| 7485 | data = xnmalloc (nelements, elsize); | 7476 | data = xnmalloc (nelements, elsize); |
| 7486 | 7477 | ||
| 7487 | x_fill_property_data (FRAME_X_DISPLAY (f), value, data, nelements, | 7478 | x_fill_property_data (FRAME_DISPLAY_INFO (f), value, data, nelements, |
| 7488 | element_format); | 7479 | element_format); |
| 7489 | } | 7480 | } |
| 7490 | else | 7481 | else |
| @@ -8455,10 +8446,10 @@ compute_tip_xy (struct frame *f, Lisp_Object parms, Lisp_Object dx, | |||
| 8455 | int min_x, min_y, max_x, max_y = -1; | 8446 | int min_x, min_y, max_x, max_y = -1; |
| 8456 | 8447 | ||
| 8457 | /* User-specified position? */ | 8448 | /* User-specified position? */ |
| 8458 | left = Fcdr (Fassq (Qleft, parms)); | 8449 | left = CDR (Fassq (Qleft, parms)); |
| 8459 | top = Fcdr (Fassq (Qtop, parms)); | 8450 | top = CDR (Fassq (Qtop, parms)); |
| 8460 | right = Fcdr (Fassq (Qright, parms)); | 8451 | right = CDR (Fassq (Qright, parms)); |
| 8461 | bottom = Fcdr (Fassq (Qbottom, parms)); | 8452 | bottom = CDR (Fassq (Qbottom, parms)); |
| 8462 | 8453 | ||
| 8463 | /* Move the tooltip window where the mouse pointer is. Resize and | 8454 | /* Move the tooltip window where the mouse pointer is. Resize and |
| 8464 | show it. */ | 8455 | show it. */ |
| @@ -8824,14 +8815,14 @@ Text larger than the specified size is clipped. */) | |||
| 8824 | for (tail = parms; CONSP (tail); tail = XCDR (tail)) | 8815 | for (tail = parms; CONSP (tail); tail = XCDR (tail)) |
| 8825 | { | 8816 | { |
| 8826 | elt = XCAR (tail); | 8817 | elt = XCAR (tail); |
| 8827 | parm = Fcar (elt); | 8818 | parm = CAR (elt); |
| 8828 | /* The left, top, right and bottom parameters are handled | 8819 | /* The left, top, right and bottom parameters are handled |
| 8829 | by compute_tip_xy so they can be ignored here. */ | 8820 | by compute_tip_xy so they can be ignored here. */ |
| 8830 | if (!EQ (parm, Qleft) && !EQ (parm, Qtop) | 8821 | if (!EQ (parm, Qleft) && !EQ (parm, Qtop) |
| 8831 | && !EQ (parm, Qright) && !EQ (parm, Qbottom)) | 8822 | && !EQ (parm, Qright) && !EQ (parm, Qbottom)) |
| 8832 | { | 8823 | { |
| 8833 | last = Fassq (parm, tip_last_parms); | 8824 | last = Fassq (parm, tip_last_parms); |
| 8834 | if (NILP (Fequal (Fcdr (elt), Fcdr (last)))) | 8825 | if (NILP (Fequal (CDR (elt), CDR (last)))) |
| 8835 | { | 8826 | { |
| 8836 | /* We lost, delete the old tooltip. */ | 8827 | /* We lost, delete the old tooltip. */ |
| 8837 | delete = true; | 8828 | delete = true; |
| @@ -8852,9 +8843,9 @@ Text larger than the specified size is clipped. */) | |||
| 8852 | for (tail = tip_last_parms; CONSP (tail); tail = XCDR (tail)) | 8843 | for (tail = tip_last_parms; CONSP (tail); tail = XCDR (tail)) |
| 8853 | { | 8844 | { |
| 8854 | elt = XCAR (tail); | 8845 | elt = XCAR (tail); |
| 8855 | parm = Fcar (elt); | 8846 | parm = CAR (elt); |
| 8856 | if (!EQ (parm, Qleft) && !EQ (parm, Qtop) && !EQ (parm, Qright) | 8847 | if (!EQ (parm, Qleft) && !EQ (parm, Qtop) && !EQ (parm, Qright) |
| 8857 | && !EQ (parm, Qbottom) && !NILP (Fcdr (elt))) | 8848 | && !EQ (parm, Qbottom) && !NILP (CDR (elt))) |
| 8858 | { | 8849 | { |
| 8859 | /* We lost, delete the old tooltip. */ | 8850 | /* We lost, delete the old tooltip. */ |
| 8860 | delete = true; | 8851 | delete = true; |
| @@ -8975,8 +8966,8 @@ Text larger than the specified size is clipped. */) | |||
| 8975 | make_fixnum (w->pixel_height), Qnil, | 8966 | make_fixnum (w->pixel_height), Qnil, |
| 8976 | Qnil); | 8967 | Qnil); |
| 8977 | /* Add the frame's internal border to calculated size. */ | 8968 | /* Add the frame's internal border to calculated size. */ |
| 8978 | width = XFIXNUM (Fcar (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f); | 8969 | width = XFIXNUM (CAR (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f); |
| 8979 | height = XFIXNUM (Fcdr (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f); | 8970 | height = XFIXNUM (CDR (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f); |
| 8980 | 8971 | ||
| 8981 | /* Calculate position of tooltip frame. */ | 8972 | /* Calculate position of tooltip frame. */ |
| 8982 | compute_tip_xy (tip_f, parms, dx, dy, width, height, &root_x, &root_y); | 8973 | compute_tip_xy (tip_f, parms, dx, dy, width, height, &root_x, &root_y); |
diff --git a/src/xftfont.c b/src/xftfont.c index 6043ef9f94f..b9686db2a73 100644 --- a/src/xftfont.c +++ b/src/xftfont.c | |||
| @@ -628,6 +628,12 @@ xftfont_shape (Lisp_Object lgstring, Lisp_Object direction) | |||
| 628 | static int | 628 | static int |
| 629 | xftfont_end_for_frame (struct frame *f) | 629 | xftfont_end_for_frame (struct frame *f) |
| 630 | { | 630 | { |
| 631 | /* XftDrawDestroy tries to access dpyinfo->display, which could've | ||
| 632 | been destroyed by now, causing Emacs to crash. The alternative | ||
| 633 | is to leak the XftDraw, but that's better than a crash. */ | ||
| 634 | if (!FRAME_X_DISPLAY (f)) | ||
| 635 | return 0; | ||
| 636 | |||
| 631 | block_input (); | 637 | block_input (); |
| 632 | XftDraw *xft_draw; | 638 | XftDraw *xft_draw; |
| 633 | 639 | ||
diff --git a/src/xselect.c b/src/xselect.c index a381fa23522..05548be311f 100644 --- a/src/xselect.c +++ b/src/xselect.c | |||
| @@ -16,7 +16,6 @@ GNU General Public License for more details. | |||
| 16 | You should have received a copy of the GNU General Public License | 16 | You should have received a copy of the GNU General Public License |
| 17 | along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | 17 | along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ |
| 18 | 18 | ||
| 19 | |||
| 20 | /* Rewritten by jwz */ | 19 | /* Rewritten by jwz */ |
| 21 | 20 | ||
| 22 | #include <config.h> | 21 | #include <config.h> |
| @@ -44,7 +43,7 @@ struct prop_location; | |||
| 44 | struct selection_data; | 43 | struct selection_data; |
| 45 | 44 | ||
| 46 | static void x_decline_selection_request (struct selection_input_event *); | 45 | static void x_decline_selection_request (struct selection_input_event *); |
| 47 | static bool x_convert_selection (Lisp_Object, Lisp_Object, Atom, bool, | 46 | static bool x_convert_selection (Lisp_Object, Lisp_Object, Atom, |
| 48 | struct x_display_info *, bool); | 47 | struct x_display_info *, bool); |
| 49 | static bool waiting_for_other_props_on_window (Display *, Window); | 48 | static bool waiting_for_other_props_on_window (Display *, Window); |
| 50 | static struct prop_location *expect_property_change (Display *, Window, | 49 | static struct prop_location *expect_property_change (Display *, Window, |
| @@ -77,18 +76,20 @@ static void x_send_client_event (Lisp_Object, Lisp_Object, Lisp_Object, | |||
| 77 | #define TRACE0(fmt) (void) 0 | 76 | #define TRACE0(fmt) (void) 0 |
| 78 | #define TRACE1(fmt, a0) (void) 0 | 77 | #define TRACE1(fmt, a0) (void) 0 |
| 79 | #define TRACE2(fmt, a0, a1) (void) 0 | 78 | #define TRACE2(fmt, a0, a1) (void) 0 |
| 79 | #define TRACE3(fmt, a0, a1, a2) (void) 0 | ||
| 80 | #endif | 80 | #endif |
| 81 | 81 | ||
| 82 | /* Bytes needed to represent 'long' data. This is as per libX11; it | 82 | /* Bytes needed to represent 'long' data. This is as per libX11; it |
| 83 | is not necessarily sizeof (long). */ | 83 | is not necessarily sizeof (long). */ |
| 84 | #define X_LONG_SIZE 4 | 84 | #define X_LONG_SIZE 4 |
| 85 | 85 | ||
| 86 | /* If this is a smaller number than the max-request-size of the display, | 86 | /* If this is a smaller number than the max-request-size of the |
| 87 | emacs will use INCR selection transfer when the selection is larger | 87 | display, Emacs will use INCR selection transfer when the selection |
| 88 | than this. The max-request-size is usually around 64k, so if you want | 88 | is larger than this. The max-request-size is usually around 64k, |
| 89 | emacs to use incremental selection transfers when the selection is | 89 | so if you want emacs to use incremental selection transfers when |
| 90 | smaller than that, set this. I added this mostly for debugging the | 90 | the selection is smaller than that, set this. I added this mostly |
| 91 | incremental transfer stuff, but it might improve server performance. | 91 | for debugging the incremental transfer stuff, but it might improve |
| 92 | server performance. | ||
| 92 | 93 | ||
| 93 | This value cannot exceed INT_MAX / max (X_LONG_SIZE, sizeof (long)) | 94 | This value cannot exceed INT_MAX / max (X_LONG_SIZE, sizeof (long)) |
| 94 | because it is multiplied by X_LONG_SIZE and by sizeof (long) in | 95 | because it is multiplied by X_LONG_SIZE and by sizeof (long) in |
| @@ -101,7 +102,9 @@ static void x_send_client_event (Lisp_Object, Lisp_Object, Lisp_Object, | |||
| 101 | static int | 102 | static int |
| 102 | selection_quantum (Display *display) | 103 | selection_quantum (Display *display) |
| 103 | { | 104 | { |
| 104 | long mrs = XExtendedMaxRequestSize (display); | 105 | long mrs; |
| 106 | |||
| 107 | mrs = XExtendedMaxRequestSize (display); | ||
| 105 | 108 | ||
| 106 | if (!mrs) | 109 | if (!mrs) |
| 107 | mrs = XMaxRequestSize (display); | 110 | mrs = XMaxRequestSize (display); |
| @@ -281,10 +284,8 @@ x_own_selection (Lisp_Object selection_name, Lisp_Object selection_value, | |||
| 281 | timestamp = dpyinfo->last_user_time; | 284 | timestamp = dpyinfo->last_user_time; |
| 282 | 285 | ||
| 283 | block_input (); | 286 | block_input (); |
| 284 | x_catch_errors (display); | 287 | XSetSelectionOwner (display, selection_atom, selecting_window, |
| 285 | XSetSelectionOwner (display, selection_atom, selecting_window, timestamp); | 288 | timestamp); |
| 286 | x_check_errors (display, "Can't set selection: %s"); | ||
| 287 | x_uncatch_errors_after_check (); | ||
| 288 | unblock_input (); | 289 | unblock_input (); |
| 289 | 290 | ||
| 290 | /* Now update the local cache */ | 291 | /* Now update the local cache */ |
| @@ -308,7 +309,7 @@ x_own_selection (Lisp_Object selection_name, Lisp_Object selection_value, | |||
| 308 | /* We know it's not the CAR, so it's easy. */ | 309 | /* We know it's not the CAR, so it's easy. */ |
| 309 | Lisp_Object rest = dpyinfo->terminal->Vselection_alist; | 310 | Lisp_Object rest = dpyinfo->terminal->Vselection_alist; |
| 310 | for (; CONSP (rest); rest = XCDR (rest)) | 311 | for (; CONSP (rest); rest = XCDR (rest)) |
| 311 | if (EQ (prev_value, Fcar (XCDR (rest)))) | 312 | if (EQ (prev_value, CAR (XCDR (rest)))) |
| 312 | { | 313 | { |
| 313 | XSETCDR (rest, XCDR (XCDR (rest))); | 314 | XSETCDR (rest, XCDR (XCDR (rest))); |
| 314 | break; | 315 | break; |
| @@ -369,7 +370,7 @@ x_get_local_selection (Lisp_Object selection_symbol, Lisp_Object target_type, | |||
| 369 | specbind (Qinhibit_quit, Qt); | 370 | specbind (Qinhibit_quit, Qt); |
| 370 | 371 | ||
| 371 | CHECK_SYMBOL (target_type); | 372 | CHECK_SYMBOL (target_type); |
| 372 | handler_fn = Fcdr (Fassq (target_type, Vselection_converter_alist)); | 373 | handler_fn = CDR (Fassq (target_type, Vselection_converter_alist)); |
| 373 | 374 | ||
| 374 | if (CONSP (handler_fn)) | 375 | if (CONSP (handler_fn)) |
| 375 | handler_fn = XCDR (handler_fn); | 376 | handler_fn = XCDR (handler_fn); |
| @@ -459,7 +460,7 @@ x_decline_selection_request (struct selection_input_event *event) | |||
| 459 | /* The reason for the error may be that the receiver has | 460 | /* The reason for the error may be that the receiver has |
| 460 | died in the meantime. Handle that case. */ | 461 | died in the meantime. Handle that case. */ |
| 461 | block_input (); | 462 | block_input (); |
| 462 | x_ignore_errors_for_next_request (dpyinfo); | 463 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 463 | XSendEvent (dpyinfo->display, reply->requestor, | 464 | XSendEvent (dpyinfo->display, reply->requestor, |
| 464 | False, 0, &reply_base); | 465 | False, 0, &reply_base); |
| 465 | x_stop_ignoring_errors (dpyinfo); | 466 | x_stop_ignoring_errors (dpyinfo); |
| @@ -472,19 +473,65 @@ x_decline_selection_request (struct selection_input_event *event) | |||
| 472 | 473 | ||
| 473 | struct selection_data | 474 | struct selection_data |
| 474 | { | 475 | { |
| 476 | /* Pointer to the selection data. */ | ||
| 475 | unsigned char *data; | 477 | unsigned char *data; |
| 478 | |||
| 479 | /* A Lisp_Object containing the selection data. This is either | ||
| 480 | Qnil, or `data' is NULL. If non-nil, then this must be a string | ||
| 481 | whose contents will be written out verbatim. */ | ||
| 482 | Lisp_Object string; | ||
| 483 | |||
| 484 | /* The size, in number of items, of the selection data. | ||
| 485 | The value is meaningless if string is non-nil. */ | ||
| 476 | ptrdiff_t size; | 486 | ptrdiff_t size; |
| 487 | |||
| 488 | /* The format of the selection data. */ | ||
| 477 | int format; | 489 | int format; |
| 490 | |||
| 491 | /* The type of the selection data. */ | ||
| 478 | Atom type; | 492 | Atom type; |
| 479 | bool nofree; | 493 | |
| 494 | /* The property describing the selection data. */ | ||
| 480 | Atom property; | 495 | Atom property; |
| 481 | /* This can be set to non-NULL during x_reply_selection_request, if | 496 | |
| 482 | the selection is waiting for an INCR transfer to complete. Don't | 497 | /* The next piece of selection data in the current selection request |
| 483 | free these; that's done by unexpect_property_change. */ | 498 | stack frame. This can be NULL. */ |
| 484 | struct prop_location *wait_object; | ||
| 485 | struct selection_data *next; | 499 | struct selection_data *next; |
| 486 | }; | 500 | }; |
| 487 | 501 | ||
| 502 | /* Structure representing a single outstanding selection request (or | ||
| 503 | subrequest if MULTIPLE is being used.) */ | ||
| 504 | |||
| 505 | struct transfer | ||
| 506 | { | ||
| 507 | /* The requestor of this transfer. */ | ||
| 508 | Window requestor; | ||
| 509 | |||
| 510 | /* The current offset in items into the selection data, and the | ||
| 511 | number of items to send with each ChangeProperty request. */ | ||
| 512 | size_t offset, items_per_request; | ||
| 513 | |||
| 514 | /* The display info associated with the transfer. */ | ||
| 515 | struct x_display_info *dpyinfo; | ||
| 516 | |||
| 517 | /* The converted selection data. */ | ||
| 518 | struct selection_data data; | ||
| 519 | |||
| 520 | /* The next and last selection transfers on this list. */ | ||
| 521 | struct transfer *next, *last; | ||
| 522 | |||
| 523 | /* The atimer for the timeout. */ | ||
| 524 | struct atimer *timeout; | ||
| 525 | |||
| 526 | /* The selection serial. */ | ||
| 527 | unsigned int serial; | ||
| 528 | |||
| 529 | /* Flags. */ | ||
| 530 | int flags; | ||
| 531 | }; | ||
| 532 | |||
| 533 | #define SELECTED_EVENTS 1 | ||
| 534 | |||
| 488 | struct x_selection_request | 535 | struct x_selection_request |
| 489 | { | 536 | { |
| 490 | /* The last element in this stack. */ | 537 | /* The last element in this stack. */ |
| @@ -499,8 +546,8 @@ struct x_selection_request | |||
| 499 | /* Linked list of the above (in support of MULTIPLE targets). */ | 546 | /* Linked list of the above (in support of MULTIPLE targets). */ |
| 500 | struct selection_data *converted_selections; | 547 | struct selection_data *converted_selections; |
| 501 | 548 | ||
| 502 | /* "Data" to send a requestor for a failed MULTIPLE subtarget. */ | 549 | /* The serial used to handle X errors. */ |
| 503 | Atom conversion_fail_tag; | 550 | unsigned int serial; |
| 504 | 551 | ||
| 505 | /* Whether or not conversion was successful. */ | 552 | /* Whether or not conversion was successful. */ |
| 506 | bool converted; | 553 | bool converted; |
| @@ -511,6 +558,50 @@ struct x_selection_request | |||
| 511 | 558 | ||
| 512 | struct x_selection_request *selection_request_stack; | 559 | struct x_selection_request *selection_request_stack; |
| 513 | 560 | ||
| 561 | /* List of all outstanding selection transfers which are currently | ||
| 562 | being processed. */ | ||
| 563 | |||
| 564 | struct transfer outstanding_transfers; | ||
| 565 | |||
| 566 | /* A counter for selection serials. */ | ||
| 567 | |||
| 568 | static unsigned int selection_serial; | ||
| 569 | |||
| 570 | |||
| 571 | |||
| 572 | struct prop_location | ||
| 573 | { | ||
| 574 | int identifier; | ||
| 575 | Display *display; | ||
| 576 | Window window; | ||
| 577 | Atom property; | ||
| 578 | int desired_state; | ||
| 579 | bool arrived; | ||
| 580 | struct prop_location *next; | ||
| 581 | }; | ||
| 582 | |||
| 583 | static int prop_location_identifier; | ||
| 584 | |||
| 585 | static Lisp_Object property_change_reply; | ||
| 586 | |||
| 587 | static struct prop_location *property_change_reply_object; | ||
| 588 | |||
| 589 | static struct prop_location *property_change_wait_list; | ||
| 590 | |||
| 591 | static void | ||
| 592 | set_property_change_object (struct prop_location *location) | ||
| 593 | { | ||
| 594 | /* Input must be blocked so we don't get the event before we set | ||
| 595 | these. */ | ||
| 596 | if (! input_blocked_p ()) | ||
| 597 | emacs_abort (); | ||
| 598 | |||
| 599 | XSETCAR (property_change_reply, Qnil); | ||
| 600 | property_change_reply_object = location; | ||
| 601 | } | ||
| 602 | |||
| 603 | |||
| 604 | |||
| 514 | static void | 605 | static void |
| 515 | x_push_current_selection_request (struct selection_input_event *se, | 606 | x_push_current_selection_request (struct selection_input_event *se, |
| 516 | struct x_display_info *dpyinfo) | 607 | struct x_display_info *dpyinfo) |
| @@ -523,7 +614,6 @@ x_push_current_selection_request (struct selection_input_event *se, | |||
| 523 | frame->request = se; | 614 | frame->request = se; |
| 524 | frame->dpyinfo = dpyinfo; | 615 | frame->dpyinfo = dpyinfo; |
| 525 | frame->converted_selections = NULL; | 616 | frame->converted_selections = NULL; |
| 526 | frame->conversion_fail_tag = None; | ||
| 527 | 617 | ||
| 528 | selection_request_stack = frame; | 618 | selection_request_stack = frame; |
| 529 | } | 619 | } |
| @@ -554,7 +644,7 @@ x_selection_request_lisp_error (void) | |||
| 554 | for (cs = frame->converted_selections; cs; cs = next) | 644 | for (cs = frame->converted_selections; cs; cs = next) |
| 555 | { | 645 | { |
| 556 | next = cs->next; | 646 | next = cs->next; |
| 557 | if (! cs->nofree && cs->data) | 647 | if (cs->data) |
| 558 | xfree (cs->data); | 648 | xfree (cs->data); |
| 559 | xfree (cs); | 649 | xfree (cs); |
| 560 | } | 650 | } |
| @@ -564,250 +654,450 @@ x_selection_request_lisp_error (void) | |||
| 564 | x_decline_selection_request (frame->request); | 654 | x_decline_selection_request (frame->request); |
| 565 | } | 655 | } |
| 566 | 656 | ||
| 567 | static void | 657 | |
| 568 | x_catch_errors_unwind (void) | 658 | |
| 659 | static size_t | ||
| 660 | c_size_for_format (int format) | ||
| 569 | { | 661 | { |
| 570 | block_input (); | 662 | switch (format) |
| 571 | x_uncatch_errors (); | 663 | { |
| 572 | unblock_input (); | 664 | case 8: |
| 665 | return sizeof (char); | ||
| 666 | |||
| 667 | case 16: | ||
| 668 | return sizeof (short); | ||
| 669 | |||
| 670 | case 32: | ||
| 671 | return sizeof (long); | ||
| 672 | } | ||
| 673 | |||
| 674 | emacs_abort (); | ||
| 573 | } | 675 | } |
| 574 | |||
| 575 | 676 | ||
| 576 | /* This stuff is so that INCR selections are reentrant (that is, so we can | 677 | static size_t |
| 577 | be servicing multiple INCR selection requests simultaneously.) I haven't | 678 | x_size_for_format (int format) |
| 578 | actually tested that yet. */ | 679 | { |
| 680 | switch (format) | ||
| 681 | { | ||
| 682 | case 8: | ||
| 683 | return 1; | ||
| 684 | |||
| 685 | case 16: | ||
| 686 | return 2; | ||
| 579 | 687 | ||
| 580 | /* Keep a list of the property changes that are awaited. */ | 688 | case 32: |
| 689 | return 4; | ||
| 690 | } | ||
| 581 | 691 | ||
| 582 | struct prop_location | 692 | emacs_abort (); |
| 693 | } | ||
| 694 | |||
| 695 | /* Return a pointer to the remaining part of the selection data, given | ||
| 696 | a pointer to a struct selection_data and an offset in items. Place | ||
| 697 | the number of items remaining in REMAINING. Garbage collection | ||
| 698 | must not happen, or the returned pointer becomes invalid. */ | ||
| 699 | |||
| 700 | static unsigned char * | ||
| 701 | selection_data_for_offset (struct selection_data *data, | ||
| 702 | long offset, size_t *remaining) | ||
| 583 | { | 703 | { |
| 584 | int identifier; | 704 | unsigned char *base; |
| 585 | Display *display; | 705 | size_t size; |
| 586 | Window window; | ||
| 587 | Atom property; | ||
| 588 | int desired_state; | ||
| 589 | bool arrived; | ||
| 590 | struct prop_location *next; | ||
| 591 | }; | ||
| 592 | 706 | ||
| 593 | static int prop_location_identifier; | 707 | if (!NILP (data->string)) |
| 708 | { | ||
| 709 | base = SDATA (data->string); | ||
| 710 | size = SBYTES (data->string); | ||
| 711 | } | ||
| 712 | else | ||
| 713 | { | ||
| 714 | base = data->data; | ||
| 715 | size = data->size; | ||
| 716 | } | ||
| 594 | 717 | ||
| 595 | static Lisp_Object property_change_reply; | 718 | if (offset >= size) |
| 719 | { | ||
| 720 | *remaining = 0; | ||
| 721 | return NULL; | ||
| 722 | } | ||
| 596 | 723 | ||
| 597 | static struct prop_location *property_change_reply_object; | 724 | base += (offset * c_size_for_format (data->format)); |
| 725 | *remaining = size - offset; | ||
| 726 | return base; | ||
| 727 | } | ||
| 598 | 728 | ||
| 599 | static struct prop_location *property_change_wait_list; | 729 | /* Return the size, in bytes transferred to the X server, of |
| 730 | data->size items of selection data in data->format-bit | ||
| 731 | quantities. */ | ||
| 600 | 732 | ||
| 601 | static void | 733 | static size_t |
| 602 | set_property_change_object (struct prop_location *location) | 734 | selection_data_size (struct selection_data *data) |
| 603 | { | 735 | { |
| 604 | /* Input must be blocked so we don't get the event before we set these. */ | 736 | size_t scratch; |
| 605 | if (! input_blocked_p ()) | 737 | |
| 606 | emacs_abort (); | 738 | if (!NILP (data->string)) |
| 607 | XSETCAR (property_change_reply, Qnil); | 739 | return SBYTES (data->string); |
| 608 | property_change_reply_object = location; | 740 | |
| 741 | switch (data->format) | ||
| 742 | { | ||
| 743 | case 8: | ||
| 744 | return (size_t) data->size; | ||
| 745 | |||
| 746 | case 16: | ||
| 747 | if (INT_MULTIPLY_WRAPV (data->size, 2, &scratch)) | ||
| 748 | return SIZE_MAX; | ||
| 749 | |||
| 750 | return scratch; | ||
| 751 | |||
| 752 | case 32: | ||
| 753 | if (INT_MULTIPLY_WRAPV (data->size, 4, &scratch)) | ||
| 754 | return SIZE_MAX; | ||
| 755 | |||
| 756 | return scratch; | ||
| 757 | } | ||
| 758 | |||
| 759 | /* The specified format is invalid. */ | ||
| 760 | emacs_abort (); | ||
| 609 | } | 761 | } |
| 610 | 762 | ||
| 611 | 763 | /* Return whether or not another outstanding selection transfer is | |
| 612 | /* Send the reply to a selection request event EVENT. */ | 764 | still selecting for events on the specified requestor window. */ |
| 613 | 765 | ||
| 614 | #ifdef TRACE_SELECTION | 766 | static bool |
| 615 | static int x_reply_selection_request_cnt; | 767 | transfer_selecting_event (struct x_display_info *dpyinfo, |
| 616 | #endif /* TRACE_SELECTION */ | 768 | Window requestor) |
| 769 | { | ||
| 770 | struct transfer *next; | ||
| 771 | |||
| 772 | next = outstanding_transfers.next; | ||
| 773 | for (; next != &outstanding_transfers; next = next->next) | ||
| 774 | { | ||
| 775 | if (next->requestor == requestor | ||
| 776 | && next->dpyinfo == dpyinfo) | ||
| 777 | return true; | ||
| 778 | } | ||
| 779 | |||
| 780 | return false; | ||
| 781 | } | ||
| 782 | |||
| 783 | /* Cancel the specified selection transfer. When called by | ||
| 784 | `start_transfer', the transfer may be partially formed. */ | ||
| 617 | 785 | ||
| 618 | static void | 786 | static void |
| 619 | x_reply_selection_request (struct selection_input_event *event, | 787 | x_cancel_selection_transfer (struct transfer *transfer) |
| 620 | struct x_display_info *dpyinfo) | ||
| 621 | { | 788 | { |
| 622 | XEvent reply_base; | 789 | xfree (transfer->data.data); |
| 623 | XSelectionEvent *reply = &(reply_base.xselection); | ||
| 624 | Display *display = SELECTION_EVENT_DISPLAY (event); | ||
| 625 | Window window = SELECTION_EVENT_REQUESTOR (event); | ||
| 626 | ptrdiff_t bytes_remaining; | ||
| 627 | int max_bytes = selection_quantum (display); | ||
| 628 | specpdl_ref count = SPECPDL_INDEX (); | ||
| 629 | struct selection_data *cs; | ||
| 630 | struct x_selection_request *frame; | ||
| 631 | 790 | ||
| 632 | frame = selection_request_stack; | 791 | if (transfer->next) |
| 792 | { | ||
| 793 | transfer->next->last = transfer->last; | ||
| 794 | transfer->last->next = transfer->next; | ||
| 795 | } | ||
| 633 | 796 | ||
| 634 | reply->type = SelectionNotify; | 797 | if (transfer->flags & SELECTED_EVENTS |
| 635 | reply->display = display; | 798 | && !transfer_selecting_event (transfer->dpyinfo, |
| 636 | reply->requestor = window; | 799 | transfer->requestor) |
| 637 | reply->selection = SELECTION_EVENT_SELECTION (event); | 800 | /* This can be called from x_delete_display. */ |
| 638 | reply->time = SELECTION_EVENT_TIME (event); | 801 | && transfer->dpyinfo->display) |
| 639 | reply->target = SELECTION_EVENT_TARGET (event); | 802 | { |
| 640 | reply->property = SELECTION_EVENT_PROPERTY (event); | 803 | /* Ignore errors generated by the change window request in case |
| 641 | if (reply->property == None) | 804 | the window has gone away. */ |
| 642 | reply->property = reply->target; | 805 | block_input (); |
| 806 | x_ignore_errors_for_next_request (transfer->dpyinfo, 0); | ||
| 807 | XSelectInput (transfer->dpyinfo->display, | ||
| 808 | transfer->requestor, NoEventMask); | ||
| 809 | x_stop_ignoring_errors (transfer->dpyinfo); | ||
| 810 | unblock_input (); | ||
| 811 | } | ||
| 643 | 812 | ||
| 644 | block_input (); | 813 | cancel_atimer (transfer->timeout); |
| 645 | /* The protected block contains wait_for_property_change, which can | 814 | xfree (transfer); |
| 646 | run random lisp code (process handlers) or signal. Therefore, we | 815 | } |
| 647 | put the x_uncatch_errors call in an unwind. */ | ||
| 648 | record_unwind_protect_void (x_catch_errors_unwind); | ||
| 649 | x_catch_errors (display); | ||
| 650 | 816 | ||
| 651 | /* Loop over converted selections, storing them in the requested | 817 | static void |
| 652 | properties. If data is large, only store the first N bytes | 818 | x_selection_transfer_timeout (struct atimer *atimer) |
| 653 | (section 2.7.2 of ICCCM). Note that we store the data for a | 819 | { |
| 654 | MULTIPLE request in the opposite order; the ICCM says only that | 820 | struct transfer *transfer; |
| 655 | the conversion itself must be done in the same order. */ | 821 | |
| 656 | for (cs = frame->converted_selections; cs; cs = cs->next) | 822 | transfer = atimer->client_data; |
| 823 | x_cancel_selection_transfer (transfer); | ||
| 824 | } | ||
| 825 | |||
| 826 | /* Start a selection transfer to write the specified selection data to | ||
| 827 | its requestor. If the data is small enough, write it to the | ||
| 828 | requestor window and return. Otherwise, start INCR transfer and | ||
| 829 | begin listening for PropertyNotify events on the requestor. */ | ||
| 830 | |||
| 831 | static void | ||
| 832 | x_start_selection_transfer (struct x_display_info *dpyinfo, Window requestor, | ||
| 833 | struct selection_data *data) | ||
| 834 | { | ||
| 835 | struct transfer *transfer; | ||
| 836 | intmax_t timeout; | ||
| 837 | intmax_t secs; | ||
| 838 | int nsecs; | ||
| 839 | size_t remaining, max_size; | ||
| 840 | unsigned char *xdata; | ||
| 841 | unsigned long data_size; | ||
| 842 | |||
| 843 | timeout = max (0, x_selection_timeout); | ||
| 844 | secs = timeout / 1000; | ||
| 845 | nsecs = (timeout % 1000) * 1000000; | ||
| 846 | |||
| 847 | transfer = xzalloc (sizeof *transfer); | ||
| 848 | transfer->requestor = requestor; | ||
| 849 | transfer->dpyinfo = dpyinfo; | ||
| 850 | |||
| 851 | transfer->timeout = start_atimer (ATIMER_RELATIVE, | ||
| 852 | make_timespec (secs, nsecs), | ||
| 853 | x_selection_transfer_timeout, | ||
| 854 | transfer); | ||
| 855 | |||
| 856 | /* Note that DATA is copied into transfer. DATA->data is then set | ||
| 857 | to NULL, giving the struct transfer ownership over the selection | ||
| 858 | data. */ | ||
| 859 | |||
| 860 | transfer->data = *data; | ||
| 861 | data->data = NULL; | ||
| 862 | |||
| 863 | /* Finally, transfer now holds a reference to data->string, if it is | ||
| 864 | present. GC cannot be allowed to happen until this function | ||
| 865 | returns. */ | ||
| 866 | data->string = Qnil; | ||
| 867 | |||
| 868 | /* Now, try to write the selection data. If it is bigger than | ||
| 869 | selection_quantum (dpyinfo->display), start incremental transfer | ||
| 870 | and link the transfer onto the list of pending selections. | ||
| 871 | Otherwise, write the transfer at once. */ | ||
| 872 | |||
| 873 | max_size = selection_quantum (dpyinfo->display); | ||
| 874 | |||
| 875 | TRACE3 (" x_start_selection_transfer: transferring to 0x%lx. " | ||
| 876 | "transfer consists of %zu bytes, quantum being %zu", | ||
| 877 | requestor, selection_data_size (&transfer->data), | ||
| 878 | max_size); | ||
| 879 | |||
| 880 | if (selection_data_size (&transfer->data) > max_size) | ||
| 657 | { | 881 | { |
| 658 | if (cs->property == None) | 882 | /* Begin incremental selection transfer. First, calculate how |
| 659 | continue; | 883 | many elements it is ok to write for every ChangeProperty |
| 884 | request. */ | ||
| 885 | transfer->items_per_request | ||
| 886 | = (max_size / x_size_for_format (transfer->data.format)); | ||
| 887 | TRACE1 (" x_start_selection_transfer: starting incremental" | ||
| 888 | " selection transfer, with %zu items per request", | ||
| 889 | transfer->items_per_request); | ||
| 890 | |||
| 891 | /* Next, link the transfer onto the list of pending selection | ||
| 892 | transfers. */ | ||
| 893 | transfer->next = outstanding_transfers.next; | ||
| 894 | transfer->last = &outstanding_transfers; | ||
| 895 | transfer->next->last = transfer; | ||
| 896 | transfer->last->next = transfer; | ||
| 897 | |||
| 898 | /* Find a valid (non-zero) serial for the selection transfer. | ||
| 899 | Any asynchronously trapped errors will then cause the | ||
| 900 | selection transfer to be cancelled. */ | ||
| 901 | transfer->serial = (++selection_serial | ||
| 902 | ? selection_serial | ||
| 903 | : ++selection_serial); | ||
| 904 | |||
| 905 | /* Now, write the INCR property to begin incremental selection | ||
| 906 | transfer. offset is currently 0. */ | ||
| 907 | |||
| 908 | data_size = selection_data_size (&transfer->data); | ||
| 909 | |||
| 910 | /* Set SELECTED_EVENTS before the actual XSelectInput | ||
| 911 | request. */ | ||
| 912 | transfer->flags |= SELECTED_EVENTS; | ||
| 913 | |||
| 914 | x_ignore_errors_for_next_request (dpyinfo, transfer->serial); | ||
| 915 | XChangeProperty (dpyinfo->display, requestor, | ||
| 916 | transfer->data.property, | ||
| 917 | dpyinfo->Xatom_INCR, 32, PropModeReplace, | ||
| 918 | (unsigned char *) &data_size, 1); | ||
| 919 | |||
| 920 | /* This assumes that Emacs is not selecting for any other events | ||
| 921 | from the requestor! | ||
| 922 | |||
| 923 | If the holder of some manager selections (i.e. the settings | ||
| 924 | manager) asks Emacs for selection data, things will subtly go | ||
| 925 | wrong. */ | ||
| 926 | XSelectInput (dpyinfo->display, requestor, PropertyChangeMask); | ||
| 927 | x_stop_ignoring_errors (dpyinfo); | ||
| 928 | } | ||
| 929 | else | ||
| 930 | { | ||
| 931 | /* Write the property data now. */ | ||
| 932 | xdata = selection_data_for_offset (&transfer->data, | ||
| 933 | 0, &remaining); | ||
| 934 | eassert (remaining <= INT_MAX); | ||
| 935 | |||
| 936 | TRACE1 (" x_start_selection_transfer: writing" | ||
| 937 | " %zu elements directly to requestor window", | ||
| 938 | remaining); | ||
| 939 | |||
| 940 | x_ignore_errors_for_next_request (dpyinfo, 0); | ||
| 941 | XChangeProperty (dpyinfo->display, requestor, | ||
| 942 | transfer->data.property, | ||
| 943 | transfer->data.type, | ||
| 944 | transfer->data.format, | ||
| 945 | PropModeReplace, xdata, remaining); | ||
| 946 | x_stop_ignoring_errors (dpyinfo); | ||
| 947 | |||
| 948 | /* Next, get rid of the transfer. */ | ||
| 949 | x_cancel_selection_transfer (transfer); | ||
| 950 | } | ||
| 951 | } | ||
| 660 | 952 | ||
| 661 | bytes_remaining = cs->size; | 953 | /* Write out the next piece of data that is part of the specified |
| 662 | bytes_remaining *= cs->format >> 3; | 954 | selection transfer. If no more data remains to be written, write |
| 663 | if (bytes_remaining <= max_bytes) | 955 | the EOF property and complete the transfer. */ |
| 664 | { | 956 | |
| 665 | /* Send all the data at once, with minimal handshaking. */ | 957 | static void |
| 666 | TRACE1 ("Sending all %"pD"d bytes", bytes_remaining); | 958 | x_continue_selection_transfer (struct transfer *transfer) |
| 667 | XChangeProperty (display, window, cs->property, | 959 | { |
| 668 | cs->type, cs->format, PropModeReplace, | 960 | size_t remaining; |
| 669 | cs->data, cs->size); | 961 | unsigned char *xdata; |
| 670 | } | 962 | |
| 671 | else | 963 | xdata = selection_data_for_offset (&transfer->data, |
| 672 | { | 964 | transfer->offset, |
| 673 | /* Send an INCR tag to initiate incremental transfer. */ | 965 | &remaining); |
| 674 | long value[1]; | 966 | remaining = min (remaining, transfer->items_per_request); |
| 675 | 967 | ||
| 676 | TRACE2 ("Start sending %"pD"d bytes incrementally (%s)", | 968 | if (!remaining) |
| 677 | bytes_remaining, XGetAtomName (display, cs->property)); | 969 | { |
| 678 | cs->wait_object | 970 | /* The transfer is finished. Write zero-length property data to |
| 679 | = expect_property_change (display, window, cs->property, | 971 | signal EOF and remove the transfer. */ |
| 680 | PropertyDelete); | 972 | TRACE0 (" x_continue_selection_transfer: writing 0 items to" |
| 681 | 973 | " indicate EOF"); | |
| 682 | /* XChangeProperty expects an array of long even if long is | 974 | x_ignore_errors_for_next_request (transfer->dpyinfo, 0); |
| 683 | more than 32 bits. */ | 975 | XChangeProperty (transfer->dpyinfo->display, |
| 684 | value[0] = min (bytes_remaining, X_LONG_MAX); | 976 | transfer->requestor, |
| 685 | XChangeProperty (display, window, cs->property, | 977 | transfer->data.property, |
| 686 | dpyinfo->Xatom_INCR, 32, PropModeReplace, | 978 | transfer->data.type, |
| 687 | (unsigned char *) value, 1); | 979 | transfer->data.format, |
| 688 | XSelectInput (display, window, PropertyChangeMask); | 980 | PropModeReplace, |
| 689 | } | 981 | NULL, 0); |
| 982 | x_stop_ignoring_errors (transfer->dpyinfo); | ||
| 983 | TRACE0 (" x_continue_selection_transfer: done sending incrementally"); | ||
| 984 | |||
| 985 | x_cancel_selection_transfer (transfer); | ||
| 986 | } | ||
| 987 | else | ||
| 988 | { | ||
| 989 | TRACE2 (" x_continue_selection_transfer: writing %zu items" | ||
| 990 | "; current offset is %zu", remaining, transfer->offset); | ||
| 991 | eassert (remaining <= INT_MAX); | ||
| 992 | |||
| 993 | transfer->offset += remaining; | ||
| 994 | |||
| 995 | x_ignore_errors_for_next_request (transfer->dpyinfo, | ||
| 996 | transfer->serial); | ||
| 997 | XChangeProperty (transfer->dpyinfo->display, | ||
| 998 | transfer->requestor, | ||
| 999 | transfer->data.property, | ||
| 1000 | transfer->data.type, | ||
| 1001 | transfer->data.format, | ||
| 1002 | PropModeReplace, xdata, | ||
| 1003 | remaining); | ||
| 1004 | x_stop_ignoring_errors (transfer->dpyinfo); | ||
| 690 | } | 1005 | } |
| 1006 | } | ||
| 691 | 1007 | ||
| 692 | /* Now issue the SelectionNotify event. */ | 1008 | void |
| 693 | XSendEvent (display, window, False, 0, &reply_base); | 1009 | x_remove_selection_transfers (struct x_display_info *dpyinfo) |
| 694 | XFlush (display); | 1010 | { |
| 1011 | struct transfer *next, *last; | ||
| 695 | 1012 | ||
| 696 | #ifdef TRACE_SELECTION | 1013 | next = outstanding_transfers.next; |
| 697 | { | 1014 | while (next != &outstanding_transfers) |
| 698 | char *sel = XGetAtomName (display, reply->selection); | 1015 | { |
| 699 | char *tgt = XGetAtomName (display, reply->target); | 1016 | last = next; |
| 700 | TRACE3 ("Sent SelectionNotify: %s, target %s (%d)", | 1017 | next = next->next; |
| 701 | sel, tgt, ++x_reply_selection_request_cnt); | ||
| 702 | if (sel) XFree (sel); | ||
| 703 | if (tgt) XFree (tgt); | ||
| 704 | } | ||
| 705 | #endif /* TRACE_SELECTION */ | ||
| 706 | 1018 | ||
| 707 | /* Finish sending the rest of each of the INCR values. This should | 1019 | if (last->dpyinfo == dpyinfo) |
| 708 | be improved; there's a chance of deadlock if more than one | 1020 | x_cancel_selection_transfer (last); |
| 709 | subtarget in a MULTIPLE selection requires an INCR transfer, and | 1021 | } |
| 710 | the requestor and Emacs loop waiting on different transfers. */ | 1022 | } |
| 711 | for (cs = frame->converted_selections; cs; cs = cs->next) | ||
| 712 | if (cs->wait_object) | ||
| 713 | { | ||
| 714 | int format_bytes = cs->format / 8; | ||
| 715 | bool had_errors_p = x_had_errors_p (display); | ||
| 716 | 1023 | ||
| 717 | /* Must set this inside block_input (). unblock_input may read | 1024 | /* Handle an X error generated trying to write to a window. SERIAL |
| 718 | events and setting property_change_reply in | 1025 | identifies the outstanding incremental selection transfer, which is |
| 719 | wait_for_property_change is then too late. */ | 1026 | immediately removed. */ |
| 720 | set_property_change_object (cs->wait_object); | ||
| 721 | unblock_input (); | ||
| 722 | 1027 | ||
| 723 | bytes_remaining = cs->size; | 1028 | void |
| 724 | bytes_remaining *= format_bytes; | 1029 | x_handle_selection_error (unsigned int serial, XErrorEvent *error) |
| 1030 | { | ||
| 1031 | struct transfer *next, *last; | ||
| 725 | 1032 | ||
| 726 | /* Wait for the requestor to ack by deleting the property. | 1033 | if (error->error_code != BadWindow) |
| 727 | This can run Lisp code (process handlers) or signal. */ | 1034 | /* The error was not caused by the window going away. As such, |
| 728 | if (! had_errors_p) | 1035 | Emacs must deselect for PropertyChangeMask from the requestor |
| 729 | { | 1036 | window, which isn't safe here. Return and wait for the timeout |
| 730 | TRACE1 ("Waiting for ACK (deletion of %s)", | 1037 | to run. */ |
| 731 | XGetAtomName (display, cs->property)); | 1038 | return; |
| 732 | wait_for_property_change (cs->wait_object); | ||
| 733 | } | ||
| 734 | else | ||
| 735 | unexpect_property_change (cs->wait_object); | ||
| 736 | 1039 | ||
| 737 | while (bytes_remaining) | 1040 | next = outstanding_transfers.next; |
| 738 | { | 1041 | while (next != &outstanding_transfers) |
| 739 | int i = ((bytes_remaining < max_bytes) | 1042 | { |
| 740 | ? bytes_remaining | 1043 | last = next; |
| 741 | : max_bytes) / format_bytes; | 1044 | next = next->next; |
| 742 | block_input (); | ||
| 743 | |||
| 744 | cs->wait_object | ||
| 745 | = expect_property_change (display, window, cs->property, | ||
| 746 | PropertyDelete); | ||
| 747 | |||
| 748 | TRACE1 ("Sending increment of %d elements", i); | ||
| 749 | TRACE1 ("Set %s to increment data", | ||
| 750 | XGetAtomName (display, cs->property)); | ||
| 751 | |||
| 752 | /* Append the next chunk of data to the property. */ | ||
| 753 | XChangeProperty (display, window, cs->property, | ||
| 754 | cs->type, cs->format, PropModeAppend, | ||
| 755 | cs->data, i); | ||
| 756 | bytes_remaining -= i * format_bytes; | ||
| 757 | cs->data += i * ((cs->format == 32) ? sizeof (long) | ||
| 758 | : format_bytes); | ||
| 759 | XFlush (display); | ||
| 760 | had_errors_p = x_had_errors_p (display); | ||
| 761 | /* See comment above about property_change_reply. */ | ||
| 762 | set_property_change_object (cs->wait_object); | ||
| 763 | unblock_input (); | ||
| 764 | |||
| 765 | if (had_errors_p) break; | ||
| 766 | |||
| 767 | /* Wait for the requestor to ack this chunk by deleting | ||
| 768 | the property. This can run Lisp code or signal. */ | ||
| 769 | TRACE1 ("Waiting for increment ACK (deletion of %s)", | ||
| 770 | XGetAtomName (display, cs->property)); | ||
| 771 | wait_for_property_change (cs->wait_object); | ||
| 772 | } | ||
| 773 | 1045 | ||
| 774 | /* Now write a zero-length chunk to the property to tell the | 1046 | if (last->serial == serial) |
| 775 | requestor that we're done. */ | 1047 | { |
| 776 | block_input (); | 1048 | /* Clear SELECTED_EVENTS, so x_cancel_selection_transfer |
| 777 | if (! waiting_for_other_props_on_window (display, window)) | 1049 | will not make X requests. That is unsafe inside an error |
| 778 | XSelectInput (display, window, 0); | 1050 | handler, and unnecessary because the window has already |
| 779 | 1051 | gone. */ | |
| 780 | TRACE1 ("Set %s to a 0-length chunk to indicate EOF", | 1052 | last->flags &= ~SELECTED_EVENTS; |
| 781 | XGetAtomName (display, cs->property)); | 1053 | x_cancel_selection_transfer (last); |
| 782 | XChangeProperty (display, window, cs->property, | 1054 | } |
| 783 | cs->type, cs->format, PropModeReplace, | 1055 | } |
| 784 | cs->data, 0); | 1056 | } |
| 785 | TRACE0 ("Done sending incrementally"); | ||
| 786 | } | ||
| 787 | 1057 | ||
| 788 | /* rms, 2003-01-03: I think I have fixed this bug. */ | 1058 | /* Send the reply to a selection request event EVENT. */ |
| 789 | /* The window we're communicating with may have been deleted | 1059 | |
| 790 | in the meantime (that's a real situation from a bug report). | 1060 | static void |
| 791 | In this case, there may be events in the event queue still | 1061 | x_reply_selection_request (struct selection_input_event *event, |
| 792 | referring to the deleted window, and we'll get a BadWindow error | 1062 | struct x_display_info *dpyinfo) |
| 793 | in XTread_socket when processing the events. I don't have | 1063 | { |
| 794 | an idea how to fix that. gerd, 2001-01-98. */ | 1064 | XEvent message; |
| 795 | /* 2004-09-10: XSync and UNBLOCK so that possible protocol errors are | 1065 | struct selection_data *cs; |
| 796 | delivered before uncatch errors. */ | 1066 | struct x_selection_request *frame; |
| 797 | XSync (display, False); | ||
| 798 | unblock_input (); | ||
| 799 | 1067 | ||
| 800 | /* GTK queues events in addition to the queue in Xlib. So we | ||
| 801 | UNBLOCK to enter the event loop and get possible errors delivered, | ||
| 802 | and then BLOCK again because x_uncatch_errors requires it. */ | ||
| 803 | block_input (); | 1068 | block_input (); |
| 804 | /* This calls x_uncatch_errors. */ | 1069 | frame = selection_request_stack; |
| 805 | unbind_to (count, Qnil); | 1070 | |
| 1071 | message.xselection.type = SelectionNotify; | ||
| 1072 | message.xselection.display = dpyinfo->display; | ||
| 1073 | message.xselection.requestor = SELECTION_EVENT_REQUESTOR (event); | ||
| 1074 | message.xselection.selection = SELECTION_EVENT_SELECTION (event); | ||
| 1075 | message.xselection.time = SELECTION_EVENT_TIME (event); | ||
| 1076 | message.xselection.target = SELECTION_EVENT_TARGET (event); | ||
| 1077 | message.xselection.property = SELECTION_EVENT_PROPERTY (event); | ||
| 1078 | |||
| 1079 | if (message.xselection.property == None) | ||
| 1080 | message.xselection.property = message.xselection.target; | ||
| 1081 | |||
| 1082 | /* For each of the converted selections, start a write transfer from | ||
| 1083 | Emacs to the requestor. */ | ||
| 1084 | for (cs = frame->converted_selections; cs; cs = cs->next) | ||
| 1085 | x_start_selection_transfer (dpyinfo, | ||
| 1086 | SELECTION_EVENT_REQUESTOR (event), | ||
| 1087 | cs); | ||
| 1088 | |||
| 1089 | |||
| 1090 | /* Send the SelectionNotify event to the requestor, telling it that | ||
| 1091 | the property data has arrived. */ | ||
| 1092 | x_ignore_errors_for_next_request (dpyinfo, 0); | ||
| 1093 | XSendEvent (dpyinfo->display, SELECTION_EVENT_REQUESTOR (event), | ||
| 1094 | False, NoEventMask, &message); | ||
| 1095 | x_stop_ignoring_errors (dpyinfo); | ||
| 806 | unblock_input (); | 1096 | unblock_input (); |
| 807 | } | 1097 | } |
| 808 | 1098 | ||
| 809 | /* Handle a SelectionRequest event EVENT. | 1099 | /* Handle a SelectionRequest event EVENT. This is called from |
| 810 | This is called from keyboard.c when such an event is found in the queue. */ | 1100 | keyboard.c when such an event is found in the queue. */ |
| 811 | 1101 | ||
| 812 | static void | 1102 | static void |
| 813 | x_handle_selection_request (struct selection_input_event *event) | 1103 | x_handle_selection_request (struct selection_input_event *event) |
| @@ -892,7 +1182,9 @@ x_handle_selection_request (struct selection_input_event *event) | |||
| 892 | ptrdiff_t j, nselections; | 1182 | ptrdiff_t j, nselections; |
| 893 | struct selection_data cs; | 1183 | struct selection_data cs; |
| 894 | 1184 | ||
| 895 | if (property == None) goto DONE; | 1185 | if (property == None) |
| 1186 | goto DONE; | ||
| 1187 | |||
| 896 | multprop | 1188 | multprop |
| 897 | = x_get_window_property_as_lisp_data (dpyinfo, requestor, property, | 1189 | = x_get_window_property_as_lisp_data (dpyinfo, requestor, property, |
| 898 | QMULTIPLE, selection, true); | 1190 | QMULTIPLE, selection, true); |
| @@ -904,23 +1196,24 @@ x_handle_selection_request (struct selection_input_event *event) | |||
| 904 | /* Perform conversions. This can signal. */ | 1196 | /* Perform conversions. This can signal. */ |
| 905 | for (j = 0; j < nselections; j++) | 1197 | for (j = 0; j < nselections; j++) |
| 906 | { | 1198 | { |
| 907 | Lisp_Object subtarget = AREF (multprop, 2*j); | 1199 | Lisp_Object subtarget = AREF (multprop, 2 * j); |
| 908 | Atom subproperty = symbol_to_x_atom (dpyinfo, | 1200 | Atom subproperty = symbol_to_x_atom (dpyinfo, |
| 909 | AREF (multprop, 2*j+1)); | 1201 | AREF (multprop, 2*j+1)); |
| 910 | bool subsuccess = false; | 1202 | bool subsuccess = false; |
| 911 | 1203 | ||
| 912 | if (subproperty != None) | 1204 | if (subproperty != None) |
| 913 | subsuccess = x_convert_selection (selection_symbol, subtarget, | 1205 | subsuccess = x_convert_selection (selection_symbol, subtarget, |
| 914 | subproperty, true, dpyinfo, | 1206 | subproperty, dpyinfo, |
| 915 | use_alternate); | 1207 | use_alternate); |
| 916 | if (!subsuccess) | 1208 | if (!subsuccess) |
| 917 | ASET (multprop, 2*j+1, Qnil); | 1209 | ASET (multprop, 2 * j + 1, Qnil); |
| 918 | } | 1210 | } |
| 1211 | |||
| 919 | /* Save conversion results */ | 1212 | /* Save conversion results */ |
| 920 | lisp_data_to_selection_data (dpyinfo, multprop, &cs); | 1213 | lisp_data_to_selection_data (dpyinfo, multprop, &cs); |
| 921 | 1214 | ||
| 922 | /* If cs.type is ATOM, change it to ATOM_PAIR. This is because | 1215 | /* If cs.type is ATOM, change it to ATOM_PAIR. This is |
| 923 | the parameters to a MULTIPLE are ATOM_PAIRs. */ | 1216 | because the parameters to a MULTIPLE are ATOM_PAIRs. */ |
| 924 | 1217 | ||
| 925 | if (cs.type == XA_ATOM) | 1218 | if (cs.type == XA_ATOM) |
| 926 | cs.type = dpyinfo->Xatom_ATOM_PAIR; | 1219 | cs.type = dpyinfo->Xatom_ATOM_PAIR; |
| @@ -929,27 +1222,29 @@ x_handle_selection_request (struct selection_input_event *event) | |||
| 929 | cs.type, cs.format, PropModeReplace, | 1222 | cs.type, cs.format, PropModeReplace, |
| 930 | cs.data, cs.size); | 1223 | cs.data, cs.size); |
| 931 | success = true; | 1224 | success = true; |
| 1225 | |||
| 1226 | xfree (cs.data); | ||
| 932 | } | 1227 | } |
| 933 | else | 1228 | else |
| 934 | { | 1229 | { |
| 935 | if (property == None) | 1230 | if (property == None) |
| 936 | property = SELECTION_EVENT_TARGET (event); | 1231 | property = SELECTION_EVENT_TARGET (event); |
| 1232 | |||
| 937 | success = x_convert_selection (selection_symbol, | 1233 | success = x_convert_selection (selection_symbol, |
| 938 | target_symbol, property, | 1234 | target_symbol, property, |
| 939 | false, dpyinfo, | 1235 | dpyinfo, use_alternate); |
| 940 | use_alternate); | ||
| 941 | } | 1236 | } |
| 942 | 1237 | ||
| 943 | DONE: | 1238 | DONE: |
| 944 | 1239 | ||
| 945 | if (pushed) | ||
| 946 | selection_request_stack->converted = true; | ||
| 947 | |||
| 948 | if (success) | 1240 | if (success) |
| 949 | x_reply_selection_request (event, dpyinfo); | 1241 | x_reply_selection_request (event, dpyinfo); |
| 950 | else | 1242 | else |
| 951 | x_decline_selection_request (event); | 1243 | x_decline_selection_request (event); |
| 952 | 1244 | ||
| 1245 | if (pushed) | ||
| 1246 | selection_request_stack->converted = true; | ||
| 1247 | |||
| 953 | /* Run the `x-sent-selection-functions' abnormal hook. */ | 1248 | /* Run the `x-sent-selection-functions' abnormal hook. */ |
| 954 | if (!NILP (Vx_sent_selection_functions) | 1249 | if (!NILP (Vx_sent_selection_functions) |
| 955 | && !BASE_EQ (Vx_sent_selection_functions, Qunbound)) | 1250 | && !BASE_EQ (Vx_sent_selection_functions, Qunbound)) |
| @@ -960,19 +1255,18 @@ x_handle_selection_request (struct selection_input_event *event) | |||
| 960 | REALLY_DONE: | 1255 | REALLY_DONE: |
| 961 | 1256 | ||
| 962 | unbind_to (count, Qnil); | 1257 | unbind_to (count, Qnil); |
| 1258 | return; | ||
| 963 | } | 1259 | } |
| 964 | 1260 | ||
| 965 | /* Perform the requested selection conversion, and write the data to | 1261 | /* Perform the requested selection conversion, and write the data to |
| 966 | the converted_selections linked list, where it can be accessed by | 1262 | the converted_selections linked list, where it can be accessed by |
| 967 | x_reply_selection_request. If FOR_MULTIPLE, write out | 1263 | x_reply_selection_request. |
| 968 | the data even if conversion fails, using conversion_fail_tag. | ||
| 969 | 1264 | ||
| 970 | Return true if successful. */ | 1265 | Return true if successful. */ |
| 971 | 1266 | ||
| 972 | static bool | 1267 | static bool |
| 973 | x_convert_selection (Lisp_Object selection_symbol, | 1268 | x_convert_selection (Lisp_Object selection_symbol, Lisp_Object target_symbol, |
| 974 | Lisp_Object target_symbol, Atom property, | 1269 | Atom property, struct x_display_info *dpyinfo, |
| 975 | bool for_multiple, struct x_display_info *dpyinfo, | ||
| 976 | bool use_alternate) | 1270 | bool use_alternate) |
| 977 | { | 1271 | { |
| 978 | Lisp_Object lisp_selection; | 1272 | Lisp_Object lisp_selection; |
| @@ -988,33 +1282,16 @@ x_convert_selection (Lisp_Object selection_symbol, | |||
| 988 | /* A nil return value means we can't perform the conversion. */ | 1282 | /* A nil return value means we can't perform the conversion. */ |
| 989 | if (NILP (lisp_selection) | 1283 | if (NILP (lisp_selection) |
| 990 | || (CONSP (lisp_selection) && NILP (XCDR (lisp_selection)))) | 1284 | || (CONSP (lisp_selection) && NILP (XCDR (lisp_selection)))) |
| 991 | { | 1285 | return false; |
| 992 | if (for_multiple) | ||
| 993 | { | ||
| 994 | cs = xmalloc (sizeof *cs); | ||
| 995 | cs->data = ((unsigned char *) | ||
| 996 | &selection_request_stack->conversion_fail_tag); | ||
| 997 | cs->size = 1; | ||
| 998 | cs->format = 32; | ||
| 999 | cs->type = XA_ATOM; | ||
| 1000 | cs->nofree = true; | ||
| 1001 | cs->property = property; | ||
| 1002 | cs->wait_object = NULL; | ||
| 1003 | cs->next = frame->converted_selections; | ||
| 1004 | frame->converted_selections = cs; | ||
| 1005 | } | ||
| 1006 | |||
| 1007 | return false; | ||
| 1008 | } | ||
| 1009 | 1286 | ||
| 1010 | /* Otherwise, record the converted selection to binary. */ | 1287 | /* Otherwise, record the converted selection to binary. */ |
| 1011 | cs = xmalloc (sizeof *cs); | 1288 | cs = xmalloc (sizeof *cs); |
| 1012 | cs->data = NULL; | 1289 | cs->data = NULL; |
| 1013 | cs->nofree = true; | 1290 | cs->string = Qnil; |
| 1014 | cs->property = property; | 1291 | cs->property = property; |
| 1015 | cs->wait_object = NULL; | ||
| 1016 | cs->next = frame->converted_selections; | 1292 | cs->next = frame->converted_selections; |
| 1017 | frame->converted_selections = cs; | 1293 | frame->converted_selections = cs; |
| 1294 | |||
| 1018 | lisp_data_to_selection_data (dpyinfo, lisp_selection, cs); | 1295 | lisp_data_to_selection_data (dpyinfo, lisp_selection, cs); |
| 1019 | return true; | 1296 | return true; |
| 1020 | } | 1297 | } |
| @@ -1129,14 +1406,14 @@ x_clear_frame_selections (struct frame *f) | |||
| 1129 | while (CONSP (t->Vselection_alist) | 1406 | while (CONSP (t->Vselection_alist) |
| 1130 | && EQ (frame, XCAR (XCDR (XCDR (XCDR (XCAR (t->Vselection_alist))))))) | 1407 | && EQ (frame, XCAR (XCDR (XCDR (XCDR (XCAR (t->Vselection_alist))))))) |
| 1131 | { | 1408 | { |
| 1132 | selection = Fcar (Fcar (t->Vselection_alist)); | 1409 | selection = CAR (CAR (t->Vselection_alist)); |
| 1133 | 1410 | ||
| 1134 | if (!x_should_preserve_selection (selection)) | 1411 | if (!x_should_preserve_selection (selection)) |
| 1135 | /* Run the `x-lost-selection-functions' abnormal hook. */ | 1412 | /* Run the `x-lost-selection-functions' abnormal hook. */ |
| 1136 | CALLN (Frun_hook_with_args, Qx_lost_selection_functions, | 1413 | CALLN (Frun_hook_with_args, Qx_lost_selection_functions, |
| 1137 | selection); | 1414 | selection); |
| 1138 | else | 1415 | else |
| 1139 | lost = Fcons (Fcar (t->Vselection_alist), lost); | 1416 | lost = Fcons (CAR (t->Vselection_alist), lost); |
| 1140 | 1417 | ||
| 1141 | tset_selection_alist (t, XCDR (t->Vselection_alist)); | 1418 | tset_selection_alist (t, XCDR (t->Vselection_alist)); |
| 1142 | } | 1419 | } |
| @@ -1274,6 +1551,10 @@ void | |||
| 1274 | x_handle_property_notify (const XPropertyEvent *event) | 1551 | x_handle_property_notify (const XPropertyEvent *event) |
| 1275 | { | 1552 | { |
| 1276 | struct prop_location *rest; | 1553 | struct prop_location *rest; |
| 1554 | struct transfer *next; | ||
| 1555 | #ifdef TRACE_SELECTION | ||
| 1556 | char *name; | ||
| 1557 | #endif | ||
| 1277 | 1558 | ||
| 1278 | for (rest = property_change_wait_list; rest; rest = rest->next) | 1559 | for (rest = property_change_wait_list; rest; rest = rest->next) |
| 1279 | { | 1560 | { |
| @@ -1283,9 +1564,16 @@ x_handle_property_notify (const XPropertyEvent *event) | |||
| 1283 | && rest->display == event->display | 1564 | && rest->display == event->display |
| 1284 | && rest->desired_state == event->state) | 1565 | && rest->desired_state == event->state) |
| 1285 | { | 1566 | { |
| 1567 | #ifdef TRACE_SELECTION | ||
| 1568 | name = XGetAtomName (event->display, event->atom); | ||
| 1569 | |||
| 1286 | TRACE2 ("Expected %s of property %s", | 1570 | TRACE2 ("Expected %s of property %s", |
| 1287 | (event->state == PropertyDelete ? "deletion" : "change"), | 1571 | (event->state == PropertyDelete ? "deletion" : "change"), |
| 1288 | XGetAtomName (event->display, event->atom)); | 1572 | name ? name : "unknown"); |
| 1573 | |||
| 1574 | if (name) | ||
| 1575 | XFree (name); | ||
| 1576 | #endif | ||
| 1289 | 1577 | ||
| 1290 | rest->arrived = true; | 1578 | rest->arrived = true; |
| 1291 | 1579 | ||
| @@ -1297,6 +1585,26 @@ x_handle_property_notify (const XPropertyEvent *event) | |||
| 1297 | return; | 1585 | return; |
| 1298 | } | 1586 | } |
| 1299 | } | 1587 | } |
| 1588 | |||
| 1589 | /* Look for a property change for an outstanding selection | ||
| 1590 | transfer. */ | ||
| 1591 | next = outstanding_transfers.next; | ||
| 1592 | while (next != &outstanding_transfers) | ||
| 1593 | { | ||
| 1594 | if (next->dpyinfo->display == event->display | ||
| 1595 | && next->requestor == event->window | ||
| 1596 | && next->data.property == event->atom | ||
| 1597 | && event->state == PropertyDelete) | ||
| 1598 | { | ||
| 1599 | TRACE1 ("Expected PropertyDelete event arrived from the" | ||
| 1600 | " requestor window %lx", next->requestor); | ||
| 1601 | |||
| 1602 | x_continue_selection_transfer (next); | ||
| 1603 | return; | ||
| 1604 | } | ||
| 1605 | |||
| 1606 | next = next->next; | ||
| 1607 | } | ||
| 1300 | } | 1608 | } |
| 1301 | 1609 | ||
| 1302 | static void | 1610 | static void |
| @@ -1450,10 +1758,10 @@ x_get_window_property (Display *display, Window window, Atom property, | |||
| 1450 | /* Maximum value for TOTAL_SIZE. It cannot exceed PTRDIFF_MAX - 1 | 1758 | /* Maximum value for TOTAL_SIZE. It cannot exceed PTRDIFF_MAX - 1 |
| 1451 | and SIZE_MAX - 1, for an extra byte at the end. And it cannot | 1759 | and SIZE_MAX - 1, for an extra byte at the end. And it cannot |
| 1452 | exceed LONG_MAX * X_LONG_SIZE, for XGetWindowProperty. */ | 1760 | exceed LONG_MAX * X_LONG_SIZE, for XGetWindowProperty. */ |
| 1453 | ptrdiff_t total_size_max = | 1761 | ptrdiff_t total_size_max |
| 1454 | ((min (PTRDIFF_MAX, SIZE_MAX) - 1) / x_long_size < LONG_MAX | 1762 | = ((min (PTRDIFF_MAX, SIZE_MAX) - 1) / x_long_size < LONG_MAX |
| 1455 | ? min (PTRDIFF_MAX, SIZE_MAX) - 1 | 1763 | ? min (PTRDIFF_MAX, SIZE_MAX) - 1 |
| 1456 | : LONG_MAX * x_long_size); | 1764 | : LONG_MAX * x_long_size); |
| 1457 | 1765 | ||
| 1458 | block_input (); | 1766 | block_input (); |
| 1459 | 1767 | ||
| @@ -1946,10 +2254,14 @@ static void | |||
| 1946 | lisp_data_to_selection_data (struct x_display_info *dpyinfo, | 2254 | lisp_data_to_selection_data (struct x_display_info *dpyinfo, |
| 1947 | Lisp_Object obj, struct selection_data *cs) | 2255 | Lisp_Object obj, struct selection_data *cs) |
| 1948 | { | 2256 | { |
| 1949 | Lisp_Object type = Qnil; | 2257 | Lisp_Object type; |
| 2258 | char **name_buffer; | ||
| 2259 | |||
| 2260 | USE_SAFE_ALLOCA; | ||
| 2261 | |||
| 2262 | type = Qnil; | ||
| 1950 | 2263 | ||
| 1951 | eassert (cs != NULL); | 2264 | eassert (cs != NULL); |
| 1952 | cs->nofree = false; | ||
| 1953 | 2265 | ||
| 1954 | if (CONSP (obj) && SYMBOLP (XCAR (obj))) | 2266 | if (CONSP (obj) && SYMBOLP (XCAR (obj))) |
| 1955 | { | 2267 | { |
| @@ -1959,8 +2271,10 @@ lisp_data_to_selection_data (struct x_display_info *dpyinfo, | |||
| 1959 | obj = XCAR (obj); | 2271 | obj = XCAR (obj); |
| 1960 | } | 2272 | } |
| 1961 | 2273 | ||
| 2274 | /* This is not the same as declining. */ | ||
| 2275 | |||
| 1962 | if (EQ (obj, QNULL) || (EQ (type, QNULL))) | 2276 | if (EQ (obj, QNULL) || (EQ (type, QNULL))) |
| 1963 | { /* This is not the same as declining */ | 2277 | { |
| 1964 | cs->format = 32; | 2278 | cs->format = 32; |
| 1965 | cs->size = 0; | 2279 | cs->size = 0; |
| 1966 | cs->data = NULL; | 2280 | cs->data = NULL; |
| @@ -1971,12 +2285,14 @@ lisp_data_to_selection_data (struct x_display_info *dpyinfo, | |||
| 1971 | if (SCHARS (obj) < SBYTES (obj)) | 2285 | if (SCHARS (obj) < SBYTES (obj)) |
| 1972 | /* OBJ is a multibyte string containing a non-ASCII char. */ | 2286 | /* OBJ is a multibyte string containing a non-ASCII char. */ |
| 1973 | signal_error ("Non-ASCII string must be encoded in advance", obj); | 2287 | signal_error ("Non-ASCII string must be encoded in advance", obj); |
| 2288 | |||
| 1974 | if (NILP (type)) | 2289 | if (NILP (type)) |
| 1975 | type = QSTRING; | 2290 | type = QSTRING; |
| 2291 | |||
| 1976 | cs->format = 8; | 2292 | cs->format = 8; |
| 1977 | cs->size = SBYTES (obj); | 2293 | cs->size = -1; |
| 1978 | cs->data = SDATA (obj); | 2294 | cs->data = NULL; |
| 1979 | cs->nofree = true; | 2295 | cs->string = obj; |
| 1980 | } | 2296 | } |
| 1981 | else if (SYMBOLP (obj)) | 2297 | else if (SYMBOLP (obj)) |
| 1982 | { | 2298 | { |
| @@ -2048,8 +2364,19 @@ lisp_data_to_selection_data (struct x_display_info *dpyinfo, | |||
| 2048 | x_atoms = data; | 2364 | x_atoms = data; |
| 2049 | cs->format = 32; | 2365 | cs->format = 32; |
| 2050 | cs->size = size; | 2366 | cs->size = size; |
| 2051 | for (i = 0; i < size; i++) | 2367 | |
| 2052 | x_atoms[i] = symbol_to_x_atom (dpyinfo, AREF (obj, i)); | 2368 | if (size == 1) |
| 2369 | x_atoms[0] = symbol_to_x_atom (dpyinfo, AREF (obj, i)); | ||
| 2370 | else | ||
| 2371 | { | ||
| 2372 | SAFE_NALLOCA (name_buffer, sizeof *x_atoms, size); | ||
| 2373 | |||
| 2374 | for (i = 0; i < size; i++) | ||
| 2375 | name_buffer[i] = SSDATA (SYMBOL_NAME (AREF (obj, i))); | ||
| 2376 | |||
| 2377 | x_intern_atoms (dpyinfo, name_buffer, size, | ||
| 2378 | x_atoms); | ||
| 2379 | } | ||
| 2053 | } | 2380 | } |
| 2054 | else | 2381 | else |
| 2055 | /* This vector is an INTEGER set, or something like it */ | 2382 | /* This vector is an INTEGER set, or something like it */ |
| @@ -2091,6 +2418,8 @@ lisp_data_to_selection_data (struct x_display_info *dpyinfo, | |||
| 2091 | signal_error (/* Qselection_error */ "Unrecognized selection data", obj); | 2418 | signal_error (/* Qselection_error */ "Unrecognized selection data", obj); |
| 2092 | 2419 | ||
| 2093 | cs->type = symbol_to_x_atom (dpyinfo, type); | 2420 | cs->type = symbol_to_x_atom (dpyinfo, type); |
| 2421 | |||
| 2422 | SAFE_FREE (); | ||
| 2094 | } | 2423 | } |
| 2095 | 2424 | ||
| 2096 | static Lisp_Object | 2425 | static Lisp_Object |
| @@ -2618,8 +2947,8 @@ x_check_property_data (Lisp_Object data) | |||
| 2618 | XClientMessageEvent). */ | 2947 | XClientMessageEvent). */ |
| 2619 | 2948 | ||
| 2620 | void | 2949 | void |
| 2621 | x_fill_property_data (Display *dpy, Lisp_Object data, void *ret, | 2950 | x_fill_property_data (struct x_display_info *dpyinfo, Lisp_Object data, |
| 2622 | int nelements_max, int format) | 2951 | void *ret, int nelements_max, int format) |
| 2623 | { | 2952 | { |
| 2624 | unsigned long val; | 2953 | unsigned long val; |
| 2625 | unsigned long *d32 = (unsigned long *) ret; | 2954 | unsigned long *d32 = (unsigned long *) ret; |
| @@ -2654,7 +2983,7 @@ x_fill_property_data (Display *dpy, Lisp_Object data, void *ret, | |||
| 2654 | else if (STRINGP (o)) | 2983 | else if (STRINGP (o)) |
| 2655 | { | 2984 | { |
| 2656 | block_input (); | 2985 | block_input (); |
| 2657 | val = XInternAtom (dpy, SSDATA (o), False); | 2986 | val = x_intern_cached_atom (dpyinfo, SSDATA (o), false); |
| 2658 | unblock_input (); | 2987 | unblock_input (); |
| 2659 | } | 2988 | } |
| 2660 | else | 2989 | else |
| @@ -2942,7 +3271,7 @@ x_send_client_event (Lisp_Object display, Lisp_Object dest, Lisp_Object from, | |||
| 2942 | 3271 | ||
| 2943 | memset (event.xclient.data.l, 0, sizeof (event.xclient.data.l)); | 3272 | memset (event.xclient.data.l, 0, sizeof (event.xclient.data.l)); |
| 2944 | /* event.xclient.data can hold 20 chars, 10 shorts, or 5 longs. */ | 3273 | /* event.xclient.data can hold 20 chars, 10 shorts, or 5 longs. */ |
| 2945 | x_fill_property_data (dpyinfo->display, values, event.xclient.data.b, | 3274 | x_fill_property_data (dpyinfo, values, event.xclient.data.b, |
| 2946 | 5 * 32 / event.xclient.format, | 3275 | 5 * 32 / event.xclient.format, |
| 2947 | event.xclient.format); | 3276 | event.xclient.format); |
| 2948 | 3277 | ||
| @@ -3002,10 +3331,11 @@ syms_of_xselect (void) | |||
| 3002 | 3331 | ||
| 3003 | reading_selection_reply = Fcons (Qnil, Qnil); | 3332 | reading_selection_reply = Fcons (Qnil, Qnil); |
| 3004 | staticpro (&reading_selection_reply); | 3333 | staticpro (&reading_selection_reply); |
| 3005 | |||
| 3006 | staticpro (&property_change_reply); | 3334 | staticpro (&property_change_reply); |
| 3007 | 3335 | ||
| 3008 | /* FIXME: Duplicate definition in nsselect.c. */ | 3336 | outstanding_transfers.next = &outstanding_transfers; |
| 3337 | outstanding_transfers.last = &outstanding_transfers; | ||
| 3338 | |||
| 3009 | DEFVAR_LISP ("selection-converter-alist", Vselection_converter_alist, | 3339 | DEFVAR_LISP ("selection-converter-alist", Vselection_converter_alist, |
| 3010 | doc: /* An alist associating X Windows selection-types with functions. | 3340 | doc: /* An alist associating X Windows selection-types with functions. |
| 3011 | These functions are called to convert the selection, with three args: | 3341 | These functions are called to convert the selection, with three args: |
| @@ -3120,9 +3450,43 @@ Note that this does not affect setting or owning selections. */); | |||
| 3120 | static void | 3450 | static void |
| 3121 | syms_of_xselect_for_pdumper (void) | 3451 | syms_of_xselect_for_pdumper (void) |
| 3122 | { | 3452 | { |
| 3453 | outstanding_transfers.next = &outstanding_transfers; | ||
| 3454 | outstanding_transfers.last = &outstanding_transfers; | ||
| 3455 | |||
| 3123 | reading_selection_window = 0; | 3456 | reading_selection_window = 0; |
| 3124 | reading_which_selection = 0; | 3457 | reading_which_selection = 0; |
| 3125 | property_change_wait_list = 0; | 3458 | property_change_wait_list = 0; |
| 3126 | prop_location_identifier = 0; | 3459 | prop_location_identifier = 0; |
| 3127 | property_change_reply = Fcons (Qnil, Qnil); | 3460 | property_change_reply = Fcons (Qnil, Qnil); |
| 3128 | } | 3461 | } |
| 3462 | |||
| 3463 | void | ||
| 3464 | mark_xselect (void) | ||
| 3465 | { | ||
| 3466 | struct transfer *next; | ||
| 3467 | struct x_selection_request *frame; | ||
| 3468 | struct selection_data *cs; | ||
| 3469 | |||
| 3470 | /* Mark all the strings being used as selection data. A string that | ||
| 3471 | is still reachable is always reachable via either the selection | ||
| 3472 | request stack or the list of outstanding transfers. */ | ||
| 3473 | |||
| 3474 | next = outstanding_transfers.next; | ||
| 3475 | |||
| 3476 | if (!next) | ||
| 3477 | /* syms_of_xselect has not yet been called. */ | ||
| 3478 | return; | ||
| 3479 | |||
| 3480 | while (next != &outstanding_transfers) | ||
| 3481 | { | ||
| 3482 | mark_object (next->data.string); | ||
| 3483 | next = next->next; | ||
| 3484 | } | ||
| 3485 | |||
| 3486 | frame = selection_request_stack; | ||
| 3487 | for (; frame; frame = frame->last) | ||
| 3488 | { | ||
| 3489 | for (cs = frame->converted_selections; cs; cs = cs->next) | ||
| 3490 | mark_object (cs->string); | ||
| 3491 | } | ||
| 3492 | } | ||
diff --git a/src/xterm.c b/src/xterm.c index cfd8c385d1d..60d48165650 100644 --- a/src/xterm.c +++ b/src/xterm.c | |||
| @@ -26,6 +26,22 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 26 | contains subroutines comprising the redisplay interface, setting up | 26 | contains subroutines comprising the redisplay interface, setting up |
| 27 | scroll bars and widgets, and handling input. | 27 | scroll bars and widgets, and handling input. |
| 28 | 28 | ||
| 29 | X WINDOW SYSTEM | ||
| 30 | |||
| 31 | The X Window System is a windowing system for bitmap graphics | ||
| 32 | displays which originated at MIT in 1984. Version 11, which is | ||
| 33 | currently supported by Emacs, first appeared in September 1987. | ||
| 34 | |||
| 35 | X has a long history and has been developed by many different | ||
| 36 | organizations over the years; at present, it is being primarily | ||
| 37 | developed by the X.Org Foundation. It is the main window system | ||
| 38 | that Emacs is developed and tested against, and X version 10 was | ||
| 39 | the first window system that Emacs was ported to. As a consequence | ||
| 40 | of its age and wide availability, X contains many idiosyncrasies, | ||
| 41 | but that has not prevented it from becoming the dominant free | ||
| 42 | window system, and the platform of reference for all GUI code in | ||
| 43 | Emacs. | ||
| 44 | |||
| 29 | Some of what is explained below also applies to the other window | 45 | Some of what is explained below also applies to the other window |
| 30 | systems that Emacs supports, to varying degrees. YMMV. | 46 | systems that Emacs supports, to varying degrees. YMMV. |
| 31 | 47 | ||
| @@ -555,7 +571,56 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 555 | drop happening with the primary selection and synthetic button | 571 | drop happening with the primary selection and synthetic button |
| 556 | events (see `x_dnd_do_unsupported_drop'). That function implements | 572 | events (see `x_dnd_do_unsupported_drop'). That function implements |
| 557 | the OffiX drag-and-drop protocol by default. See | 573 | the OffiX drag-and-drop protocol by default. See |
| 558 | `x-dnd-handle-unsupported-drop' in `x-dnd.el' for more details. */ | 574 | `x-dnd-handle-unsupported-drop' in `x-dnd.el' for more details. |
| 575 | |||
| 576 | DISPLAY ERROR HANDLING | ||
| 577 | |||
| 578 | While error handling under X was originally designed solely as a | ||
| 579 | mechanism for the X server to report fatal errors to clients, most | ||
| 580 | clients (including Emacs) have adopted a system of "error traps" to | ||
| 581 | handle or discard these errors as they arrive. Discarding errors is | ||
| 582 | usually necessary when Emacs performs an X request that might fail: | ||
| 583 | for example, sending a message to a window that may no longer exist, | ||
| 584 | or might not exist at all. Handling errors is then necessary when | ||
| 585 | the detailed error must be reported to another piece of code: for | ||
| 586 | example, as a Lisp error. | ||
| 587 | |||
| 588 | It is not acceptable for Emacs to crash when it is sent invalid data | ||
| 589 | by another client, or by Lisp. As a result, errors must be caught | ||
| 590 | around Xlib functions generating requests containing resource | ||
| 591 | identifiers that could potentially be invalid, such as window or | ||
| 592 | atom identifiers provided in a client message from another program, | ||
| 593 | or a child window ID obtained through XTranslateCoordinates that may | ||
| 594 | refer to a window that has been deleted in the meantime. | ||
| 595 | |||
| 596 | There are two sets of functions used to perform this "error | ||
| 597 | trapping". Which one should be used depends on what kind of | ||
| 598 | processing must be done on the error. The first consists of the | ||
| 599 | functions `x_ignore_errors_for_next_request' and | ||
| 600 | `x_stop_ignoring_errors', which ignore errors generated by requests | ||
| 601 | made in between a call to the first function and a corresponding | ||
| 602 | call to the second. They should be used for simple asynchronous | ||
| 603 | requests that do not require a reply from the X server: using them | ||
| 604 | instead of the second set improves performance, as they simply | ||
| 605 | record a range of request serials to ignore errors from, instead of | ||
| 606 | synchronizing with the X server to handle errors. | ||
| 607 | |||
| 608 | The second set consists of the following functions: | ||
| 609 | |||
| 610 | - x_catch_errors_with_handler | ||
| 611 | - x_catch_errors | ||
| 612 | - x_uncatch_errors_after_check | ||
| 613 | - x_uncatch_errors | ||
| 614 | - x_check_errors | ||
| 615 | - x_had_errors_p | ||
| 616 | - x_clear_errors | ||
| 617 | |||
| 618 | Callers using this set should consult the comment(s) on top of the | ||
| 619 | aformentioned functions. They should not be used when the requests | ||
| 620 | being made do not require roundtrips to the X server, and obtaining | ||
| 621 | the details of any error generated is unecessary, as | ||
| 622 | `x_uncatch_errors' will always synchronize with the X server, which | ||
| 623 | is a potentially slow operation. */ | ||
| 559 | 624 | ||
| 560 | #include <config.h> | 625 | #include <config.h> |
| 561 | #include <stdlib.h> | 626 | #include <stdlib.h> |
| @@ -574,7 +639,6 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 574 | #ifdef USE_XCB | 639 | #ifdef USE_XCB |
| 575 | #include <xcb/xproto.h> | 640 | #include <xcb/xproto.h> |
| 576 | #include <xcb/xcb.h> | 641 | #include <xcb/xcb.h> |
| 577 | #include <xcb/xcb_aux.h> | ||
| 578 | #endif | 642 | #endif |
| 579 | 643 | ||
| 580 | /* If we have Xfixes extension, use it for pointer blanking. */ | 644 | /* If we have Xfixes extension, use it for pointer blanking. */ |
| @@ -1052,6 +1116,20 @@ static const struct x_atom_ref x_atom_refs[] = | |||
| 1052 | /* Old OffiX (a.k.a. old KDE) drop protocol support. */ | 1116 | /* Old OffiX (a.k.a. old KDE) drop protocol support. */ |
| 1053 | ATOM_REFS_INIT ("DndProtocol", Xatom_DndProtocol) | 1117 | ATOM_REFS_INIT ("DndProtocol", Xatom_DndProtocol) |
| 1054 | ATOM_REFS_INIT ("_DND_PROTOCOL", Xatom_DND_PROTOCOL) | 1118 | ATOM_REFS_INIT ("_DND_PROTOCOL", Xatom_DND_PROTOCOL) |
| 1119 | /* Here are some atoms that are not actually used from C, just | ||
| 1120 | defined to make replying to selection requests fast. */ | ||
| 1121 | ATOM_REFS_INIT ("text/plain;charset=utf-8", Xatom_text_plain_charset_utf_8) | ||
| 1122 | ATOM_REFS_INIT ("LENGTH", Xatom_LENGTH) | ||
| 1123 | ATOM_REFS_INIT ("FILE_NAME", Xatom_FILE_NAME) | ||
| 1124 | ATOM_REFS_INIT ("CHARACTER_POSITION", Xatom_CHARACTER_POSITION) | ||
| 1125 | ATOM_REFS_INIT ("LINE_NUMBER", Xatom_LINE_NUMBER) | ||
| 1126 | ATOM_REFS_INIT ("COLUMN_NUMBER", Xatom_COLUMN_NUMBER) | ||
| 1127 | ATOM_REFS_INIT ("OWNER_OS", Xatom_OWNER_OS) | ||
| 1128 | ATOM_REFS_INIT ("HOST_NAME", Xatom_HOST_NAME) | ||
| 1129 | ATOM_REFS_INIT ("USER", Xatom_USER) | ||
| 1130 | ATOM_REFS_INIT ("CLASS", Xatom_CLASS) | ||
| 1131 | ATOM_REFS_INIT ("NAME", Xatom_NAME) | ||
| 1132 | ATOM_REFS_INIT ("SAVE_TARGETS", Xatom_SAVE_TARGETS) | ||
| 1055 | }; | 1133 | }; |
| 1056 | 1134 | ||
| 1057 | enum | 1135 | enum |
| @@ -2509,7 +2587,7 @@ xm_send_drop_message (struct x_display_info *dpyinfo, Window source, | |||
| 2509 | *((uint32_t *) &msg.xclient.data.b[12]) = dmsg->index_atom; | 2587 | *((uint32_t *) &msg.xclient.data.b[12]) = dmsg->index_atom; |
| 2510 | *((uint32_t *) &msg.xclient.data.b[16]) = dmsg->source_window; | 2588 | *((uint32_t *) &msg.xclient.data.b[16]) = dmsg->source_window; |
| 2511 | 2589 | ||
| 2512 | x_ignore_errors_for_next_request (dpyinfo); | 2590 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 2513 | XSendEvent (dpyinfo->display, target, False, NoEventMask, &msg); | 2591 | XSendEvent (dpyinfo->display, target, False, NoEventMask, &msg); |
| 2514 | x_stop_ignoring_errors (dpyinfo); | 2592 | x_stop_ignoring_errors (dpyinfo); |
| 2515 | } | 2593 | } |
| @@ -2536,7 +2614,7 @@ xm_send_top_level_enter_message (struct x_display_info *dpyinfo, Window source, | |||
| 2536 | msg.xclient.data.b[18] = 0; | 2614 | msg.xclient.data.b[18] = 0; |
| 2537 | msg.xclient.data.b[19] = 0; | 2615 | msg.xclient.data.b[19] = 0; |
| 2538 | 2616 | ||
| 2539 | x_ignore_errors_for_next_request (dpyinfo); | 2617 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 2540 | XSendEvent (dpyinfo->display, target, False, NoEventMask, &msg); | 2618 | XSendEvent (dpyinfo->display, target, False, NoEventMask, &msg); |
| 2541 | x_stop_ignoring_errors (dpyinfo); | 2619 | x_stop_ignoring_errors (dpyinfo); |
| 2542 | } | 2620 | } |
| @@ -2567,7 +2645,7 @@ xm_send_drag_motion_message (struct x_display_info *dpyinfo, Window source, | |||
| 2567 | msg.xclient.data.b[18] = 0; | 2645 | msg.xclient.data.b[18] = 0; |
| 2568 | msg.xclient.data.b[19] = 0; | 2646 | msg.xclient.data.b[19] = 0; |
| 2569 | 2647 | ||
| 2570 | x_ignore_errors_for_next_request (dpyinfo); | 2648 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 2571 | XSendEvent (dpyinfo->display, target, False, NoEventMask, &msg); | 2649 | XSendEvent (dpyinfo->display, target, False, NoEventMask, &msg); |
| 2572 | x_stop_ignoring_errors (dpyinfo); | 2650 | x_stop_ignoring_errors (dpyinfo); |
| 2573 | } | 2651 | } |
| @@ -2626,7 +2704,7 @@ xm_send_top_level_leave_message (struct x_display_info *dpyinfo, Window source, | |||
| 2626 | msg.xclient.data.b[18] = 0; | 2704 | msg.xclient.data.b[18] = 0; |
| 2627 | msg.xclient.data.b[19] = 0; | 2705 | msg.xclient.data.b[19] = 0; |
| 2628 | 2706 | ||
| 2629 | x_ignore_errors_for_next_request (dpyinfo); | 2707 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 2630 | XSendEvent (dpyinfo->display, target, False, NoEventMask, &msg); | 2708 | XSendEvent (dpyinfo->display, target, False, NoEventMask, &msg); |
| 2631 | x_stop_ignoring_errors (dpyinfo); | 2709 | x_stop_ignoring_errors (dpyinfo); |
| 2632 | } | 2710 | } |
| @@ -2921,7 +2999,7 @@ x_dnd_free_toplevels (bool display_alive) | |||
| 2921 | if (n_windows) | 2999 | if (n_windows) |
| 2922 | { | 3000 | { |
| 2923 | eassume (dpyinfo); | 3001 | eassume (dpyinfo); |
| 2924 | x_ignore_errors_for_next_request (dpyinfo); | 3002 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 2925 | 3003 | ||
| 2926 | for (i = 0; i < n_windows; ++i) | 3004 | for (i = 0; i < n_windows; ++i) |
| 2927 | { | 3005 | { |
| @@ -3058,7 +3136,7 @@ x_dnd_compute_toplevels (struct x_display_info *dpyinfo) | |||
| 3058 | 0, 0); | 3136 | 0, 0); |
| 3059 | get_property_cookies[i] | 3137 | get_property_cookies[i] |
| 3060 | = xcb_get_property (dpyinfo->xcb_connection, 0, (xcb_window_t) toplevels[i], | 3138 | = xcb_get_property (dpyinfo->xcb_connection, 0, (xcb_window_t) toplevels[i], |
| 3061 | (xcb_atom_t) dpyinfo->Xatom_wm_state, XCB_ATOM_ANY, | 3139 | (xcb_atom_t) dpyinfo->Xatom_wm_state, 0, |
| 3062 | 0, 2); | 3140 | 0, 2); |
| 3063 | xm_property_cookies[i] | 3141 | xm_property_cookies[i] |
| 3064 | = xcb_get_property (dpyinfo->xcb_connection, 0, (xcb_window_t) toplevels[i], | 3142 | = xcb_get_property (dpyinfo->xcb_connection, 0, (xcb_window_t) toplevels[i], |
| @@ -3069,7 +3147,7 @@ x_dnd_compute_toplevels (struct x_display_info *dpyinfo) | |||
| 3069 | = xcb_get_property (dpyinfo->xcb_connection, 0, | 3147 | = xcb_get_property (dpyinfo->xcb_connection, 0, |
| 3070 | (xcb_window_t) toplevels[i], | 3148 | (xcb_window_t) toplevels[i], |
| 3071 | (xcb_atom_t) dpyinfo->Xatom_net_frame_extents, | 3149 | (xcb_atom_t) dpyinfo->Xatom_net_frame_extents, |
| 3072 | XCB_ATOM_CARDINAL, 0, 4); | 3150 | XA_CARDINAL, 0, 4); |
| 3073 | get_geometry_cookies[i] | 3151 | get_geometry_cookies[i] |
| 3074 | = xcb_get_geometry (dpyinfo->xcb_connection, (xcb_window_t) toplevels[i]); | 3152 | = xcb_get_geometry (dpyinfo->xcb_connection, (xcb_window_t) toplevels[i]); |
| 3075 | 3153 | ||
| @@ -3197,7 +3275,7 @@ x_dnd_compute_toplevels (struct x_display_info *dpyinfo) | |||
| 3197 | { | 3275 | { |
| 3198 | if (xcb_get_property_value_length (extent_property_reply) == 16 | 3276 | if (xcb_get_property_value_length (extent_property_reply) == 16 |
| 3199 | && extent_property_reply->format == 32 | 3277 | && extent_property_reply->format == 32 |
| 3200 | && extent_property_reply->type == XCB_ATOM_CARDINAL) | 3278 | && extent_property_reply->type == XA_CARDINAL) |
| 3201 | { | 3279 | { |
| 3202 | fextents = xcb_get_property_value (extent_property_reply); | 3280 | fextents = xcb_get_property_value (extent_property_reply); |
| 3203 | frame_extents[0] = fextents[0]; | 3281 | frame_extents[0] = fextents[0]; |
| @@ -3291,7 +3369,7 @@ x_dnd_compute_toplevels (struct x_display_info *dpyinfo) | |||
| 3291 | 3369 | ||
| 3292 | if (dpyinfo->xshape_supported_p) | 3370 | if (dpyinfo->xshape_supported_p) |
| 3293 | { | 3371 | { |
| 3294 | x_ignore_errors_for_next_request (dpyinfo); | 3372 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 3295 | XShapeSelectInput (dpyinfo->display, | 3373 | XShapeSelectInput (dpyinfo->display, |
| 3296 | toplevels[i], | 3374 | toplevels[i], |
| 3297 | ShapeNotifyMask); | 3375 | ShapeNotifyMask); |
| @@ -3456,7 +3534,7 @@ x_dnd_compute_toplevels (struct x_display_info *dpyinfo) | |||
| 3456 | } | 3534 | } |
| 3457 | #endif | 3535 | #endif |
| 3458 | 3536 | ||
| 3459 | x_ignore_errors_for_next_request (dpyinfo); | 3537 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 3460 | XSelectInput (dpyinfo->display, toplevels[i], | 3538 | XSelectInput (dpyinfo->display, toplevels[i], |
| 3461 | (attrs.your_event_mask | 3539 | (attrs.your_event_mask |
| 3462 | | StructureNotifyMask | 3540 | | StructureNotifyMask |
| @@ -3571,13 +3649,13 @@ x_dnd_get_proxy_proto (struct x_display_info *dpyinfo, Window wdesc, | |||
| 3571 | xdnd_proxy_cookie = xcb_get_property (dpyinfo->xcb_connection, 0, | 3649 | xdnd_proxy_cookie = xcb_get_property (dpyinfo->xcb_connection, 0, |
| 3572 | (xcb_window_t) wdesc, | 3650 | (xcb_window_t) wdesc, |
| 3573 | (xcb_atom_t) dpyinfo->Xatom_XdndProxy, | 3651 | (xcb_atom_t) dpyinfo->Xatom_XdndProxy, |
| 3574 | XCB_ATOM_WINDOW, 0, 1); | 3652 | XA_WINDOW, 0, 1); |
| 3575 | 3653 | ||
| 3576 | if (proto_out) | 3654 | if (proto_out) |
| 3577 | xdnd_proto_cookie = xcb_get_property (dpyinfo->xcb_connection, 0, | 3655 | xdnd_proto_cookie = xcb_get_property (dpyinfo->xcb_connection, 0, |
| 3578 | (xcb_window_t) wdesc, | 3656 | (xcb_window_t) wdesc, |
| 3579 | (xcb_atom_t) dpyinfo->Xatom_XdndAware, | 3657 | (xcb_atom_t) dpyinfo->Xatom_XdndAware, |
| 3580 | XCB_ATOM_ATOM, 0, 1); | 3658 | XA_ATOM, 0, 1); |
| 3581 | 3659 | ||
| 3582 | if (proxy_out) | 3660 | if (proxy_out) |
| 3583 | { | 3661 | { |
| @@ -3589,7 +3667,7 @@ x_dnd_get_proxy_proto (struct x_display_info *dpyinfo, Window wdesc, | |||
| 3589 | else | 3667 | else |
| 3590 | { | 3668 | { |
| 3591 | if (reply->format == 32 | 3669 | if (reply->format == 32 |
| 3592 | && reply->type == XCB_ATOM_WINDOW | 3670 | && reply->type == XA_WINDOW |
| 3593 | && (xcb_get_property_value_length (reply) >= 4)) | 3671 | && (xcb_get_property_value_length (reply) >= 4)) |
| 3594 | *proxy_out = *(xcb_window_t *) xcb_get_property_value (reply); | 3672 | *proxy_out = *(xcb_window_t *) xcb_get_property_value (reply); |
| 3595 | 3673 | ||
| @@ -3607,7 +3685,7 @@ x_dnd_get_proxy_proto (struct x_display_info *dpyinfo, Window wdesc, | |||
| 3607 | else | 3685 | else |
| 3608 | { | 3686 | { |
| 3609 | if (reply->format == 32 | 3687 | if (reply->format == 32 |
| 3610 | && reply->type == XCB_ATOM_ATOM | 3688 | && reply->type == XA_ATOM |
| 3611 | && (xcb_get_property_value_length (reply) >= 4)) | 3689 | && (xcb_get_property_value_length (reply) >= 4)) |
| 3612 | *proto_out = (int) *(xcb_atom_t *) xcb_get_property_value (reply); | 3690 | *proto_out = (int) *(xcb_atom_t *) xcb_get_property_value (reply); |
| 3613 | 3691 | ||
| @@ -3791,15 +3869,15 @@ x_dnd_get_wm_state_and_proto (struct x_display_info *dpyinfo, | |||
| 3791 | wmstate_cookie = xcb_get_property (dpyinfo->xcb_connection, 0, | 3869 | wmstate_cookie = xcb_get_property (dpyinfo->xcb_connection, 0, |
| 3792 | (xcb_window_t) window, | 3870 | (xcb_window_t) window, |
| 3793 | (xcb_atom_t) dpyinfo->Xatom_wm_state, | 3871 | (xcb_atom_t) dpyinfo->Xatom_wm_state, |
| 3794 | XCB_ATOM_ANY, 0, 2); | 3872 | 0, 0, 2); |
| 3795 | xdnd_proto_cookie = xcb_get_property (dpyinfo->xcb_connection, 0, | 3873 | xdnd_proto_cookie = xcb_get_property (dpyinfo->xcb_connection, 0, |
| 3796 | (xcb_window_t) window, | 3874 | (xcb_window_t) window, |
| 3797 | (xcb_atom_t) dpyinfo->Xatom_XdndAware, | 3875 | (xcb_atom_t) dpyinfo->Xatom_XdndAware, |
| 3798 | XCB_ATOM_ATOM, 0, 1); | 3876 | XA_ATOM, 0, 1); |
| 3799 | xdnd_proxy_cookie = xcb_get_property (dpyinfo->xcb_connection, 0, | 3877 | xdnd_proxy_cookie = xcb_get_property (dpyinfo->xcb_connection, 0, |
| 3800 | (xcb_window_t) window, | 3878 | (xcb_window_t) window, |
| 3801 | (xcb_atom_t) dpyinfo->Xatom_XdndProxy, | 3879 | (xcb_atom_t) dpyinfo->Xatom_XdndProxy, |
| 3802 | XCB_ATOM_WINDOW, 0, 1); | 3880 | XA_WINDOW, 0, 1); |
| 3803 | xm_style_cookie = xcb_get_property (dpyinfo->xcb_connection, 0, | 3881 | xm_style_cookie = xcb_get_property (dpyinfo->xcb_connection, 0, |
| 3804 | (xcb_window_t) window, | 3882 | (xcb_window_t) window, |
| 3805 | (xcb_atom_t) dpyinfo->Xatom_MOTIF_DRAG_RECEIVER_INFO, | 3883 | (xcb_atom_t) dpyinfo->Xatom_MOTIF_DRAG_RECEIVER_INFO, |
| @@ -3846,7 +3924,7 @@ x_dnd_get_wm_state_and_proto (struct x_display_info *dpyinfo, | |||
| 3846 | else | 3924 | else |
| 3847 | { | 3925 | { |
| 3848 | if (reply->format == 32 | 3926 | if (reply->format == 32 |
| 3849 | && reply->type == XCB_ATOM_WINDOW | 3927 | && reply->type == XA_WINDOW |
| 3850 | && (xcb_get_property_value_length (reply) >= 4)) | 3928 | && (xcb_get_property_value_length (reply) >= 4)) |
| 3851 | *proxy_out = *(xcb_window_t *) xcb_get_property_value (reply); | 3929 | *proxy_out = *(xcb_window_t *) xcb_get_property_value (reply); |
| 3852 | 3930 | ||
| @@ -3962,6 +4040,12 @@ x_dnd_do_unsupported_drop (struct x_display_info *dpyinfo, | |||
| 3962 | if (owner != FRAME_X_WINDOW (f)) | 4040 | if (owner != FRAME_X_WINDOW (f)) |
| 3963 | return; | 4041 | return; |
| 3964 | 4042 | ||
| 4043 | /* mouse-drag-and-drop-region will immediately deactivate the mark | ||
| 4044 | after this is set. Make sure the primary selection is not | ||
| 4045 | clobbered in that case by setting `deactivate-mark' to | ||
| 4046 | Qdont_save. */ | ||
| 4047 | Vdeactivate_mark = Qdont_save; | ||
| 4048 | |||
| 3965 | event.xbutton.window = child; | 4049 | event.xbutton.window = child; |
| 3966 | event.xbutton.subwindow = None; | 4050 | event.xbutton.subwindow = None; |
| 3967 | event.xbutton.x = dest_x; | 4051 | event.xbutton.x = dest_x; |
| @@ -3975,7 +4059,7 @@ x_dnd_do_unsupported_drop (struct x_display_info *dpyinfo, | |||
| 3975 | event.xbutton.type = ButtonPress; | 4059 | event.xbutton.type = ButtonPress; |
| 3976 | event.xbutton.time = before + 1; | 4060 | event.xbutton.time = before + 1; |
| 3977 | 4061 | ||
| 3978 | x_ignore_errors_for_next_request (dpyinfo); | 4062 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 3979 | XSendEvent (dpyinfo->display, child, | 4063 | XSendEvent (dpyinfo->display, child, |
| 3980 | True, ButtonPressMask, &event); | 4064 | True, ButtonPressMask, &event); |
| 3981 | 4065 | ||
| @@ -4487,7 +4571,7 @@ x_dnd_send_enter (struct frame *f, Window target, Window toplevel, | |||
| 4487 | so we don't have to set it again. */ | 4571 | so we don't have to set it again. */ |
| 4488 | x_dnd_init_type_lists = true; | 4572 | x_dnd_init_type_lists = true; |
| 4489 | 4573 | ||
| 4490 | x_ignore_errors_for_next_request (dpyinfo); | 4574 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 4491 | XSendEvent (FRAME_X_DISPLAY (f), target, False, NoEventMask, &msg); | 4575 | XSendEvent (FRAME_X_DISPLAY (f), target, False, NoEventMask, &msg); |
| 4492 | x_stop_ignoring_errors (dpyinfo); | 4576 | x_stop_ignoring_errors (dpyinfo); |
| 4493 | } | 4577 | } |
| @@ -4559,7 +4643,7 @@ x_dnd_send_position (struct frame *f, Window target, Window toplevel, | |||
| 4559 | return; | 4643 | return; |
| 4560 | } | 4644 | } |
| 4561 | 4645 | ||
| 4562 | x_ignore_errors_for_next_request (dpyinfo); | 4646 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 4563 | XSendEvent (FRAME_X_DISPLAY (f), target, False, NoEventMask, &msg); | 4647 | XSendEvent (FRAME_X_DISPLAY (f), target, False, NoEventMask, &msg); |
| 4564 | x_stop_ignoring_errors (dpyinfo); | 4648 | x_stop_ignoring_errors (dpyinfo); |
| 4565 | 4649 | ||
| @@ -4586,7 +4670,7 @@ x_dnd_send_leave (struct frame *f, Window target, Window toplevel) | |||
| 4586 | x_dnd_waiting_for_status_window = None; | 4670 | x_dnd_waiting_for_status_window = None; |
| 4587 | x_dnd_pending_send_position.type = 0; | 4671 | x_dnd_pending_send_position.type = 0; |
| 4588 | 4672 | ||
| 4589 | x_ignore_errors_for_next_request (dpyinfo); | 4673 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 4590 | XSendEvent (FRAME_X_DISPLAY (f), target, False, NoEventMask, &msg); | 4674 | XSendEvent (FRAME_X_DISPLAY (f), target, False, NoEventMask, &msg); |
| 4591 | x_stop_ignoring_errors (dpyinfo); | 4675 | x_stop_ignoring_errors (dpyinfo); |
| 4592 | } | 4676 | } |
| @@ -4619,7 +4703,7 @@ x_dnd_send_drop (struct frame *f, Window target, Window toplevel, | |||
| 4619 | if (supported >= 1) | 4703 | if (supported >= 1) |
| 4620 | msg.xclient.data.l[2] = timestamp; | 4704 | msg.xclient.data.l[2] = timestamp; |
| 4621 | 4705 | ||
| 4622 | x_ignore_errors_for_next_request (dpyinfo); | 4706 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 4623 | XSendEvent (FRAME_X_DISPLAY (f), target, False, NoEventMask, &msg); | 4707 | XSendEvent (FRAME_X_DISPLAY (f), target, False, NoEventMask, &msg); |
| 4624 | x_stop_ignoring_errors (dpyinfo); | 4708 | x_stop_ignoring_errors (dpyinfo); |
| 4625 | return true; | 4709 | return true; |
| @@ -6731,7 +6815,7 @@ x_set_frame_alpha (struct frame *f) | |||
| 6731 | Do this unconditionally as this function is called on reparent when | 6815 | Do this unconditionally as this function is called on reparent when |
| 6732 | alpha has not changed on the frame. */ | 6816 | alpha has not changed on the frame. */ |
| 6733 | 6817 | ||
| 6734 | x_ignore_errors_for_next_request (dpyinfo); | 6818 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 6735 | 6819 | ||
| 6736 | if (!FRAME_PARENT_FRAME (f)) | 6820 | if (!FRAME_PARENT_FRAME (f)) |
| 6737 | { | 6821 | { |
| @@ -6907,6 +6991,7 @@ static void | |||
| 6907 | x_sync_wait_for_frame_drawn_event (struct frame *f) | 6991 | x_sync_wait_for_frame_drawn_event (struct frame *f) |
| 6908 | { | 6992 | { |
| 6909 | XEvent event; | 6993 | XEvent event; |
| 6994 | struct x_display_info *dpyinfo; | ||
| 6910 | 6995 | ||
| 6911 | if (!FRAME_X_WAITING_FOR_DRAW (f) | 6996 | if (!FRAME_X_WAITING_FOR_DRAW (f) |
| 6912 | /* The compositing manager can't draw a frame if it is | 6997 | /* The compositing manager can't draw a frame if it is |
| @@ -6914,21 +6999,42 @@ x_sync_wait_for_frame_drawn_event (struct frame *f) | |||
| 6914 | || !FRAME_VISIBLE_P (f)) | 6999 | || !FRAME_VISIBLE_P (f)) |
| 6915 | return; | 7000 | return; |
| 6916 | 7001 | ||
| 7002 | dpyinfo = FRAME_DISPLAY_INFO (f); | ||
| 7003 | |||
| 6917 | /* Wait for the frame drawn message to arrive. */ | 7004 | /* Wait for the frame drawn message to arrive. */ |
| 6918 | if (x_if_event (FRAME_X_DISPLAY (f), &event, | 7005 | if (x_if_event (FRAME_X_DISPLAY (f), &event, |
| 6919 | x_sync_is_frame_drawn_event, (XPointer) f, | 7006 | x_sync_is_frame_drawn_event, (XPointer) f, |
| 6920 | make_timespec (1, 0))) | 7007 | make_timespec (1, 0))) |
| 6921 | { | 7008 | { |
| 6922 | /* TODO: display this warning in the echo area. */ | 7009 | /* The first time a draw hangs, treat it as a random fluctuation |
| 6923 | fprintf (stderr, "Warning: compositing manager spent more than 1 second " | 7010 | on the part of the compositor. If the next draw continues to |
| 6924 | "drawing a frame. Frame synchronization has been disabled\n"); | 7011 | hang, disable frame synchronization. */ |
| 6925 | FRAME_X_OUTPUT (f)->use_vsync_p = false; | 7012 | if (FRAME_X_DRAW_JUST_HUNG (f)) |
| 7013 | { | ||
| 7014 | fprintf (stderr, "Warning: compositing manager spent more than 1 " | ||
| 7015 | "second drawing a frame. Frame synchronization has " | ||
| 7016 | "been disabled\n"); | ||
| 7017 | FRAME_X_OUTPUT (f)->use_vsync_p = false; | ||
| 7018 | |||
| 7019 | /* Remove the compositor bypass property from the outer | ||
| 7020 | window. */ | ||
| 7021 | XDeleteProperty (dpyinfo->display, FRAME_OUTER_WINDOW (f), | ||
| 7022 | dpyinfo->Xatom_net_wm_bypass_compositor); | ||
| 6926 | 7023 | ||
| 6927 | /* Also change the frame parameter to reflect the new state. */ | 7024 | /* Also change the frame parameter to reflect the new |
| 6928 | store_frame_param (f, Quse_frame_synchronization, Qnil); | 7025 | state. */ |
| 7026 | store_frame_param (f, Quse_frame_synchronization, Qnil); | ||
| 7027 | } | ||
| 7028 | else | ||
| 7029 | { | ||
| 7030 | fprintf (stderr, "Warning: compositing manager spent more than 1 " | ||
| 7031 | "second drawing a frame. Frame synchronization will be " | ||
| 7032 | "disabled if this happens again\n"); | ||
| 7033 | FRAME_X_DRAW_JUST_HUNG (f) = true; | ||
| 7034 | } | ||
| 6929 | } | 7035 | } |
| 6930 | else | 7036 | else |
| 6931 | x_sync_note_frame_times (FRAME_DISPLAY_INFO (f), f, &event); | 7037 | x_sync_note_frame_times (dpyinfo, f, &event); |
| 6932 | 7038 | ||
| 6933 | FRAME_X_WAITING_FOR_DRAW (f) = false; | 7039 | FRAME_X_WAITING_FOR_DRAW (f) = false; |
| 6934 | } | 7040 | } |
| @@ -7128,8 +7234,26 @@ static void | |||
| 7128 | x_sync_handle_frame_drawn (struct x_display_info *dpyinfo, | 7234 | x_sync_handle_frame_drawn (struct x_display_info *dpyinfo, |
| 7129 | XEvent *message, struct frame *f) | 7235 | XEvent *message, struct frame *f) |
| 7130 | { | 7236 | { |
| 7237 | XSyncValue value, counter; | ||
| 7238 | |||
| 7131 | if (FRAME_OUTER_WINDOW (f) == message->xclient.window) | 7239 | if (FRAME_OUTER_WINDOW (f) == message->xclient.window) |
| 7132 | FRAME_X_WAITING_FOR_DRAW (f) = false; | 7240 | { |
| 7241 | counter = FRAME_X_COUNTER_VALUE (f); | ||
| 7242 | |||
| 7243 | /* Check that the counter in the message is the same as the | ||
| 7244 | counter in the frame. */ | ||
| 7245 | XSyncIntsToValue (&value, | ||
| 7246 | message->xclient.data.l[0] & 0xffffffff, | ||
| 7247 | message->xclient.data.l[1] & 0xffffffff); | ||
| 7248 | |||
| 7249 | if (XSyncValueEqual (value, counter)) | ||
| 7250 | FRAME_X_WAITING_FOR_DRAW (f) = false; | ||
| 7251 | |||
| 7252 | /* As long as a _NET_WM_FRAME_DRAWN message arrives, we know | ||
| 7253 | that the compositor is still sending events, so avoid timing | ||
| 7254 | out. */ | ||
| 7255 | FRAME_X_DRAW_JUST_HUNG (f) = false; | ||
| 7256 | } | ||
| 7133 | 7257 | ||
| 7134 | x_sync_note_frame_times (dpyinfo, f, message); | 7258 | x_sync_note_frame_times (dpyinfo, f, message); |
| 7135 | } | 7259 | } |
| @@ -10959,6 +11083,31 @@ x_clear_frame (struct frame *f) | |||
| 10959 | unblock_input (); | 11083 | unblock_input (); |
| 10960 | } | 11084 | } |
| 10961 | 11085 | ||
| 11086 | /* Send a message to frame F telling the event loop to track whether | ||
| 11087 | or not an hourglass is being displayed. This is required to ignore | ||
| 11088 | the right events when the hourglass is mapped without callig XSync | ||
| 11089 | after displaying or hiding the hourglass. */ | ||
| 11090 | |||
| 11091 | static void | ||
| 11092 | x_send_hourglass_message (struct frame *f, bool hourglass_enabled) | ||
| 11093 | { | ||
| 11094 | struct x_display_info *dpyinfo; | ||
| 11095 | XEvent msg; | ||
| 11096 | |||
| 11097 | dpyinfo = FRAME_DISPLAY_INFO (f); | ||
| 11098 | memset (&msg, 0, sizeof msg); | ||
| 11099 | |||
| 11100 | msg.xclient.type = ClientMessage; | ||
| 11101 | msg.xclient.message_type | ||
| 11102 | = dpyinfo->Xatom_EMACS_TMP; | ||
| 11103 | msg.xclient.format = 8; | ||
| 11104 | msg.xclient.window = FRAME_X_WINDOW (f); | ||
| 11105 | msg.xclient.data.b[0] = hourglass_enabled ? 1 : 0; | ||
| 11106 | |||
| 11107 | XSendEvent (dpyinfo->display, FRAME_X_WINDOW (f), | ||
| 11108 | False, NoEventMask, &msg); | ||
| 11109 | } | ||
| 11110 | |||
| 10962 | /* RIF: Show hourglass cursor on frame F. */ | 11111 | /* RIF: Show hourglass cursor on frame F. */ |
| 10963 | 11112 | ||
| 10964 | static void | 11113 | static void |
| @@ -10979,14 +11128,14 @@ x_show_hourglass (struct frame *f) | |||
| 10979 | if (popup_activated ()) | 11128 | if (popup_activated ()) |
| 10980 | return; | 11129 | return; |
| 10981 | 11130 | ||
| 11131 | x_send_hourglass_message (f, true); | ||
| 11132 | |||
| 10982 | #ifdef USE_X_TOOLKIT | 11133 | #ifdef USE_X_TOOLKIT |
| 10983 | if (x->widget) | 11134 | if (x->widget) |
| 10984 | #else | 11135 | #else |
| 10985 | if (FRAME_OUTER_WINDOW (f)) | 11136 | if (FRAME_OUTER_WINDOW (f)) |
| 10986 | #endif | 11137 | #endif |
| 10987 | { | 11138 | { |
| 10988 | x->hourglass_p = true; | ||
| 10989 | |||
| 10990 | if (!x->hourglass_window) | 11139 | if (!x->hourglass_window) |
| 10991 | { | 11140 | { |
| 10992 | #ifndef USE_XCB | 11141 | #ifndef USE_XCB |
| @@ -11053,15 +11202,11 @@ x_hide_hourglass (struct frame *f) | |||
| 11053 | { | 11202 | { |
| 11054 | #ifndef USE_XCB | 11203 | #ifndef USE_XCB |
| 11055 | XUnmapWindow (FRAME_X_DISPLAY (f), x->hourglass_window); | 11204 | XUnmapWindow (FRAME_X_DISPLAY (f), x->hourglass_window); |
| 11056 | /* Sync here because XTread_socket looks at the | ||
| 11057 | hourglass_p flag that is reset to zero below. */ | ||
| 11058 | XSync (FRAME_X_DISPLAY (f), False); | ||
| 11059 | #else | 11205 | #else |
| 11060 | xcb_unmap_window (FRAME_DISPLAY_INFO (f)->xcb_connection, | 11206 | xcb_unmap_window (FRAME_DISPLAY_INFO (f)->xcb_connection, |
| 11061 | (xcb_window_t) x->hourglass_window); | 11207 | (xcb_window_t) x->hourglass_window); |
| 11062 | xcb_aux_sync (FRAME_DISPLAY_INFO (f)->xcb_connection); | ||
| 11063 | #endif | 11208 | #endif |
| 11064 | x->hourglass_p = false; | 11209 | x_send_hourglass_message (f, false); |
| 11065 | } | 11210 | } |
| 11066 | } | 11211 | } |
| 11067 | 11212 | ||
| @@ -11185,21 +11330,32 @@ XTflash (struct frame *f) | |||
| 11185 | static void | 11330 | static void |
| 11186 | XTring_bell (struct frame *f) | 11331 | XTring_bell (struct frame *f) |
| 11187 | { | 11332 | { |
| 11188 | if (FRAME_X_DISPLAY (f)) | 11333 | struct x_display_info *dpyinfo; |
| 11334 | |||
| 11335 | if (!FRAME_X_DISPLAY (f)) | ||
| 11336 | return; | ||
| 11337 | |||
| 11338 | dpyinfo = FRAME_DISPLAY_INFO (f); | ||
| 11339 | |||
| 11340 | if (visible_bell) | ||
| 11341 | XTflash (f); | ||
| 11342 | else | ||
| 11189 | { | 11343 | { |
| 11190 | if (visible_bell) | 11344 | /* When Emacs is untrusted, Bell requests sometimes generate |
| 11191 | XTflash (f); | 11345 | Access errors. This is not in the security extension |
| 11192 | else | 11346 | specification but seems to be a bug in the X consortium XKB |
| 11193 | { | 11347 | implementation. */ |
| 11194 | block_input (); | 11348 | |
| 11349 | block_input (); | ||
| 11350 | x_ignore_errors_for_next_request (dpyinfo, 0); | ||
| 11195 | #ifdef HAVE_XKB | 11351 | #ifdef HAVE_XKB |
| 11196 | XkbBell (FRAME_X_DISPLAY (f), None, 0, None); | 11352 | XkbBell (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), 0, None); |
| 11197 | #else | 11353 | #else |
| 11198 | XBell (FRAME_X_DISPLAY (f), 0); | 11354 | XBell (FRAME_X_DISPLAY (f), 0); |
| 11199 | #endif | 11355 | #endif |
| 11200 | XFlush (FRAME_X_DISPLAY (f)); | 11356 | XFlush (FRAME_X_DISPLAY (f)); |
| 11201 | unblock_input (); | 11357 | x_stop_ignoring_errors (dpyinfo); |
| 11202 | } | 11358 | unblock_input (); |
| 11203 | } | 11359 | } |
| 11204 | } | 11360 | } |
| 11205 | 11361 | ||
| @@ -11445,7 +11601,7 @@ x_frame_highlight (struct frame *f) | |||
| 11445 | the window-manager in use, tho something more is at play since I've been | 11601 | the window-manager in use, tho something more is at play since I've been |
| 11446 | using that same window-manager binary for ever. Let's not crash just | 11602 | using that same window-manager binary for ever. Let's not crash just |
| 11447 | because of this (bug#9310). */ | 11603 | because of this (bug#9310). */ |
| 11448 | x_ignore_errors_for_next_request (dpyinfo); | 11604 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 11449 | XSetWindowBorder (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), | 11605 | XSetWindowBorder (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), |
| 11450 | f->output_data.x->border_pixel); | 11606 | f->output_data.x->border_pixel); |
| 11451 | x_stop_ignoring_errors (dpyinfo); | 11607 | x_stop_ignoring_errors (dpyinfo); |
| @@ -11468,7 +11624,7 @@ x_frame_unhighlight (struct frame *f) | |||
| 11468 | 11624 | ||
| 11469 | block_input (); | 11625 | block_input (); |
| 11470 | /* Same as above for XSetWindowBorder (bug#9310). */ | 11626 | /* Same as above for XSetWindowBorder (bug#9310). */ |
| 11471 | x_ignore_errors_for_next_request (dpyinfo); | 11627 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 11472 | XSetWindowBorderPixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), | 11628 | XSetWindowBorderPixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), |
| 11473 | f->output_data.x->border_tile); | 11629 | f->output_data.x->border_tile); |
| 11474 | x_stop_ignoring_errors (dpyinfo); | 11630 | x_stop_ignoring_errors (dpyinfo); |
| @@ -11532,7 +11688,7 @@ x_new_focus_frame (struct x_display_info *dpyinfo, struct frame *frame) | |||
| 11532 | x_frame_rehighlight (dpyinfo); | 11688 | x_frame_rehighlight (dpyinfo); |
| 11533 | } | 11689 | } |
| 11534 | 11690 | ||
| 11535 | #ifdef HAVE_XFIXES | 11691 | #if defined HAVE_XFIXES && XFIXES_VERSION >= 40000 |
| 11536 | 11692 | ||
| 11537 | /* True if the display in DPYINFO supports a version of Xfixes | 11693 | /* True if the display in DPYINFO supports a version of Xfixes |
| 11538 | sufficient for pointer blanking. */ | 11694 | sufficient for pointer blanking. */ |
| @@ -11544,11 +11700,12 @@ x_fixes_pointer_blanking_supported (struct x_display_info *dpyinfo) | |||
| 11544 | && dpyinfo->xfixes_major >= 4); | 11700 | && dpyinfo->xfixes_major >= 4); |
| 11545 | } | 11701 | } |
| 11546 | 11702 | ||
| 11547 | #endif /* HAVE_XFIXES */ | 11703 | #endif /* HAVE_XFIXES && XFIXES_VERSION >= 40000 */ |
| 11548 | 11704 | ||
| 11549 | /* Toggle mouse pointer visibility on frame F using the XFixes | 11705 | /* Toggle mouse pointer visibility on frame F using the XFixes |
| 11550 | extension. */ | 11706 | extension. */ |
| 11551 | #ifdef HAVE_XFIXES | 11707 | #if defined HAVE_XFIXES && XFIXES_VERSION >= 40000 |
| 11708 | |||
| 11552 | static void | 11709 | static void |
| 11553 | xfixes_toggle_visible_pointer (struct frame *f, bool invisible) | 11710 | xfixes_toggle_visible_pointer (struct frame *f, bool invisible) |
| 11554 | 11711 | ||
| @@ -11559,6 +11716,7 @@ xfixes_toggle_visible_pointer (struct frame *f, bool invisible) | |||
| 11559 | XFixesShowCursor (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f)); | 11716 | XFixesShowCursor (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f)); |
| 11560 | f->pointer_invisible = invisible; | 11717 | f->pointer_invisible = invisible; |
| 11561 | } | 11718 | } |
| 11719 | |||
| 11562 | #endif /* HAVE_XFIXES */ | 11720 | #endif /* HAVE_XFIXES */ |
| 11563 | 11721 | ||
| 11564 | /* Create invisible cursor on the X display referred by DPYINFO. */ | 11722 | /* Create invisible cursor on the X display referred by DPYINFO. */ |
| @@ -11607,7 +11765,7 @@ x_toggle_visible_pointer (struct frame *f, bool invisible) | |||
| 11607 | if (dpyinfo->invisible_cursor == None) | 11765 | if (dpyinfo->invisible_cursor == None) |
| 11608 | dpyinfo->invisible_cursor = make_invisible_cursor (dpyinfo); | 11766 | dpyinfo->invisible_cursor = make_invisible_cursor (dpyinfo); |
| 11609 | 11767 | ||
| 11610 | #ifndef HAVE_XFIXES | 11768 | #if !defined HAVE_XFIXES || XFIXES_VERSION < 40000 |
| 11611 | if (dpyinfo->invisible_cursor == None) | 11769 | if (dpyinfo->invisible_cursor == None) |
| 11612 | invisible = false; | 11770 | invisible = false; |
| 11613 | #else | 11771 | #else |
| @@ -11640,7 +11798,7 @@ static void | |||
| 11640 | XTtoggle_invisible_pointer (struct frame *f, bool invisible) | 11798 | XTtoggle_invisible_pointer (struct frame *f, bool invisible) |
| 11641 | { | 11799 | { |
| 11642 | block_input (); | 11800 | block_input (); |
| 11643 | #ifdef HAVE_XFIXES | 11801 | #if defined HAVE_XFIXES && XFIXES_VERSION >= 40000 |
| 11644 | if (FRAME_DISPLAY_INFO (f)->fixes_pointer_blanking | 11802 | if (FRAME_DISPLAY_INFO (f)->fixes_pointer_blanking |
| 11645 | && x_fixes_pointer_blanking_supported (FRAME_DISPLAY_INFO (f))) | 11803 | && x_fixes_pointer_blanking_supported (FRAME_DISPLAY_INFO (f))) |
| 11646 | xfixes_toggle_visible_pointer (f, invisible); | 11804 | xfixes_toggle_visible_pointer (f, invisible); |
| @@ -12229,6 +12387,13 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, | |||
| 12229 | struct xi_device_t *device; | 12387 | struct xi_device_t *device; |
| 12230 | #endif | 12388 | #endif |
| 12231 | 12389 | ||
| 12390 | if (FRAME_DISPLAY_INFO (f)->untrusted) | ||
| 12391 | /* Untrusted clients cannot send messages to trusted clients or | ||
| 12392 | read the window tree, so drag and drop will likely not work at | ||
| 12393 | all. */ | ||
| 12394 | error ("Drag-and-drop is not possible when the client is" | ||
| 12395 | " not trusted by the X server."); | ||
| 12396 | |||
| 12232 | base = SPECPDL_INDEX (); | 12397 | base = SPECPDL_INDEX (); |
| 12233 | 12398 | ||
| 12234 | /* Bind this here to avoid juggling bindings and SAFE_FREE in | 12399 | /* Bind this here to avoid juggling bindings and SAFE_FREE in |
| @@ -15000,9 +15165,7 @@ x_send_scroll_bar_event (Lisp_Object window, enum scroll_bar_part part, | |||
| 15000 | XClientMessageEvent *ev = &event.xclient; | 15165 | XClientMessageEvent *ev = &event.xclient; |
| 15001 | struct window *w = XWINDOW (window); | 15166 | struct window *w = XWINDOW (window); |
| 15002 | struct frame *f = XFRAME (w->frame); | 15167 | struct frame *f = XFRAME (w->frame); |
| 15003 | intptr_t iw = (intptr_t) w; | ||
| 15004 | verify (INTPTR_WIDTH <= 64); | 15168 | verify (INTPTR_WIDTH <= 64); |
| 15005 | int sign_shift = INTPTR_WIDTH - 32; | ||
| 15006 | 15169 | ||
| 15007 | /* Don't do anything if too many scroll bar events have been | 15170 | /* Don't do anything if too many scroll bar events have been |
| 15008 | sent but not received. */ | 15171 | sent but not received. */ |
| @@ -15019,15 +15182,11 @@ x_send_scroll_bar_event (Lisp_Object window, enum scroll_bar_part part, | |||
| 15019 | ev->window = FRAME_X_WINDOW (f); | 15182 | ev->window = FRAME_X_WINDOW (f); |
| 15020 | ev->format = 32; | 15183 | ev->format = 32; |
| 15021 | 15184 | ||
| 15022 | /* A 32-bit X client can pass a window pointer through the X server | 15185 | /* These messages formerly contained a pointer to the window, but |
| 15023 | as-is. | 15186 | now that information is kept internally. The following two |
| 15024 | 15187 | fields are thus zero. */ | |
| 15025 | A 64-bit client is in trouble because a pointer does not fit in | 15188 | ev->data.l[0] = 0; |
| 15026 | the 32 bits given for ClientMessage data and will be truncated by | 15189 | ev->data.l[1] = 0; |
| 15027 | Xlib. So use two slots and hope that X12 will resolve such | ||
| 15028 | issues someday. */ | ||
| 15029 | ev->data.l[0] = iw >> 31 >> 1; | ||
| 15030 | ev->data.l[1] = sign_shift <= 0 ? iw : iw << sign_shift >> sign_shift; | ||
| 15031 | ev->data.l[2] = part; | 15190 | ev->data.l[2] = part; |
| 15032 | ev->data.l[3] = portion; | 15191 | ev->data.l[3] = portion; |
| 15033 | ev->data.l[4] = whole; | 15192 | ev->data.l[4] = whole; |
| @@ -18458,7 +18617,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, | |||
| 18458 | x_dnd_waiting_for_status_window = None; | 18617 | x_dnd_waiting_for_status_window = None; |
| 18459 | else | 18618 | else |
| 18460 | { | 18619 | { |
| 18461 | x_ignore_errors_for_next_request (dpyinfo); | 18620 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 18462 | XSendEvent (dpyinfo->display, target, | 18621 | XSendEvent (dpyinfo->display, target, |
| 18463 | False, NoEventMask, | 18622 | False, NoEventMask, |
| 18464 | &x_dnd_pending_send_position); | 18623 | &x_dnd_pending_send_position); |
| @@ -18572,6 +18731,16 @@ handle_one_xevent (struct x_display_info *dpyinfo, | |||
| 18572 | } | 18731 | } |
| 18573 | } | 18732 | } |
| 18574 | 18733 | ||
| 18734 | if (event->xclient.message_type == dpyinfo->Xatom_EMACS_TMP | ||
| 18735 | && event->xclient.format == 8) | ||
| 18736 | { | ||
| 18737 | /* This is actually an hourglass message. Set whether or | ||
| 18738 | not events from here on have the hourglass enabled. */ | ||
| 18739 | |||
| 18740 | if (any) | ||
| 18741 | FRAME_X_OUTPUT (any)->hourglass_p = event->xclient.data.b[0]; | ||
| 18742 | } | ||
| 18743 | |||
| 18575 | if (event->xclient.message_type == dpyinfo->Xatom_wm_protocols | 18744 | if (event->xclient.message_type == dpyinfo->Xatom_wm_protocols |
| 18576 | && event->xclient.format == 32) | 18745 | && event->xclient.format == 32) |
| 18577 | { | 18746 | { |
| @@ -19160,7 +19329,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, | |||
| 19160 | = xcb_get_property (dpyinfo->xcb_connection, 0, | 19329 | = xcb_get_property (dpyinfo->xcb_connection, 0, |
| 19161 | (xcb_window_t) FRAME_OUTER_WINDOW (f), | 19330 | (xcb_window_t) FRAME_OUTER_WINDOW (f), |
| 19162 | (xcb_atom_t) dpyinfo->Xatom_net_wm_window_opacity, | 19331 | (xcb_atom_t) dpyinfo->Xatom_net_wm_window_opacity, |
| 19163 | XCB_ATOM_CARDINAL, 0, 1); | 19332 | XA_CARDINAL, 0, 1); |
| 19164 | opacity_reply | 19333 | opacity_reply |
| 19165 | = xcb_get_property_reply (dpyinfo->xcb_connection, | 19334 | = xcb_get_property_reply (dpyinfo->xcb_connection, |
| 19166 | opacity_cookie, &error); | 19335 | opacity_cookie, &error); |
| @@ -19169,9 +19338,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, | |||
| 19169 | free (error), rc = false; | 19338 | free (error), rc = false; |
| 19170 | else | 19339 | else |
| 19171 | rc = (opacity_reply->format == 32 | 19340 | rc = (opacity_reply->format == 32 |
| 19172 | && (opacity_reply->type == XCB_ATOM_CARDINAL | 19341 | && (opacity_reply->type == XA_CARDINAL |
| 19173 | || opacity_reply->type == XCB_ATOM_ATOM | 19342 | || opacity_reply->type == XA_ATOM |
| 19174 | || opacity_reply->type == XCB_ATOM_WINDOW) | 19343 | || opacity_reply->type == XA_WINDOW) |
| 19175 | && (xcb_get_property_value_length (opacity_reply) >= 4)); | 19344 | && (xcb_get_property_value_length (opacity_reply) >= 4)); |
| 19176 | 19345 | ||
| 19177 | if (rc) | 19346 | if (rc) |
| @@ -21317,7 +21486,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, | |||
| 21317 | 21486 | ||
| 21318 | if (FRAME_PARENT_FRAME (f) || (hf && frame_ancestor_p (f, hf))) | 21487 | if (FRAME_PARENT_FRAME (f) || (hf && frame_ancestor_p (f, hf))) |
| 21319 | { | 21488 | { |
| 21320 | x_ignore_errors_for_next_request (dpyinfo); | 21489 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 21321 | XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), | 21490 | XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), |
| 21322 | RevertToParent, event->xbutton.time); | 21491 | RevertToParent, event->xbutton.time); |
| 21323 | x_stop_ignoring_errors (dpyinfo); | 21492 | x_stop_ignoring_errors (dpyinfo); |
| @@ -21430,7 +21599,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, | |||
| 21430 | if (!NILP (tab_bar_arg)) | 21599 | if (!NILP (tab_bar_arg)) |
| 21431 | inev.ie.arg = tab_bar_arg; | 21600 | inev.ie.arg = tab_bar_arg; |
| 21432 | } | 21601 | } |
| 21433 | if (FRAME_X_EMBEDDED_P (f)) | 21602 | |
| 21603 | if (FRAME_X_EMBEDDED_P (f) | ||
| 21604 | && !FRAME_NO_ACCEPT_FOCUS (f)) | ||
| 21434 | xembed_send_message (f, event->xbutton.time, | 21605 | xembed_send_message (f, event->xbutton.time, |
| 21435 | XEMBED_REQUEST_FOCUS, 0, 0, 0); | 21606 | XEMBED_REQUEST_FOCUS, 0, 0, 0); |
| 21436 | } | 21607 | } |
| @@ -23013,7 +23184,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, | |||
| 23013 | /* This can generate XI_BadDevice if the | 23184 | /* This can generate XI_BadDevice if the |
| 23014 | device's attachment was destroyed | 23185 | device's attachment was destroyed |
| 23015 | server-side. */ | 23186 | server-side. */ |
| 23016 | x_ignore_errors_for_next_request (dpyinfo); | 23187 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 23017 | XISetFocus (dpyinfo->display, device->attachment, | 23188 | XISetFocus (dpyinfo->display, device->attachment, |
| 23018 | /* Note that the input extension | 23189 | /* Note that the input extension |
| 23019 | only supports RevertToParent-type | 23190 | only supports RevertToParent-type |
| @@ -23026,7 +23197,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, | |||
| 23026 | events to handle focus. Errors are still | 23197 | events to handle focus. Errors are still |
| 23027 | caught here in case the window is not | 23198 | caught here in case the window is not |
| 23028 | viewable. */ | 23199 | viewable. */ |
| 23029 | x_ignore_errors_for_next_request (dpyinfo); | 23200 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 23030 | XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), | 23201 | XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), |
| 23031 | RevertToParent, xev->time); | 23202 | RevertToParent, xev->time); |
| 23032 | x_stop_ignoring_errors (dpyinfo); | 23203 | x_stop_ignoring_errors (dpyinfo); |
| @@ -23198,7 +23369,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, | |||
| 23198 | if (!NILP (tab_bar_arg)) | 23369 | if (!NILP (tab_bar_arg)) |
| 23199 | inev.ie.arg = tab_bar_arg; | 23370 | inev.ie.arg = tab_bar_arg; |
| 23200 | } | 23371 | } |
| 23201 | if (FRAME_X_EMBEDDED_P (f)) | 23372 | |
| 23373 | if (FRAME_X_EMBEDDED_P (f) | ||
| 23374 | && !FRAME_NO_ACCEPT_FOCUS (f)) | ||
| 23202 | xembed_send_message (f, xev->time, | 23375 | xembed_send_message (f, xev->time, |
| 23203 | XEMBED_REQUEST_FOCUS, 0, 0, 0); | 23376 | XEMBED_REQUEST_FOCUS, 0, 0, 0); |
| 23204 | } | 23377 | } |
| @@ -24033,7 +24206,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, | |||
| 24033 | #ifndef HAVE_GTK3 | 24206 | #ifndef HAVE_GTK3 |
| 24034 | else if (x_input_grab_touch_events) | 24207 | else if (x_input_grab_touch_events) |
| 24035 | { | 24208 | { |
| 24036 | x_ignore_errors_for_next_request (dpyinfo); | 24209 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 24037 | XIAllowTouchEvents (dpyinfo->display, xev->deviceid, | 24210 | XIAllowTouchEvents (dpyinfo->display, xev->deviceid, |
| 24038 | xev->detail, xev->event, XIRejectTouch); | 24211 | xev->detail, xev->event, XIRejectTouch); |
| 24039 | x_stop_ignoring_errors (dpyinfo); | 24212 | x_stop_ignoring_errors (dpyinfo); |
| @@ -25461,14 +25634,38 @@ x_clean_failable_requests (struct x_display_info *dpyinfo) | |||
| 25461 | + (last - first)); | 25634 | + (last - first)); |
| 25462 | } | 25635 | } |
| 25463 | 25636 | ||
| 25637 | /* Protect a section of X requests: ignore errors generated by X | ||
| 25638 | requests made from now until `x_stop_ignoring_errors'. Each call | ||
| 25639 | must be paired with a call to `x_stop_ignoring_errors', and | ||
| 25640 | recursive calls inside the protected section are not allowed. | ||
| 25641 | |||
| 25642 | The advantage over x_catch_errors followed by | ||
| 25643 | x_uncatch_errors_after_check is that this function does not sync to | ||
| 25644 | catch errors if requests were made. It should be used instead of | ||
| 25645 | those two functions for catching errors around requests that do not | ||
| 25646 | require a reply. | ||
| 25647 | |||
| 25648 | As a special feature intended to support xselect.c, | ||
| 25649 | SELECTION_SERIAL may be an arbitrary number greater than zero: when | ||
| 25650 | that is the case, x_select_handle_selection_error is called with | ||
| 25651 | the specified number to delete the selection request that | ||
| 25652 | encountered the error. */ | ||
| 25653 | |||
| 25464 | void | 25654 | void |
| 25465 | x_ignore_errors_for_next_request (struct x_display_info *dpyinfo) | 25655 | x_ignore_errors_for_next_request (struct x_display_info *dpyinfo, |
| 25656 | unsigned int selection_serial) | ||
| 25466 | { | 25657 | { |
| 25467 | struct x_failable_request *request, *max; | 25658 | struct x_failable_request *request, *max; |
| 25468 | unsigned long next_request; | 25659 | unsigned long next_request; |
| 25469 | #ifdef HAVE_GTK3 | 25660 | #ifdef HAVE_GTK3 |
| 25470 | GdkDisplay *gdpy; | 25661 | GdkDisplay *gdpy; |
| 25662 | #endif | ||
| 25471 | 25663 | ||
| 25664 | /* This code is not reentrant, so be sure nothing calls it | ||
| 25665 | recursively in response to input. */ | ||
| 25666 | block_input (); | ||
| 25667 | |||
| 25668 | #ifdef HAVE_GTK3 | ||
| 25472 | /* GTK 3 tends to override our own error handler inside certain | 25669 | /* GTK 3 tends to override our own error handler inside certain |
| 25473 | callbacks, which this can be called from. Instead of trying to | 25670 | callbacks, which this can be called from. Instead of trying to |
| 25474 | restore our own, add a trap for the following requests with | 25671 | restore our own, add a trap for the following requests with |
| @@ -25510,6 +25707,7 @@ x_ignore_errors_for_next_request (struct x_display_info *dpyinfo) | |||
| 25510 | 25707 | ||
| 25511 | request->start = next_request; | 25708 | request->start = next_request; |
| 25512 | request->end = 0; | 25709 | request->end = 0; |
| 25710 | request->selection_serial = selection_serial; | ||
| 25513 | 25711 | ||
| 25514 | dpyinfo->next_failable_request++; | 25712 | dpyinfo->next_failable_request++; |
| 25515 | } | 25713 | } |
| @@ -25537,6 +25735,8 @@ x_stop_ignoring_errors (struct x_display_info *dpyinfo) | |||
| 25537 | if (gdpy) | 25735 | if (gdpy) |
| 25538 | gdk_x11_display_error_trap_pop_ignored (gdpy); | 25736 | gdk_x11_display_error_trap_pop_ignored (gdpy); |
| 25539 | #endif | 25737 | #endif |
| 25738 | |||
| 25739 | unblock_input (); | ||
| 25540 | } | 25740 | } |
| 25541 | 25741 | ||
| 25542 | /* Undo the last x_catch_errors call. | 25742 | /* Undo the last x_catch_errors call. |
| @@ -25929,9 +26129,11 @@ For details, see etc/PROBLEMS.\n", | |||
| 25929 | if (!ioerror && dpyinfo) | 26129 | if (!ioerror && dpyinfo) |
| 25930 | { | 26130 | { |
| 25931 | /* Dump the list of error handlers for debugging | 26131 | /* Dump the list of error handlers for debugging |
| 25932 | purposes. */ | 26132 | purposes if the list exists. */ |
| 25933 | 26133 | ||
| 25934 | fprintf (stderr, "X error handlers currently installed:\n"); | 26134 | if ((dpyinfo->failable_requests |
| 26135 | != dpyinfo->next_failable_request) || x_error_message) | ||
| 26136 | fprintf (stderr, "X error handlers currently installed:\n"); | ||
| 25935 | 26137 | ||
| 25936 | for (failable = dpyinfo->failable_requests; | 26138 | for (failable = dpyinfo->failable_requests; |
| 25937 | failable < dpyinfo->next_failable_request; | 26139 | failable < dpyinfo->next_failable_request; |
| @@ -26020,6 +26222,12 @@ x_error_handler (Display *display, XErrorEvent *event) | |||
| 26020 | + (last - fail)); | 26222 | + (last - fail)); |
| 26021 | } | 26223 | } |
| 26022 | 26224 | ||
| 26225 | /* If a selection transfer is the cause of this error, | ||
| 26226 | remove the selection transfer now. */ | ||
| 26227 | if (fail->selection_serial) | ||
| 26228 | x_handle_selection_error (fail->selection_serial, | ||
| 26229 | event); | ||
| 26230 | |||
| 26023 | return 0; | 26231 | return 0; |
| 26024 | } | 26232 | } |
| 26025 | } | 26233 | } |
| @@ -26572,38 +26780,43 @@ x_set_offset (struct frame *f, int xoff, int yoff, int change_gravity) | |||
| 26572 | modified_left, modified_top); | 26780 | modified_left, modified_top); |
| 26573 | #endif | 26781 | #endif |
| 26574 | 26782 | ||
| 26575 | /* 'x_sync_with_move' is too costly for dragging child frames. */ | 26783 | /* The following code is too slow over a latent network |
| 26576 | if (!FRAME_PARENT_FRAME (f) | 26784 | connection. */ |
| 26577 | /* If no window manager exists, just calling XSync will be | 26785 | if (NILP (Vx_lax_frame_positioning)) |
| 26578 | sufficient to ensure that the window geometry has been | 26786 | { |
| 26579 | updated. */ | 26787 | /* 'x_sync_with_move' is too costly for dragging child frames. */ |
| 26580 | && NILP (Vx_no_window_manager)) | 26788 | if (!FRAME_PARENT_FRAME (f) |
| 26581 | { | 26789 | /* If no window manager exists, just calling XSync will be |
| 26582 | x_sync_with_move (f, f->left_pos, f->top_pos, | 26790 | sufficient to ensure that the window geometry has been |
| 26583 | FRAME_DISPLAY_INFO (f)->wm_type == X_WMTYPE_UNKNOWN); | 26791 | updated. */ |
| 26584 | 26792 | && NILP (Vx_no_window_manager)) | |
| 26585 | /* change_gravity is non-zero when this function is called from Lisp to | 26793 | { |
| 26586 | programmatically move a frame. In that case, we call | 26794 | x_sync_with_move (f, f->left_pos, f->top_pos, |
| 26587 | x_check_expected_move to discover if we have a "Type A" or "Type B" | 26795 | FRAME_DISPLAY_INFO (f)->wm_type == X_WMTYPE_UNKNOWN); |
| 26588 | window manager, and, for a "Type A" window manager, adjust the position | 26796 | |
| 26589 | of the frame. | 26797 | /* change_gravity is non-zero when this function is called from Lisp to |
| 26590 | 26798 | programmatically move a frame. In that case, we call | |
| 26591 | We call x_check_expected_move if a programmatic move occurred, and | 26799 | x_check_expected_move to discover if we have a "Type A" or "Type B" |
| 26592 | either the window manager type (A/B) is unknown or it is Type A but we | 26800 | window manager, and, for a "Type A" window manager, adjust the position |
| 26593 | need to compute the top/left offset adjustment for this frame. */ | 26801 | of the frame. |
| 26594 | 26802 | ||
| 26595 | if (change_gravity != 0 | 26803 | We call x_check_expected_move if a programmatic move occurred, and |
| 26596 | && (FRAME_DISPLAY_INFO (f)->wm_type == X_WMTYPE_UNKNOWN | 26804 | either the window manager type (A/B) is unknown or it is Type A but we |
| 26597 | || (FRAME_DISPLAY_INFO (f)->wm_type == X_WMTYPE_A | 26805 | need to compute the top/left offset adjustment for this frame. */ |
| 26598 | && (FRAME_X_OUTPUT (f)->move_offset_left == 0 | 26806 | |
| 26599 | && FRAME_X_OUTPUT (f)->move_offset_top == 0)))) | 26807 | if (change_gravity != 0 |
| 26600 | x_check_expected_move (f, modified_left, modified_top); | 26808 | && (FRAME_DISPLAY_INFO (f)->wm_type == X_WMTYPE_UNKNOWN |
| 26601 | } | 26809 | || (FRAME_DISPLAY_INFO (f)->wm_type == X_WMTYPE_A |
| 26602 | /* Instead, just wait for the last ConfigureWindow request to | 26810 | && (FRAME_X_OUTPUT (f)->move_offset_left == 0 |
| 26603 | complete. No window manager is involved when moving child | 26811 | && FRAME_X_OUTPUT (f)->move_offset_top == 0)))) |
| 26604 | frames. */ | 26812 | x_check_expected_move (f, modified_left, modified_top); |
| 26605 | else | 26813 | } |
| 26606 | XSync (FRAME_X_DISPLAY (f), False); | 26814 | /* Instead, just wait for the last ConfigureWindow request to |
| 26815 | complete. No window manager is involved when moving child | ||
| 26816 | frames. */ | ||
| 26817 | else | ||
| 26818 | XSync (FRAME_X_DISPLAY (f), False); | ||
| 26819 | } | ||
| 26607 | 26820 | ||
| 26608 | unblock_input (); | 26821 | unblock_input (); |
| 26609 | } | 26822 | } |
| @@ -26663,6 +26876,12 @@ x_wm_supports_1 (struct x_display_info *dpyinfo, Atom want_atom) | |||
| 26663 | if (!NILP (Vx_no_window_manager)) | 26876 | if (!NILP (Vx_no_window_manager)) |
| 26664 | return false; | 26877 | return false; |
| 26665 | 26878 | ||
| 26879 | /* If the window system says Emacs is untrusted, there will be no | ||
| 26880 | way to send any information to the window manager, making any | ||
| 26881 | hints useless. */ | ||
| 26882 | if (dpyinfo->untrusted) | ||
| 26883 | return false; | ||
| 26884 | |||
| 26666 | block_input (); | 26885 | block_input (); |
| 26667 | 26886 | ||
| 26668 | x_catch_errors (dpy); | 26887 | x_catch_errors (dpy); |
| @@ -26713,13 +26932,14 @@ x_wm_supports_1 (struct x_display_info *dpyinfo, Atom want_atom) | |||
| 26713 | 26932 | ||
| 26714 | if (rc != Success || actual_type != XA_ATOM || x_had_errors_p (dpy)) | 26933 | if (rc != Success || actual_type != XA_ATOM || x_had_errors_p (dpy)) |
| 26715 | { | 26934 | { |
| 26716 | if (tmp_data) XFree (tmp_data); | 26935 | if (tmp_data) |
| 26936 | XFree (tmp_data); | ||
| 26717 | x_uncatch_errors (); | 26937 | x_uncatch_errors (); |
| 26718 | unblock_input (); | 26938 | unblock_input (); |
| 26719 | return false; | 26939 | return false; |
| 26720 | } | 26940 | } |
| 26721 | 26941 | ||
| 26722 | dpyinfo->net_supported_atoms = (Atom *)tmp_data; | 26942 | dpyinfo->net_supported_atoms = (Atom *) tmp_data; |
| 26723 | dpyinfo->nr_net_supported_atoms = actual_size; | 26943 | dpyinfo->nr_net_supported_atoms = actual_size; |
| 26724 | dpyinfo->net_supported_window = wmcheck_window; | 26944 | dpyinfo->net_supported_window = wmcheck_window; |
| 26725 | } | 26945 | } |
| @@ -27132,13 +27352,12 @@ do_ewmh_fullscreen (struct frame *f) | |||
| 27132 | static void | 27352 | static void |
| 27133 | XTfullscreen_hook (struct frame *f) | 27353 | XTfullscreen_hook (struct frame *f) |
| 27134 | { | 27354 | { |
| 27135 | if (FRAME_VISIBLE_P (f)) | 27355 | if (!FRAME_VISIBLE_P (f)) |
| 27136 | { | 27356 | return; |
| 27137 | block_input (); | 27357 | |
| 27138 | x_check_fullscreen (f); | 27358 | block_input (); |
| 27139 | x_sync (f); | 27359 | x_check_fullscreen (f); |
| 27140 | unblock_input (); | 27360 | unblock_input (); |
| 27141 | } | ||
| 27142 | } | 27361 | } |
| 27143 | 27362 | ||
| 27144 | 27363 | ||
| @@ -27232,10 +27451,7 @@ x_check_fullscreen (struct frame *f) | |||
| 27232 | if (FRAME_VISIBLE_P (f)) | 27451 | if (FRAME_VISIBLE_P (f)) |
| 27233 | x_wait_for_event (f, ConfigureNotify); | 27452 | x_wait_for_event (f, ConfigureNotify); |
| 27234 | else | 27453 | else |
| 27235 | { | 27454 | change_frame_size (f, width, height, false, true, false); |
| 27236 | change_frame_size (f, width, height, false, true, false); | ||
| 27237 | x_sync (f); | ||
| 27238 | } | ||
| 27239 | } | 27455 | } |
| 27240 | 27456 | ||
| 27241 | /* `x_net_wm_state' might have reset the fullscreen frame parameter, | 27457 | /* `x_net_wm_state' might have reset the fullscreen frame parameter, |
| @@ -27409,6 +27625,12 @@ x_set_window_size_1 (struct frame *f, bool change_gravity, | |||
| 27409 | we have to make sure to do it here. */ | 27625 | we have to make sure to do it here. */ |
| 27410 | SET_FRAME_GARBAGED (f); | 27626 | SET_FRAME_GARBAGED (f); |
| 27411 | 27627 | ||
| 27628 | /* The following code is too slow over a latent network | ||
| 27629 | connection, so skip it when the user says so. */ | ||
| 27630 | |||
| 27631 | if (!NILP (Vx_lax_frame_positioning)) | ||
| 27632 | return; | ||
| 27633 | |||
| 27412 | /* Now, strictly speaking, we can't be sure that this is accurate, | 27634 | /* Now, strictly speaking, we can't be sure that this is accurate, |
| 27413 | but the window manager will get around to dealing with the size | 27635 | but the window manager will get around to dealing with the size |
| 27414 | change request eventually, and we'll hear how it went when the | 27636 | change request eventually, and we'll hear how it went when the |
| @@ -27449,8 +27671,6 @@ x_set_window_size_1 (struct frame *f, bool change_gravity, | |||
| 27449 | adjust_frame_size (f, FRAME_PIXEL_TO_TEXT_WIDTH (f, width), | 27671 | adjust_frame_size (f, FRAME_PIXEL_TO_TEXT_WIDTH (f, width), |
| 27450 | FRAME_PIXEL_TO_TEXT_HEIGHT (f, height), | 27672 | FRAME_PIXEL_TO_TEXT_HEIGHT (f, height), |
| 27451 | 5, 0, Qx_set_window_size_1); | 27673 | 5, 0, Qx_set_window_size_1); |
| 27452 | |||
| 27453 | x_sync (f); | ||
| 27454 | } | 27674 | } |
| 27455 | } | 27675 | } |
| 27456 | 27676 | ||
| @@ -27504,7 +27724,7 @@ frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y) | |||
| 27504 | && deviceid != -1) | 27724 | && deviceid != -1) |
| 27505 | { | 27725 | { |
| 27506 | block_input (); | 27726 | block_input (); |
| 27507 | x_ignore_errors_for_next_request (FRAME_DISPLAY_INFO (f)); | 27727 | x_ignore_errors_for_next_request (FRAME_DISPLAY_INFO (f), 0); |
| 27508 | XIWarpPointer (FRAME_X_DISPLAY (f), deviceid, None, | 27728 | XIWarpPointer (FRAME_X_DISPLAY (f), deviceid, None, |
| 27509 | FRAME_X_WINDOW (f), 0, 0, 0, 0, pix_x, pix_y); | 27729 | FRAME_X_WINDOW (f), 0, 0, 0, 0, pix_x, pix_y); |
| 27510 | x_stop_ignoring_errors (FRAME_DISPLAY_INFO (f)); | 27730 | x_stop_ignoring_errors (FRAME_DISPLAY_INFO (f)); |
| @@ -27801,7 +28021,7 @@ x_set_input_focus (struct x_display_info *dpyinfo, Window window, | |||
| 27801 | { | 28021 | { |
| 27802 | eassert (device->use == XIMasterPointer); | 28022 | eassert (device->use == XIMasterPointer); |
| 27803 | 28023 | ||
| 27804 | x_ignore_errors_for_next_request (dpyinfo); | 28024 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 27805 | XISetFocus (dpyinfo->display, device->attachment, | 28025 | XISetFocus (dpyinfo->display, device->attachment, |
| 27806 | /* Note that the input extension | 28026 | /* Note that the input extension |
| 27807 | only supports RevertToParent-type | 28027 | only supports RevertToParent-type |
| @@ -27816,7 +28036,7 @@ x_set_input_focus (struct x_display_info *dpyinfo, Window window, | |||
| 27816 | 28036 | ||
| 27817 | /* Otherwise, use the pointer device that the X server says is the | 28037 | /* Otherwise, use the pointer device that the X server says is the |
| 27818 | client pointer. */ | 28038 | client pointer. */ |
| 27819 | x_ignore_errors_for_next_request (dpyinfo); | 28039 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 27820 | XSetInputFocus (dpyinfo->display, window, RevertToParent, time); | 28040 | XSetInputFocus (dpyinfo->display, window, RevertToParent, time); |
| 27821 | x_stop_ignoring_errors (dpyinfo); | 28041 | x_stop_ignoring_errors (dpyinfo); |
| 27822 | } | 28042 | } |
| @@ -27838,6 +28058,15 @@ x_focus_frame (struct frame *f, bool noactivate) | |||
| 27838 | 28058 | ||
| 27839 | dpyinfo = FRAME_DISPLAY_INFO (f); | 28059 | dpyinfo = FRAME_DISPLAY_INFO (f); |
| 27840 | 28060 | ||
| 28061 | if (dpyinfo->untrusted) | ||
| 28062 | /* The X server ignores all input focus related requests from | ||
| 28063 | untrusted clients. */ | ||
| 28064 | return; | ||
| 28065 | |||
| 28066 | /* The code below is not reentrant wrt to dpyinfo->x_focus_frame and | ||
| 28067 | friends being set. */ | ||
| 28068 | block_input (); | ||
| 28069 | |||
| 27841 | if (FRAME_X_EMBEDDED_P (f)) | 28070 | if (FRAME_X_EMBEDDED_P (f)) |
| 27842 | /* For Xembedded frames, normally the embedder forwards key | 28071 | /* For Xembedded frames, normally the embedder forwards key |
| 27843 | events. See XEmbed Protocol Specification at | 28072 | events. See XEmbed Protocol Specification at |
| @@ -27866,7 +28095,7 @@ x_focus_frame (struct frame *f, bool noactivate) | |||
| 27866 | the current workspace, and mapping it, etc, before moving | 28095 | the current workspace, and mapping it, etc, before moving |
| 27867 | input focus to the frame. */ | 28096 | input focus to the frame. */ |
| 27868 | x_ewmh_activate_frame (f); | 28097 | x_ewmh_activate_frame (f); |
| 27869 | return; | 28098 | goto out; |
| 27870 | } | 28099 | } |
| 27871 | 28100 | ||
| 27872 | if (NILP (Vx_no_window_manager)) | 28101 | if (NILP (Vx_no_window_manager)) |
| @@ -27900,6 +28129,9 @@ x_focus_frame (struct frame *f, bool noactivate) | |||
| 27900 | matter. */ | 28129 | matter. */ |
| 27901 | CurrentTime); | 28130 | CurrentTime); |
| 27902 | } | 28131 | } |
| 28132 | |||
| 28133 | out: | ||
| 28134 | unblock_input (); | ||
| 27903 | } | 28135 | } |
| 27904 | 28136 | ||
| 27905 | 28137 | ||
| @@ -27946,7 +28178,7 @@ xembed_send_message (struct frame *f, Time t, enum xembed_message msg, | |||
| 27946 | but I don't understand why: there is no way for clients to | 28178 | but I don't understand why: there is no way for clients to |
| 27947 | survive the death of the parent anyway. */ | 28179 | survive the death of the parent anyway. */ |
| 27948 | 28180 | ||
| 27949 | x_ignore_errors_for_next_request (FRAME_DISPLAY_INFO (f)); | 28181 | x_ignore_errors_for_next_request (FRAME_DISPLAY_INFO (f), 0); |
| 27950 | XSendEvent (FRAME_X_DISPLAY (f), FRAME_X_OUTPUT (f)->parent_desc, | 28182 | XSendEvent (FRAME_X_DISPLAY (f), FRAME_X_OUTPUT (f)->parent_desc, |
| 27951 | False, NoEventMask, &event); | 28183 | False, NoEventMask, &event); |
| 27952 | x_stop_ignoring_errors (FRAME_DISPLAY_INFO (f)); | 28184 | x_stop_ignoring_errors (FRAME_DISPLAY_INFO (f)); |
| @@ -28097,6 +28329,7 @@ x_make_frame_visible (struct frame *f) | |||
| 28097 | && !FRAME_ICONIFIED_P (f) | 28329 | && !FRAME_ICONIFIED_P (f) |
| 28098 | && !FRAME_X_EMBEDDED_P (f) | 28330 | && !FRAME_X_EMBEDDED_P (f) |
| 28099 | && !FRAME_PARENT_FRAME (f) | 28331 | && !FRAME_PARENT_FRAME (f) |
| 28332 | && NILP (Vx_lax_frame_positioning) | ||
| 28100 | && f->win_gravity == NorthWestGravity | 28333 | && f->win_gravity == NorthWestGravity |
| 28101 | && previously_visible) | 28334 | && previously_visible) |
| 28102 | { | 28335 | { |
| @@ -28125,7 +28358,8 @@ x_make_frame_visible (struct frame *f) | |||
| 28125 | } | 28358 | } |
| 28126 | 28359 | ||
| 28127 | /* Try to wait for a MapNotify event (that is what tells us when a | 28360 | /* Try to wait for a MapNotify event (that is what tells us when a |
| 28128 | frame becomes visible). */ | 28361 | frame becomes visible). Unless `x-lax-frame-positioning' is |
| 28362 | non-nil: there, that is a little slow. */ | ||
| 28129 | 28363 | ||
| 28130 | #ifdef CYGWIN | 28364 | #ifdef CYGWIN |
| 28131 | /* On Cygwin, which uses input polling, we need to force input to | 28365 | /* On Cygwin, which uses input polling, we need to force input to |
| @@ -28143,7 +28377,8 @@ x_make_frame_visible (struct frame *f) | |||
| 28143 | poll_suppress_count = old_poll_suppress_count; | 28377 | poll_suppress_count = old_poll_suppress_count; |
| 28144 | #endif | 28378 | #endif |
| 28145 | 28379 | ||
| 28146 | if (!FRAME_VISIBLE_P (f)) | 28380 | if (!FRAME_VISIBLE_P (f) |
| 28381 | && NILP (Vx_lax_frame_positioning)) | ||
| 28147 | { | 28382 | { |
| 28148 | if (CONSP (frame_size_history)) | 28383 | if (CONSP (frame_size_history)) |
| 28149 | frame_size_history_plain | 28384 | frame_size_history_plain |
| @@ -28200,7 +28435,7 @@ x_make_frame_invisible (struct frame *f) | |||
| 28200 | error ("Can't notify window manager of window withdrawal"); | 28435 | error ("Can't notify window manager of window withdrawal"); |
| 28201 | } | 28436 | } |
| 28202 | 28437 | ||
| 28203 | x_sync (f); | 28438 | XSync (FRAME_X_DISPLAY (f), False); |
| 28204 | 28439 | ||
| 28205 | /* We can't distinguish this from iconification | 28440 | /* We can't distinguish this from iconification |
| 28206 | just by the event that we get from the server. | 28441 | just by the event that we get from the server. |
| @@ -28798,6 +29033,53 @@ x_get_atom_name (struct x_display_info *dpyinfo, Atom atom, | |||
| 28798 | return value; | 29033 | return value; |
| 28799 | } | 29034 | } |
| 28800 | 29035 | ||
| 29036 | /* Intern an array of atoms, and do so quickly, avoiding extraneous | ||
| 29037 | roundtrips to the X server. | ||
| 29038 | |||
| 29039 | Avoid sending atoms that have already been found to the X server. | ||
| 29040 | This cannot do anything that will end up triggering garbage | ||
| 29041 | collection. */ | ||
| 29042 | |||
| 29043 | void | ||
| 29044 | x_intern_atoms (struct x_display_info *dpyinfo, char **names, int count, | ||
| 29045 | Atom *atoms_return) | ||
| 29046 | { | ||
| 29047 | int i, j, indices[256]; | ||
| 29048 | char *new_names[256]; | ||
| 29049 | Atom results[256], candidate; | ||
| 29050 | |||
| 29051 | if (count > 256) | ||
| 29052 | /* Atoms array too big to inspect reasonably, just send it to the | ||
| 29053 | server and back. */ | ||
| 29054 | XInternAtoms (dpyinfo->display, new_names, count, False, atoms_return); | ||
| 29055 | else | ||
| 29056 | { | ||
| 29057 | for (i = 0, j = 0; i < count; ++i) | ||
| 29058 | { | ||
| 29059 | candidate = x_intern_cached_atom (dpyinfo, names[i], | ||
| 29060 | true); | ||
| 29061 | |||
| 29062 | if (candidate) | ||
| 29063 | atoms_return[i] = candidate; | ||
| 29064 | else | ||
| 29065 | { | ||
| 29066 | indices[j++] = i; | ||
| 29067 | new_names[j - 1] = names[i]; | ||
| 29068 | } | ||
| 29069 | } | ||
| 29070 | |||
| 29071 | if (!j) | ||
| 29072 | return; | ||
| 29073 | |||
| 29074 | /* Now, get the results back from the X server. */ | ||
| 29075 | XInternAtoms (dpyinfo->display, new_names, j, False, | ||
| 29076 | results); | ||
| 29077 | |||
| 29078 | for (i = 0; i < j; ++i) | ||
| 29079 | atoms_return[indices[i]] = results[i]; | ||
| 29080 | } | ||
| 29081 | } | ||
| 29082 | |||
| 28801 | #ifndef USE_GTK | 29083 | #ifndef USE_GTK |
| 28802 | 29084 | ||
| 28803 | /* Set up XEmbed for F, and change its save set to handle the parent | 29085 | /* Set up XEmbed for F, and change its save set to handle the parent |
| @@ -29346,6 +29628,7 @@ struct x_display_info * | |||
| 29346 | x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name) | 29628 | x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name) |
| 29347 | { | 29629 | { |
| 29348 | Display *dpy; | 29630 | Display *dpy; |
| 29631 | XKeyboardState keyboard_state; | ||
| 29349 | struct terminal *terminal; | 29632 | struct terminal *terminal; |
| 29350 | struct x_display_info *dpyinfo; | 29633 | struct x_display_info *dpyinfo; |
| 29351 | XrmDatabase xrdb; | 29634 | XrmDatabase xrdb; |
| @@ -29565,6 +29848,32 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name) | |||
| 29565 | dpyinfo = xzalloc (sizeof *dpyinfo); | 29848 | dpyinfo = xzalloc (sizeof *dpyinfo); |
| 29566 | terminal = x_create_terminal (dpyinfo); | 29849 | terminal = x_create_terminal (dpyinfo); |
| 29567 | 29850 | ||
| 29851 | if (!NILP (Vx_detect_server_trust)) | ||
| 29852 | { | ||
| 29853 | /* Detect whether or not the X server trusts this client, which | ||
| 29854 | is done by making a SetKeyboardControl request and checking | ||
| 29855 | for an Access error. */ | ||
| 29856 | XGrabServer (dpy); | ||
| 29857 | XGetKeyboardControl (dpy, &keyboard_state); | ||
| 29858 | |||
| 29859 | x_catch_errors (dpy); | ||
| 29860 | |||
| 29861 | /* At this point, the display is not on x_display_list, so | ||
| 29862 | x_uncatch_errors won't sync. However, that's okay because | ||
| 29863 | x_had_errors_p will. */ | ||
| 29864 | |||
| 29865 | if (keyboard_state.global_auto_repeat | ||
| 29866 | == AutoRepeatModeOn) | ||
| 29867 | XAutoRepeatOn (dpy); | ||
| 29868 | else | ||
| 29869 | XAutoRepeatOff (dpy); | ||
| 29870 | |||
| 29871 | if (x_had_errors_p (dpy)) | ||
| 29872 | dpyinfo->untrusted = true; | ||
| 29873 | x_uncatch_errors_after_check (); | ||
| 29874 | XUngrabServer (dpy); | ||
| 29875 | } | ||
| 29876 | |||
| 29568 | dpyinfo->next_failable_request = dpyinfo->failable_requests; | 29877 | dpyinfo->next_failable_request = dpyinfo->failable_requests; |
| 29569 | 29878 | ||
| 29570 | { | 29879 | { |
| @@ -29585,13 +29894,17 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name) | |||
| 29585 | { | 29894 | { |
| 29586 | char *vendor = ServerVendor (dpy); | 29895 | char *vendor = ServerVendor (dpy); |
| 29587 | 29896 | ||
| 29588 | /* Temporarily hide the partially initialized terminal. */ | 29897 | /* Temporarily hide the partially initialized terminal. |
| 29898 | Use safe_call so that if a signal happens, a partially | ||
| 29899 | initialized display (and display connection) is not | ||
| 29900 | kept around. */ | ||
| 29589 | terminal_list = terminal->next_terminal; | 29901 | terminal_list = terminal->next_terminal; |
| 29590 | unblock_input (); | 29902 | unblock_input (); |
| 29591 | kset_system_key_alist | 29903 | kset_system_key_alist (terminal->kboard, |
| 29592 | (terminal->kboard, | 29904 | safe_call1 (Qvendor_specific_keysyms, |
| 29593 | call1 (Qvendor_specific_keysyms, | 29905 | (vendor |
| 29594 | vendor ? build_string (vendor) : empty_unibyte_string)); | 29906 | ? build_string (vendor) |
| 29907 | : empty_unibyte_string))); | ||
| 29595 | block_input (); | 29908 | block_input (); |
| 29596 | terminal->next_terminal = terminal_list; | 29909 | terminal->next_terminal = terminal_list; |
| 29597 | terminal_list = terminal; | 29910 | terminal_list = terminal; |
| @@ -30209,7 +30522,7 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name) | |||
| 30209 | 1, 0, 1); | 30522 | 1, 0, 1); |
| 30210 | 30523 | ||
| 30211 | dpyinfo->invisible_cursor = make_invisible_cursor (dpyinfo); | 30524 | dpyinfo->invisible_cursor = make_invisible_cursor (dpyinfo); |
| 30212 | #ifdef HAVE_XFIXES | 30525 | #if defined HAVE_XFIXES && XFIXES_VERSION >= 40000 |
| 30213 | dpyinfo->fixes_pointer_blanking = egetenv ("EMACS_XFIXES"); | 30526 | dpyinfo->fixes_pointer_blanking = egetenv ("EMACS_XFIXES"); |
| 30214 | #endif | 30527 | #endif |
| 30215 | 30528 | ||
| @@ -30537,8 +30850,13 @@ x_delete_display (struct x_display_info *dpyinfo) | |||
| 30537 | last = ie; | 30850 | last = ie; |
| 30538 | } | 30851 | } |
| 30539 | 30852 | ||
| 30853 | /* Delete selection requests bound for dpyinfo from the keyboard | ||
| 30854 | buffer. */ | ||
| 30540 | x_delete_selection_requests (dpyinfo); | 30855 | x_delete_selection_requests (dpyinfo); |
| 30541 | 30856 | ||
| 30857 | /* And remove any outstanding selection transfers. */ | ||
| 30858 | x_remove_selection_transfers (dpyinfo); | ||
| 30859 | |||
| 30542 | if (next_noop_dpyinfo == dpyinfo) | 30860 | if (next_noop_dpyinfo == dpyinfo) |
| 30543 | next_noop_dpyinfo = dpyinfo->next; | 30861 | next_noop_dpyinfo = dpyinfo->next; |
| 30544 | 30862 | ||
| @@ -30568,6 +30886,9 @@ x_delete_display (struct x_display_info *dpyinfo) | |||
| 30568 | } | 30886 | } |
| 30569 | } | 30887 | } |
| 30570 | 30888 | ||
| 30889 | if (dpyinfo->net_supported_atoms) | ||
| 30890 | XFree (dpyinfo->net_supported_atoms); | ||
| 30891 | |||
| 30571 | xfree (dpyinfo->color_names); | 30892 | xfree (dpyinfo->color_names); |
| 30572 | xfree (dpyinfo->color_names_length); | 30893 | xfree (dpyinfo->color_names_length); |
| 30573 | xfree (dpyinfo->x_id_name); | 30894 | xfree (dpyinfo->x_id_name); |
| @@ -30679,7 +31000,11 @@ static struct redisplay_interface x_redisplay_interface = | |||
| 30679 | void | 31000 | void |
| 30680 | x_delete_terminal (struct terminal *terminal) | 31001 | x_delete_terminal (struct terminal *terminal) |
| 30681 | { | 31002 | { |
| 30682 | struct x_display_info *dpyinfo = terminal->display_info.x; | 31003 | struct x_display_info *dpyinfo; |
| 31004 | struct frame *f; | ||
| 31005 | Lisp_Object tail, frame; | ||
| 31006 | |||
| 31007 | dpyinfo = terminal->display_info.x; | ||
| 30683 | 31008 | ||
| 30684 | /* Protect against recursive calls. delete_frame in | 31009 | /* Protect against recursive calls. delete_frame in |
| 30685 | delete_terminal calls us back when it deletes our last frame. */ | 31010 | delete_terminal calls us back when it deletes our last frame. */ |
| @@ -30687,6 +31012,19 @@ x_delete_terminal (struct terminal *terminal) | |||
| 30687 | return; | 31012 | return; |
| 30688 | 31013 | ||
| 30689 | block_input (); | 31014 | block_input (); |
| 31015 | |||
| 31016 | /* Delete all remaining frames on the display that is going away. | ||
| 31017 | Otherwise, font backends assume the display is still up, and | ||
| 31018 | xftfont_end_for_frame crashes. */ | ||
| 31019 | FOR_EACH_FRAME (tail, frame) | ||
| 31020 | { | ||
| 31021 | f = XFRAME (frame); | ||
| 31022 | |||
| 31023 | if (FRAME_LIVE_P (f) && f->terminal == terminal) | ||
| 31024 | /* Pass Qnoelisp rather than Qt. */ | ||
| 31025 | delete_frame (frame, Qnoelisp); | ||
| 31026 | } | ||
| 31027 | |||
| 30690 | #ifdef HAVE_X_I18N | 31028 | #ifdef HAVE_X_I18N |
| 30691 | /* We must close our connection to the XIM server before closing the | 31029 | /* We must close our connection to the XIM server before closing the |
| 30692 | X display. */ | 31030 | X display. */ |
| @@ -30700,6 +31038,10 @@ x_delete_terminal (struct terminal *terminal) | |||
| 30700 | image_destroy_all_bitmaps (dpyinfo); | 31038 | image_destroy_all_bitmaps (dpyinfo); |
| 30701 | XSetCloseDownMode (dpyinfo->display, DestroyAll); | 31039 | XSetCloseDownMode (dpyinfo->display, DestroyAll); |
| 30702 | 31040 | ||
| 31041 | /* Delete the scratch cursor GC, should it exist. */ | ||
| 31042 | if (dpyinfo->scratch_cursor_gc) | ||
| 31043 | XFreeGC (dpyinfo->display, dpyinfo->scratch_cursor_gc); | ||
| 31044 | |||
| 30703 | /* Get rid of any drag-and-drop operation that might be in | 31045 | /* Get rid of any drag-and-drop operation that might be in |
| 30704 | progress as well. */ | 31046 | progress as well. */ |
| 30705 | if ((x_dnd_in_progress || x_dnd_waiting_for_finish) | 31047 | if ((x_dnd_in_progress || x_dnd_waiting_for_finish) |
| @@ -31020,7 +31362,7 @@ x_catch_errors_for_lisp (struct x_display_info *dpyinfo) | |||
| 31020 | if (!x_fast_protocol_requests) | 31362 | if (!x_fast_protocol_requests) |
| 31021 | x_catch_errors (dpyinfo->display); | 31363 | x_catch_errors (dpyinfo->display); |
| 31022 | else | 31364 | else |
| 31023 | x_ignore_errors_for_next_request (dpyinfo); | 31365 | x_ignore_errors_for_next_request (dpyinfo, 0); |
| 31024 | } | 31366 | } |
| 31025 | 31367 | ||
| 31026 | void | 31368 | void |
| @@ -31229,6 +31571,8 @@ syms_of_xterm (void) | |||
| 31229 | DEFSYM (Qnow, "now"); | 31571 | DEFSYM (Qnow, "now"); |
| 31230 | DEFSYM (Qx_dnd_targets_list, "x-dnd-targets-list"); | 31572 | DEFSYM (Qx_dnd_targets_list, "x-dnd-targets-list"); |
| 31231 | DEFSYM (Qx_auto_preserve_selections, "x-auto-preserve-selections"); | 31573 | DEFSYM (Qx_auto_preserve_selections, "x-auto-preserve-selections"); |
| 31574 | DEFSYM (Qexpose, "expose"); | ||
| 31575 | DEFSYM (Qdont_save, "dont-save"); | ||
| 31232 | 31576 | ||
| 31233 | #ifdef USE_GTK | 31577 | #ifdef USE_GTK |
| 31234 | xg_default_icon_file = build_pure_c_string ("icons/hicolor/scalable/apps/emacs.svg"); | 31578 | xg_default_icon_file = build_pure_c_string ("icons/hicolor/scalable/apps/emacs.svg"); |
| @@ -31398,7 +31742,6 @@ always uses gtk_window_move and ignores the value of this variable. */); | |||
| 31398 | This option is only effective when Emacs is built with XInput 2 | 31742 | This option is only effective when Emacs is built with XInput 2 |
| 31399 | support. */); | 31743 | support. */); |
| 31400 | Vx_scroll_event_delta_factor = make_float (1.0); | 31744 | Vx_scroll_event_delta_factor = make_float (1.0); |
| 31401 | DEFSYM (Qexpose, "expose"); | ||
| 31402 | 31745 | ||
| 31403 | DEFVAR_BOOL ("x-gtk-use-native-input", x_gtk_use_native_input, | 31746 | DEFVAR_BOOL ("x-gtk-use-native-input", x_gtk_use_native_input, |
| 31404 | doc: /* Non-nil means to use GTK for input method support. | 31747 | doc: /* Non-nil means to use GTK for input method support. |
| @@ -31612,4 +31955,25 @@ select text over slow X connections. | |||
| 31612 | If that is still too slow, setting this variable to the symbol | 31955 | If that is still too slow, setting this variable to the symbol |
| 31613 | `really-fast' will make Emacs return only cached values. */); | 31956 | `really-fast' will make Emacs return only cached values. */); |
| 31614 | Vx_use_fast_mouse_position = Qnil; | 31957 | Vx_use_fast_mouse_position = Qnil; |
| 31958 | |||
| 31959 | DEFVAR_LISP ("x-detect-server-trust", Vx_detect_server_trust, | ||
| 31960 | doc: /* If non-nil, Emacs should detect whether or not it is trusted by X. | ||
| 31961 | |||
| 31962 | If non-nil, Emacs will make an X request at connection startup that is | ||
| 31963 | prohibited to untrusted clients under the X Security Extension and | ||
| 31964 | check whether or not a resulting Access error is generated by the X | ||
| 31965 | server. If the X server reports the error, Emacs will disable certain | ||
| 31966 | features that do not work for untrusted clients. */); | ||
| 31967 | Vx_detect_server_trust = Qnil; | ||
| 31968 | |||
| 31969 | DEFVAR_LISP ("x-lax-frame-positioning", Vx_lax_frame_positioning, | ||
| 31970 | doc: /* If non-nil, Emacs won't compensate for WM geometry behavior. | ||
| 31971 | |||
| 31972 | Setting this to non-nil is useful when the compensation proves to be | ||
| 31973 | too slow, which is usually true when the X server is located over a | ||
| 31974 | network connection with high latency. Doing so will make frame | ||
| 31975 | creation and placement faster at the cost of reducing the accuracy of | ||
| 31976 | frame placement via frame parameters, `set-frame-position', and | ||
| 31977 | `set-frame-size'. */); | ||
| 31978 | Vx_lax_frame_positioning = Qnil; | ||
| 31615 | } | 31979 | } |
diff --git a/src/xterm.h b/src/xterm.h index c36920081d3..832ffc172b9 100644 --- a/src/xterm.h +++ b/src/xterm.h | |||
| @@ -21,6 +21,22 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 21 | #define XTERM_H | 21 | #define XTERM_H |
| 22 | 22 | ||
| 23 | #include <X11/Xlib.h> | 23 | #include <X11/Xlib.h> |
| 24 | |||
| 25 | #ifdef HAVE_XFIXES | ||
| 26 | #include <X11/extensions/Xfixes.h> | ||
| 27 | |||
| 28 | #if defined HAVE_XINPUT2 && XFIXES_MAJOR < 5 | ||
| 29 | /* XI2 headers need PointerBarrier, which is not defined in old | ||
| 30 | versions of the fixes library. Define that type here. */ | ||
| 31 | typedef XID PointerBarrier; | ||
| 32 | #endif | ||
| 33 | #if defined HAVE_XCOMPOSITE && XFIXES_MAJOR < 2 | ||
| 34 | /* Recent Composite headers need XserverRegion, which is not defined | ||
| 35 | in old versions of the fixes library. Define that type here. */ | ||
| 36 | typedef XID XserverRegion; | ||
| 37 | #endif | ||
| 38 | #endif | ||
| 39 | |||
| 24 | #include <X11/cursorfont.h> | 40 | #include <X11/cursorfont.h> |
| 25 | 41 | ||
| 26 | /* Include Xutil.h after keysym.h to work around a bug that prevents | 42 | /* Include Xutil.h after keysym.h to work around a bug that prevents |
| @@ -318,6 +334,9 @@ struct x_failable_request | |||
| 318 | /* If this is zero, then the request has not yet been made. | 334 | /* If this is zero, then the request has not yet been made. |
| 319 | Otherwise, this is the request that ends this sequence. */ | 335 | Otherwise, this is the request that ends this sequence. */ |
| 320 | unsigned long end; | 336 | unsigned long end; |
| 337 | |||
| 338 | /* Any selection event serial associated with this error trap. */ | ||
| 339 | unsigned int selection_serial; | ||
| 321 | }; | 340 | }; |
| 322 | 341 | ||
| 323 | #ifdef HAVE_XFIXES | 342 | #ifdef HAVE_XFIXES |
| @@ -360,6 +379,10 @@ struct x_display_info | |||
| 360 | /* Number of frames that are on this display. */ | 379 | /* Number of frames that are on this display. */ |
| 361 | int reference_count; | 380 | int reference_count; |
| 362 | 381 | ||
| 382 | /* True if this display connection cannot communicate with the | ||
| 383 | window manager because it is not trusted by the X server. */ | ||
| 384 | bool untrusted; | ||
| 385 | |||
| 363 | /* The Screen this connection is connected to. */ | 386 | /* The Screen this connection is connected to. */ |
| 364 | Screen *screen; | 387 | Screen *screen; |
| 365 | 388 | ||
| @@ -406,7 +429,7 @@ struct x_display_info | |||
| 406 | Unused if this display supports Xfixes extension. */ | 429 | Unused if this display supports Xfixes extension. */ |
| 407 | Cursor invisible_cursor; | 430 | Cursor invisible_cursor; |
| 408 | 431 | ||
| 409 | #ifdef HAVE_XFIXES | 432 | #if defined HAVE_XFIXES && XFIXES_VERSION >= 40000 |
| 410 | /* Whether or not to use Xfixes for pointer blanking. */ | 433 | /* Whether or not to use Xfixes for pointer blanking. */ |
| 411 | bool fixes_pointer_blanking; | 434 | bool fixes_pointer_blanking; |
| 412 | #endif | 435 | #endif |
| @@ -537,6 +560,12 @@ struct x_display_info | |||
| 537 | KDE" protocol in x-dnd.el). */ | 560 | KDE" protocol in x-dnd.el). */ |
| 538 | Atom Xatom_DndProtocol, Xatom_DND_PROTOCOL; | 561 | Atom Xatom_DndProtocol, Xatom_DND_PROTOCOL; |
| 539 | 562 | ||
| 563 | /* Atoms to make x_intern_cached_atom fast. */ | ||
| 564 | Atom Xatom_text_plain_charset_utf_8, Xatom_LENGTH, Xatom_FILE_NAME, | ||
| 565 | Xatom_CHARACTER_POSITION, Xatom_LINE_NUMBER, Xatom_COLUMN_NUMBER, | ||
| 566 | Xatom_OWNER_OS, Xatom_HOST_NAME, Xatom_USER, Xatom_CLASS, | ||
| 567 | Xatom_NAME, Xatom_SAVE_TARGETS; | ||
| 568 | |||
| 540 | /* The frame (if any) which has the X window that has keyboard focus. | 569 | /* The frame (if any) which has the X window that has keyboard focus. |
| 541 | Zero if none. This is examined by Ffocus_frame in xfns.c. Note | 570 | Zero if none. This is examined by Ffocus_frame in xfns.c. Note |
| 542 | that a mere EnterNotify event can set this; if you need to know the | 571 | that a mere EnterNotify event can set this; if you need to know the |
| @@ -1179,6 +1208,10 @@ struct x_output | |||
| 1179 | frame. */ | 1208 | frame. */ |
| 1180 | bool_bf waiting_for_frame_p : 1; | 1209 | bool_bf waiting_for_frame_p : 1; |
| 1181 | 1210 | ||
| 1211 | /* Whether or not Emacs just skipped waiting for a frame due to a | ||
| 1212 | timeout. */ | ||
| 1213 | bool_bf draw_just_hung_p : 1; | ||
| 1214 | |||
| 1182 | #if !defined USE_GTK && defined HAVE_CLOCK_GETTIME | 1215 | #if !defined USE_GTK && defined HAVE_CLOCK_GETTIME |
| 1183 | /* Whether or not Emacs should wait for the compositing manager to | 1216 | /* Whether or not Emacs should wait for the compositing manager to |
| 1184 | draw frames before starting a new frame. */ | 1217 | draw frames before starting a new frame. */ |
| @@ -1392,6 +1425,8 @@ extern void x_mark_frame_dirty (struct frame *f); | |||
| 1392 | FRAME_X_OUTPUT (f)->extended_frame_counter | 1425 | FRAME_X_OUTPUT (f)->extended_frame_counter |
| 1393 | #define FRAME_X_WAITING_FOR_DRAW(f) \ | 1426 | #define FRAME_X_WAITING_FOR_DRAW(f) \ |
| 1394 | FRAME_X_OUTPUT (f)->waiting_for_frame_p | 1427 | FRAME_X_OUTPUT (f)->waiting_for_frame_p |
| 1428 | #define FRAME_X_DRAW_JUST_HUNG(f) \ | ||
| 1429 | FRAME_X_OUTPUT (f)->draw_just_hung_p | ||
| 1395 | #define FRAME_X_COUNTER_VALUE(f) \ | 1430 | #define FRAME_X_COUNTER_VALUE(f) \ |
| 1396 | FRAME_X_OUTPUT (f)->current_extended_counter_value | 1431 | FRAME_X_OUTPUT (f)->current_extended_counter_value |
| 1397 | #endif | 1432 | #endif |
| @@ -1638,7 +1673,8 @@ extern bool x_had_errors_p (Display *); | |||
| 1638 | extern void x_unwind_errors_to (int); | 1673 | extern void x_unwind_errors_to (int); |
| 1639 | extern void x_uncatch_errors (void); | 1674 | extern void x_uncatch_errors (void); |
| 1640 | extern void x_uncatch_errors_after_check (void); | 1675 | extern void x_uncatch_errors_after_check (void); |
| 1641 | extern void x_ignore_errors_for_next_request (struct x_display_info *); | 1676 | extern void x_ignore_errors_for_next_request (struct x_display_info *, |
| 1677 | unsigned int); | ||
| 1642 | extern void x_stop_ignoring_errors (struct x_display_info *); | 1678 | extern void x_stop_ignoring_errors (struct x_display_info *); |
| 1643 | extern void x_clear_errors (Display *); | 1679 | extern void x_clear_errors (Display *); |
| 1644 | extern void x_set_window_size (struct frame *, bool, int, int); | 1680 | extern void x_set_window_size (struct frame *, bool, int, int); |
| @@ -1711,6 +1747,11 @@ extern Lisp_Object x_handle_translate_coordinates (struct frame *, Lisp_Object, | |||
| 1711 | 1747 | ||
| 1712 | extern Bool x_query_pointer (Display *, Window, Window *, Window *, int *, | 1748 | extern Bool x_query_pointer (Display *, Window, Window *, Window *, int *, |
| 1713 | int *, int *, int *, unsigned int *); | 1749 | int *, int *, int *, unsigned int *); |
| 1750 | extern Atom x_intern_cached_atom (struct x_display_info *, const char *, | ||
| 1751 | bool); | ||
| 1752 | extern void x_intern_atoms (struct x_display_info *, char **, int, Atom *); | ||
| 1753 | extern char *x_get_atom_name (struct x_display_info *, Atom, bool *) | ||
| 1754 | ATTRIBUTE_MALLOC ATTRIBUTE_DEALLOC_FREE; | ||
| 1714 | 1755 | ||
| 1715 | #ifdef HAVE_GTK3 | 1756 | #ifdef HAVE_GTK3 |
| 1716 | extern void x_scroll_bar_configure (GdkEvent *); | 1757 | extern void x_scroll_bar_configure (GdkEvent *); |
| @@ -1792,6 +1833,9 @@ extern void x_handle_property_notify (const XPropertyEvent *); | |||
| 1792 | extern void x_handle_selection_notify (const XSelectionEvent *); | 1833 | extern void x_handle_selection_notify (const XSelectionEvent *); |
| 1793 | extern void x_handle_selection_event (struct selection_input_event *); | 1834 | extern void x_handle_selection_event (struct selection_input_event *); |
| 1794 | extern void x_clear_frame_selections (struct frame *); | 1835 | extern void x_clear_frame_selections (struct frame *); |
| 1836 | extern void x_remove_selection_transfers (struct x_display_info *); | ||
| 1837 | extern void x_handle_selection_error (unsigned int, XErrorEvent *); | ||
| 1838 | |||
| 1795 | extern Lisp_Object x_atom_to_symbol (struct x_display_info *, Atom); | 1839 | extern Lisp_Object x_atom_to_symbol (struct x_display_info *, Atom); |
| 1796 | extern Atom symbol_to_x_atom (struct x_display_info *, Lisp_Object); | 1840 | extern Atom symbol_to_x_atom (struct x_display_info *, Lisp_Object); |
| 1797 | 1841 | ||
| @@ -1801,11 +1845,8 @@ extern bool x_handle_dnd_message (struct frame *, | |||
| 1801 | struct input_event *, | 1845 | struct input_event *, |
| 1802 | bool, int, int); | 1846 | bool, int, int); |
| 1803 | extern int x_check_property_data (Lisp_Object); | 1847 | extern int x_check_property_data (Lisp_Object); |
| 1804 | extern void x_fill_property_data (Display *, | 1848 | extern void x_fill_property_data (struct x_display_info *, Lisp_Object, |
| 1805 | Lisp_Object, | 1849 | void *, int, int); |
| 1806 | void *, | ||
| 1807 | int, | ||
| 1808 | int); | ||
| 1809 | extern Lisp_Object x_property_data_to_lisp (struct frame *, | 1850 | extern Lisp_Object x_property_data_to_lisp (struct frame *, |
| 1810 | const unsigned char *, | 1851 | const unsigned char *, |
| 1811 | Atom, | 1852 | Atom, |
| @@ -1818,10 +1859,10 @@ extern Lisp_Object x_timestamp_for_selection (struct x_display_info *, | |||
| 1818 | Lisp_Object); | 1859 | Lisp_Object); |
| 1819 | extern void x_own_selection (Lisp_Object, Lisp_Object, Lisp_Object, | 1860 | extern void x_own_selection (Lisp_Object, Lisp_Object, Lisp_Object, |
| 1820 | Lisp_Object, Time); | 1861 | Lisp_Object, Time); |
| 1821 | extern Atom x_intern_cached_atom (struct x_display_info *, const char *, | 1862 | |
| 1822 | bool); | 1863 | extern void mark_xselect (void); |
| 1823 | extern char *x_get_atom_name (struct x_display_info *, Atom, bool *) | 1864 | |
| 1824 | ATTRIBUTE_MALLOC ATTRIBUTE_DEALLOC_FREE; | 1865 | /* Misc definitions. */ |
| 1825 | 1866 | ||
| 1826 | #ifdef USE_GTK | 1867 | #ifdef USE_GTK |
| 1827 | extern bool xg_set_icon (struct frame *, Lisp_Object); | 1868 | extern bool xg_set_icon (struct frame *, Lisp_Object); |