diff --git a/$GNUSTEP_MAKEFILES/config.guess b/$GNUSTEP_MAKEFILES/config.guess new file mode 100755 index 00000000..48a68460 --- /dev/null +++ b/$GNUSTEP_MAKEFILES/config.guess @@ -0,0 +1,1815 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright 1992-2024 Free Software Foundation, Inc. + +# shellcheck disable=SC2006,SC2268 # see below for rationale + +timestamp='2024-07-27' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). +# +# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. +# +# You can get the latest version of this script from: +# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess +# +# Please send patches to . + + +# The "shellcheck disable" line above the timestamp inhibits complaints +# about features and limitations of the classic Bourne shell that were +# superseded or lifted in POSIX. However, this script identifies a wide +# variety of pre-POSIX systems that do not have POSIX shells at all, and +# even some reasonably current systems (Solaris 10 as case-in-point) still +# have a pre-POSIX /bin/sh. + + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system '$me' is run on. + +Options: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright 1992-2024 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try '$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +# Just in case it came from the environment. +GUESS= + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, 'CC_FOR_BUILD' used to be named 'HOST_CC'. We still +# use 'HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +tmp= +# shellcheck disable=SC2172 +trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 + +set_cc_for_build() { + # prevent multiple calls if $tmp is already set + test "$tmp" && return 0 + : "${TMPDIR=/tmp}" + # shellcheck disable=SC2039,SC3028 + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } + dummy=$tmp/dummy + case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in + ,,) echo "int x;" > "$dummy.c" + for driver in cc gcc c17 c99 c89 ; do + if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then + CC_FOR_BUILD=$driver + break + fi + done + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; + esac +} + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if test -f /.attbin/uname ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +case $UNAME_SYSTEM in +Linux|GNU|GNU/*) + LIBC=unknown + + set_cc_for_build + cat <<-EOF > "$dummy.c" + #if defined(__ANDROID__) + LIBC=android + #else + #include + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #elif defined(__GLIBC__) + LIBC=gnu + #elif defined(__LLVM_LIBC__) + LIBC=llvm + #else + #include + /* First heuristic to detect musl libc. */ + #ifdef __DEFINED_va_list + LIBC=musl + #endif + #endif + #endif + EOF + cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` + eval "$cc_set_libc" + + # Second heuristic to detect musl libc. + if [ "$LIBC" = unknown ] && + command -v ldd >/dev/null && + ldd --version 2>&1 | grep -q ^musl; then + LIBC=musl + fi + + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + if [ "$LIBC" = unknown ]; then + LIBC=gnu + fi + ;; +esac + +# Note: order is significant - the case branches are not exclusive. + +case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ + /sbin/sysctl -n hw.machine_arch 2>/dev/null || \ + /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \ + echo unknown)` + case $UNAME_MACHINE_ARCH in + aarch64eb) machine=aarch64_be-unknown ;; + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + earmv*) + arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` + endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` + machine=${arch}${endian}-unknown + ;; + *) machine=$UNAME_MACHINE_ARCH-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently (or will in the future) and ABI. + case $UNAME_MACHINE_ARCH in + earm*) + os=netbsdelf + ;; + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # Determine ABI tags. + case $UNAME_MACHINE_ARCH in + earm*) + expr='s/^earmv[0-9]/-eabi/;s/eb$//' + abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case $UNAME_VERSION in + Debian*) + release='-gnu' + ;; + *) + release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + GUESS=$machine-${os}${release}${abi-} + ;; + *:Bitrig:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE + ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE + ;; + *:SecBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE + ;; + *:LibertyBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE + ;; + *:MidnightBSD:*:*) + GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE + ;; + *:ekkoBSD:*:*) + GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE + ;; + *:SolidBSD:*:*) + GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE + ;; + *:OS108:*:*) + GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE + ;; + macppc:MirBSD:*:*) + GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE + ;; + *:MirBSD:*:*) + GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE + ;; + *:Sortix:*:*) + GUESS=$UNAME_MACHINE-unknown-sortix + ;; + *:Twizzler:*:*) + GUESS=$UNAME_MACHINE-unknown-twizzler + ;; + *:Redox:*:*) + GUESS=$UNAME_MACHINE-unknown-redox + ;; + mips:OSF1:*.*) + GUESS=mips-dec-osf1 + ;; + alpha:OSF1:*:*) + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + trap '' 0 + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case $ALPHA_CPU_TYPE in + "EV4 (21064)") + UNAME_MACHINE=alpha ;; + "EV4.5 (21064)") + UNAME_MACHINE=alpha ;; + "LCA4 (21066/21068)") + UNAME_MACHINE=alpha ;; + "EV5 (21164)") + UNAME_MACHINE=alphaev5 ;; + "EV5.6 (21164A)") + UNAME_MACHINE=alphaev56 ;; + "EV5.6 (21164PC)") + UNAME_MACHINE=alphapca56 ;; + "EV5.7 (21164PC)") + UNAME_MACHINE=alphapca57 ;; + "EV6 (21264)") + UNAME_MACHINE=alphaev6 ;; + "EV6.7 (21264A)") + UNAME_MACHINE=alphaev67 ;; + "EV6.8CB (21264C)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8AL (21264B)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8CX (21264D)") + UNAME_MACHINE=alphaev68 ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE=alphaev69 ;; + "EV7 (21364)") + UNAME_MACHINE=alphaev7 ;; + "EV7.9 (21364A)") + UNAME_MACHINE=alphaev79 ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + GUESS=$UNAME_MACHINE-dec-osf$OSF_REL + ;; + Amiga*:UNIX_System_V:4.0:*) + GUESS=m68k-unknown-sysv4 + ;; + *:[Aa]miga[Oo][Ss]:*:*) + GUESS=$UNAME_MACHINE-unknown-amigaos + ;; + *:[Mm]orph[Oo][Ss]:*:*) + GUESS=$UNAME_MACHINE-unknown-morphos + ;; + *:OS/390:*:*) + GUESS=i370-ibm-openedition + ;; + *:z/VM:*:*) + GUESS=s390-ibm-zvmoe + ;; + *:OS400:*:*) + GUESS=powerpc-ibm-os400 + ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + GUESS=arm-acorn-riscix$UNAME_RELEASE + ;; + arm*:riscos:*:*|arm*:RISCOS:*:*) + GUESS=arm-unknown-riscos + ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + GUESS=hppa1.1-hitachi-hiuxmpp + ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + case `(/bin/universe) 2>/dev/null` in + att) GUESS=pyramid-pyramid-sysv3 ;; + *) GUESS=pyramid-pyramid-bsd ;; + esac + ;; + NILE*:*:*:dcosx) + GUESS=pyramid-pyramid-svr4 + ;; + DRS?6000:unix:4.0:6*) + GUESS=sparc-icl-nx6 + ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) GUESS=sparc-icl-nx7 ;; + esac + ;; + s390x:SunOS:*:*) + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL + ;; + sun4H:SunOS:5.*:*) + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-hal-solaris2$SUN_REL + ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-sun-solaris2$SUN_REL + ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + GUESS=i386-pc-auroraux$UNAME_RELEASE + ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + set_cc_for_build + SUN_ARCH=i386 + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if test "$CC_FOR_BUILD" != no_compiler_found; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH=x86_64 + fi + fi + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=$SUN_ARCH-pc-solaris2$SUN_REL + ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-sun-solaris3$SUN_REL + ;; + sun4*:SunOS:*:*) + case `/usr/bin/arch -k` in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like '4.1.3-JL'. + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'` + GUESS=sparc-sun-sunos$SUN_REL + ;; + sun3*:SunOS:*:*) + GUESS=m68k-sun-sunos$UNAME_RELEASE + ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 + case `/bin/arch` in + sun3) + GUESS=m68k-sun-sunos$UNAME_RELEASE + ;; + sun4) + GUESS=sparc-sun-sunos$UNAME_RELEASE + ;; + esac + ;; + aushp:SunOS:*:*) + GUESS=sparc-auspex-sunos$UNAME_RELEASE + ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + GUESS=m68k-milan-mint$UNAME_RELEASE + ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + GUESS=m68k-hades-mint$UNAME_RELEASE + ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + GUESS=m68k-unknown-mint$UNAME_RELEASE + ;; + m68k:machten:*:*) + GUESS=m68k-apple-machten$UNAME_RELEASE + ;; + powerpc:machten:*:*) + GUESS=powerpc-apple-machten$UNAME_RELEASE + ;; + RISC*:Mach:*:*) + GUESS=mips-dec-mach_bsd4.3 + ;; + RISC*:ULTRIX:*:*) + GUESS=mips-dec-ultrix$UNAME_RELEASE + ;; + VAX*:ULTRIX*:*:*) + GUESS=vax-dec-ultrix$UNAME_RELEASE + ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + GUESS=clipper-intergraph-clix$UNAME_RELEASE + ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && + dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`"$dummy" "$dummyarg"` && + { echo "$SYSTEM_NAME"; exit; } + GUESS=mips-mips-riscos$UNAME_RELEASE + ;; + Motorola:PowerMAX_OS:*:*) + GUESS=powerpc-motorola-powermax + ;; + Motorola:*:4.3:PL8-*) + GUESS=powerpc-harris-powermax + ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + GUESS=powerpc-harris-powermax + ;; + Night_Hawk:Power_UNIX:*:*) + GUESS=powerpc-harris-powerunix + ;; + m88k:CX/UX:7*:*) + GUESS=m88k-harris-cxux7 + ;; + m88k:*:4*:R4*) + GUESS=m88k-motorola-sysv4 + ;; + m88k:*:3*:R3*) + GUESS=m88k-motorola-sysv3 + ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 + then + if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ + test "$TARGET_BINARY_INTERFACE"x = x + then + GUESS=m88k-dg-dgux$UNAME_RELEASE + else + GUESS=m88k-dg-dguxbcs$UNAME_RELEASE + fi + else + GUESS=i586-dg-dgux$UNAME_RELEASE + fi + ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + GUESS=m88k-dolphin-sysv3 + ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + GUESS=m88k-motorola-sysv3 + ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + GUESS=m88k-tektronix-sysv3 + ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + GUESS=m68k-tektronix-bsd + ;; + *:IRIX*:*:*) + IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'` + GUESS=mips-sgi-irix$IRIX_REL + ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + GUESS=romp-ibm-aix # uname -m gives an 8 hex-code CPU id + ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + GUESS=i386-ibm-aix + ;; + ia64:AIX:*:*) + if test -x /usr/bin/oslevel ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=$UNAME_VERSION.$UNAME_RELEASE + fi + GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV + ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" + #include + + int + main () + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` + then + GUESS=$SYSTEM_NAME + else + GUESS=rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + GUESS=rs6000-ibm-aix3.2.4 + else + GUESS=rs6000-ibm-aix3.2 + fi + ;; + *:AIX:*:[4567]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if test -x /usr/bin/lslpp ; then + IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \ + awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` + else + IBM_REV=$UNAME_VERSION.$UNAME_RELEASE + fi + GUESS=$IBM_ARCH-ibm-aix$IBM_REV + ;; + *:AIX:*:*) + GUESS=rs6000-ibm-aix + ;; + ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) + GUESS=romp-ibm-bsd4.4 + ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + GUESS=romp-ibm-bsd$UNAME_RELEASE # 4.3 with uname added to + ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + GUESS=rs6000-bull-bosx + ;; + DPX/2?00:B.O.S.:*:*) + GUESS=m68k-bull-sysv3 + ;; + 9000/[34]??:4.3bsd:1.*:*) + GUESS=m68k-hp-bsd + ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + GUESS=m68k-hp-bsd4.4 + ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` + case $UNAME_MACHINE in + 9000/31?) HP_ARCH=m68000 ;; + 9000/[34]??) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if test -x /usr/bin/getconf; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case $sc_cpu_version in + 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 + 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case $sc_kernel_bits in + 32) HP_ARCH=hppa2.0n ;; + 64) HP_ARCH=hppa2.0w ;; + '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 + esac ;; + esac + fi + if test "$HP_ARCH" = ""; then + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" + + #define _HPUX_SOURCE + #include + #include + + int + main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if test "$HP_ARCH" = hppa2.0w + then + set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH=hppa2.0w + else + HP_ARCH=hppa64 + fi + fi + GUESS=$HP_ARCH-hp-hpux$HPUX_REV + ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` + GUESS=ia64-hp-hpux$HPUX_REV + ;; + 3050*:HI-UX:*:*) + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && + { echo "$SYSTEM_NAME"; exit; } + GUESS=unknown-hitachi-hiuxwe2 + ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) + GUESS=hppa1.1-hp-bsd + ;; + 9000/8??:4.3bsd:*:*) + GUESS=hppa1.0-hp-bsd + ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + GUESS=hppa1.0-hp-mpeix + ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) + GUESS=hppa1.1-hp-osf + ;; + hp8??:OSF1:*:*) + GUESS=hppa1.0-hp-osf + ;; + i*86:OSF1:*:*) + if test -x /usr/sbin/sysversion ; then + GUESS=$UNAME_MACHINE-unknown-osf1mk + else + GUESS=$UNAME_MACHINE-unknown-osf1 + fi + ;; + parisc*:Lites*:*:*) + GUESS=hppa1.1-hp-lites + ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + GUESS=c1-convex-bsd + ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + GUESS=c34-convex-bsd + ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + GUESS=c38-convex-bsd + ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + GUESS=c4-convex-bsd + ;; + CRAY*Y-MP:*:*:*) + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=ymp-cray-unicos$CRAY_REL + ;; + CRAY*[A-Z]90:*:*:*) + echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=t90-cray-unicos$CRAY_REL + ;; + CRAY*T3E:*:*:*) + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=alphaev5-cray-unicosmk$CRAY_REL + ;; + CRAY*SV1:*:*:*) + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=sv1-cray-unicos$CRAY_REL + ;; + *:UNICOS/mp:*:*) + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=craynv-cray-unicosmp$CRAY_REL + ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` + GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} + ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` + GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} + ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE + ;; + sparc*:BSD/OS:*:*) + GUESS=sparc-unknown-bsdi$UNAME_RELEASE + ;; + *:BSD/OS:*:*) + GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE + ;; + arm:FreeBSD:*:*) + UNAME_PROCESSOR=`uname -p` + set_cc_for_build + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi + else + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf + fi + ;; + *:FreeBSD:*:*) + UNAME_PROCESSOR=`uname -p` + case $UNAME_PROCESSOR in + amd64) + UNAME_PROCESSOR=x86_64 ;; + i386) + UNAME_PROCESSOR=i586 ;; + esac + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL + ;; + i*:CYGWIN*:*) + GUESS=$UNAME_MACHINE-pc-cygwin + ;; + *:MINGW64*:*) + GUESS=$UNAME_MACHINE-pc-mingw64 + ;; + *:MINGW*:*) + GUESS=$UNAME_MACHINE-pc-mingw32 + ;; + *:MSYS*:*) + GUESS=$UNAME_MACHINE-pc-msys + ;; + i*:PW*:*) + GUESS=$UNAME_MACHINE-pc-pw32 + ;; + *:SerenityOS:*:*) + GUESS=$UNAME_MACHINE-pc-serenity + ;; + *:Interix*:*) + case $UNAME_MACHINE in + x86) + GUESS=i586-pc-interix$UNAME_RELEASE + ;; + authenticamd | genuineintel | EM64T) + GUESS=x86_64-unknown-interix$UNAME_RELEASE + ;; + IA64) + GUESS=ia64-unknown-interix$UNAME_RELEASE + ;; + esac ;; + i*:UWIN*:*) + GUESS=$UNAME_MACHINE-pc-uwin + ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + GUESS=x86_64-pc-cygwin + ;; + prep*:SunOS:5.*:*) + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=powerpcle-unknown-solaris2$SUN_REL + ;; + *:GNU:*:*) + # the GNU system + GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'` + GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'` + GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL + ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"` + GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC + ;; + x86_64:[Mm]anagarm:*:*|i?86:[Mm]anagarm:*:*) + GUESS="$UNAME_MACHINE-pc-managarm-mlibc" + ;; + *:[Mm]anagarm:*:*) + GUESS="$UNAME_MACHINE-unknown-managarm-mlibc" + ;; + *:Minix:*:*) + GUESS=$UNAME_MACHINE-unknown-minix + ;; + aarch64:Linux:*:*) + set_cc_for_build + CPU=$UNAME_MACHINE + LIBCABI=$LIBC + if test "$CC_FOR_BUILD" != no_compiler_found; then + ABI=64 + sed 's/^ //' << EOF > "$dummy.c" + #ifdef __ARM_EABI__ + #ifdef __ARM_PCS_VFP + ABI=eabihf + #else + ABI=eabi + #endif + #endif +EOF + cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` + eval "$cc_set_abi" + case $ABI in + eabi | eabihf) CPU=armv8l; LIBCABI=$LIBC$ABI ;; + esac + fi + GUESS=$CPU-unknown-linux-$LIBCABI + ;; + aarch64_be:Linux:*:*) + UNAME_MACHINE=aarch64_be + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC=gnulibc1 ; fi + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + arm*:Linux:*:*) + set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + else + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi + else + GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf + fi + fi + ;; + avr32*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + cris:Linux:*:*) + GUESS=$UNAME_MACHINE-axis-linux-$LIBC + ;; + crisv32:Linux:*:*) + GUESS=$UNAME_MACHINE-axis-linux-$LIBC + ;; + e2k:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + frv:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + hexagon:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + i*86:Linux:*:*) + GUESS=$UNAME_MACHINE-pc-linux-$LIBC + ;; + ia64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + k1om:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + kvx:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + kvx:cos:*:*) + GUESS=$UNAME_MACHINE-unknown-cos + ;; + kvx:mbr:*:*) + GUESS=$UNAME_MACHINE-unknown-mbr + ;; + loongarch32:Linux:*:* | loongarch64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + m32r*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + m68*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + mips:Linux:*:* | mips64:Linux:*:*) + set_cc_for_build + IS_GLIBC=0 + test x"${LIBC}" = xgnu && IS_GLIBC=1 + sed 's/^ //' << EOF > "$dummy.c" + #undef CPU + #undef mips + #undef mipsel + #undef mips64 + #undef mips64el + #if ${IS_GLIBC} && defined(_ABI64) + LIBCABI=gnuabi64 + #else + #if ${IS_GLIBC} && defined(_ABIN32) + LIBCABI=gnuabin32 + #else + LIBCABI=${LIBC} + #endif + #endif + + #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa64r6 + #else + #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa32r6 + #else + #if defined(__mips64) + CPU=mips64 + #else + CPU=mips + #endif + #endif + #endif + + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + MIPS_ENDIAN=el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + MIPS_ENDIAN= + #else + MIPS_ENDIAN= + #endif + #endif +EOF + cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'` + eval "$cc_set_vars" + test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } + ;; + mips64el:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + openrisc*:Linux:*:*) + GUESS=or1k-unknown-linux-$LIBC + ;; + or32:Linux:*:* | or1k*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + padre:Linux:*:*) + GUESS=sparc-unknown-linux-$LIBC + ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + GUESS=hppa64-unknown-linux-$LIBC + ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;; + PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;; + *) GUESS=hppa-unknown-linux-$LIBC ;; + esac + ;; + ppc64:Linux:*:*) + GUESS=powerpc64-unknown-linux-$LIBC + ;; + ppc:Linux:*:*) + GUESS=powerpc-unknown-linux-$LIBC + ;; + ppc64le:Linux:*:*) + GUESS=powerpc64le-unknown-linux-$LIBC + ;; + ppcle:Linux:*:*) + GUESS=powerpcle-unknown-linux-$LIBC + ;; + riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + s390:Linux:*:* | s390x:Linux:*:*) + GUESS=$UNAME_MACHINE-ibm-linux-$LIBC + ;; + sh64*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + sh*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + tile*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + vax:Linux:*:*) + GUESS=$UNAME_MACHINE-dec-linux-$LIBC + ;; + x86_64:Linux:*:*) + set_cc_for_build + CPU=$UNAME_MACHINE + LIBCABI=$LIBC + if test "$CC_FOR_BUILD" != no_compiler_found; then + ABI=64 + sed 's/^ //' << EOF > "$dummy.c" + #ifdef __i386__ + ABI=x86 + #else + #ifdef __ILP32__ + ABI=x32 + #endif + #endif +EOF + cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` + eval "$cc_set_abi" + case $ABI in + x86) CPU=i686 ;; + x32) LIBCABI=${LIBC}x32 ;; + esac + fi + GUESS=$CPU-pc-linux-$LIBCABI + ;; + xtensa*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + GUESS=i386-sequent-sysv4 + ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION + ;; + i*86:OS/2:*:*) + # If we were able to find 'uname', then EMX Unix compatibility + # is probably installed. + GUESS=$UNAME_MACHINE-pc-os2-emx + ;; + i*86:XTS-300:*:STOP) + GUESS=$UNAME_MACHINE-unknown-stop + ;; + i*86:atheos:*:*) + GUESS=$UNAME_MACHINE-unknown-atheos + ;; + i*86:syllable:*:*) + GUESS=$UNAME_MACHINE-pc-syllable + ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + GUESS=i386-unknown-lynxos$UNAME_RELEASE + ;; + i*86:*DOS:*:*) + GUESS=$UNAME_MACHINE-pc-msdosdjgpp + ;; + i*86:*:4.*:*) + UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL + else + GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL + fi + ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL + else + GUESS=$UNAME_MACHINE-pc-sysv32 + fi + ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configure will decide that + # this is a cross-build. + GUESS=i586-pc-msdosdjgpp + ;; + Intel:Mach:3*:*) + GUESS=i386-pc-mach3 + ;; + paragon:*:*:*) + GUESS=i860-intel-osf1 + ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + GUESS=i860-stardent-sysv$UNAME_RELEASE # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + GUESS=i860-unknown-sysv$UNAME_RELEASE # Unknown i860-SVR4 + fi + ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + GUESS=m68010-convergent-sysv + ;; + mc68k:UNIX:SYSTEM5:3.51m) + GUESS=m68k-convergent-sysv + ;; + M680?0:D-NIX:5.3:*) + GUESS=m68k-diab-dnix + ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + GUESS=m68k-unknown-lynxos$UNAME_RELEASE + ;; + mc68030:UNIX_System_V:4.*:*) + GUESS=m68k-atari-sysv4 + ;; + TSUNAMI:LynxOS:2.*:*) + GUESS=sparc-unknown-lynxos$UNAME_RELEASE + ;; + rs6000:LynxOS:2.*:*) + GUESS=rs6000-unknown-lynxos$UNAME_RELEASE + ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + GUESS=powerpc-unknown-lynxos$UNAME_RELEASE + ;; + SM[BE]S:UNIX_SV:*:*) + GUESS=mips-dde-sysv$UNAME_RELEASE + ;; + RM*:ReliantUNIX-*:*:*) + GUESS=mips-sni-sysv4 + ;; + RM*:SINIX-*:*:*) + GUESS=mips-sni-sysv4 + ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + GUESS=$UNAME_MACHINE-sni-sysv4 + else + GUESS=ns32k-sni-sysv + fi + ;; + PENTIUM:*:4.0*:*) # Unisys 'ClearPath HMP IX 4000' SVR4/MP effort + # says + GUESS=i586-unisys-sysv4 + ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + GUESS=hppa1.1-stratus-sysv4 + ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + GUESS=i860-stratus-sysv4 + ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + GUESS=$UNAME_MACHINE-stratus-vos + ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + GUESS=hppa1.1-stratus-vos + ;; + mc68*:A/UX:*:*) + GUESS=m68k-apple-aux$UNAME_RELEASE + ;; + news*:NEWS-OS:6*:*) + GUESS=mips-sony-newsos6 + ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if test -d /usr/nec; then + GUESS=mips-nec-sysv$UNAME_RELEASE + else + GUESS=mips-unknown-sysv$UNAME_RELEASE + fi + ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + GUESS=powerpc-be-beos + ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + GUESS=powerpc-apple-beos + ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + GUESS=i586-pc-beos + ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + GUESS=i586-pc-haiku + ;; + ppc:Haiku:*:*) # Haiku running on Apple PowerPC + GUESS=powerpc-apple-haiku + ;; + *:Haiku:*:*) # Haiku modern gcc (not bound by BeOS compat) + GUESS=$UNAME_MACHINE-unknown-haiku + ;; + SX-4:SUPER-UX:*:*) + GUESS=sx4-nec-superux$UNAME_RELEASE + ;; + SX-5:SUPER-UX:*:*) + GUESS=sx5-nec-superux$UNAME_RELEASE + ;; + SX-6:SUPER-UX:*:*) + GUESS=sx6-nec-superux$UNAME_RELEASE + ;; + SX-7:SUPER-UX:*:*) + GUESS=sx7-nec-superux$UNAME_RELEASE + ;; + SX-8:SUPER-UX:*:*) + GUESS=sx8-nec-superux$UNAME_RELEASE + ;; + SX-8R:SUPER-UX:*:*) + GUESS=sx8r-nec-superux$UNAME_RELEASE + ;; + SX-ACE:SUPER-UX:*:*) + GUESS=sxace-nec-superux$UNAME_RELEASE + ;; + Power*:Rhapsody:*:*) + GUESS=powerpc-apple-rhapsody$UNAME_RELEASE + ;; + *:Rhapsody:*:*) + GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE + ;; + arm64:Darwin:*:*) + GUESS=aarch64-apple-darwin$UNAME_RELEASE + ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` + case $UNAME_PROCESSOR in + unknown) UNAME_PROCESSOR=powerpc ;; + esac + if command -v xcode-select > /dev/null 2> /dev/null && \ + ! xcode-select --print-path > /dev/null 2> /dev/null ; then + # Avoid executing cc if there is no toolchain installed as + # cc will be a stub that puts up a graphical alert + # prompting the user to install developer tools. + CC_FOR_BUILD=no_compiler_found + else + set_cc_for_build + fi + if test "$CC_FOR_BUILD" != no_compiler_found; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc + if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_PPC >/dev/null + then + UNAME_PROCESSOR=powerpc + fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # uname -m returns i386 or x86_64 + UNAME_PROCESSOR=$UNAME_MACHINE + fi + GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE + ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = x86; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE + ;; + *:QNX:*:4*) + GUESS=i386-pc-qnx + ;; + NEO-*:NONSTOP_KERNEL:*:*) + GUESS=neo-tandem-nsk$UNAME_RELEASE + ;; + NSE-*:NONSTOP_KERNEL:*:*) + GUESS=nse-tandem-nsk$UNAME_RELEASE + ;; + NSR-*:NONSTOP_KERNEL:*:*) + GUESS=nsr-tandem-nsk$UNAME_RELEASE + ;; + NSV-*:NONSTOP_KERNEL:*:*) + GUESS=nsv-tandem-nsk$UNAME_RELEASE + ;; + NSX-*:NONSTOP_KERNEL:*:*) + GUESS=nsx-tandem-nsk$UNAME_RELEASE + ;; + *:NonStop-UX:*:*) + GUESS=mips-compaq-nonstopux + ;; + BS2000:POSIX*:*:*) + GUESS=bs2000-siemens-sysv + ;; + DS/*:UNIX_System_V:*:*) + GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE + ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "${cputype-}" = 386; then + UNAME_MACHINE=i386 + elif test "x${cputype-}" != x; then + UNAME_MACHINE=$cputype + fi + GUESS=$UNAME_MACHINE-unknown-plan9 + ;; + *:TOPS-10:*:*) + GUESS=pdp10-unknown-tops10 + ;; + *:TENEX:*:*) + GUESS=pdp10-unknown-tenex + ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + GUESS=pdp10-dec-tops20 + ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + GUESS=pdp10-xkl-tops20 + ;; + *:TOPS-20:*:*) + GUESS=pdp10-unknown-tops20 + ;; + *:ITS:*:*) + GUESS=pdp10-unknown-its + ;; + SEI:*:*:SEIUX) + GUESS=mips-sei-seiux$UNAME_RELEASE + ;; + *:DragonFly:*:*) + DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL + ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case $UNAME_MACHINE in + A*) GUESS=alpha-dec-vms ;; + I*) GUESS=ia64-dec-vms ;; + V*) GUESS=vax-dec-vms ;; + esac ;; + *:XENIX:*:SysV) + GUESS=i386-pc-xenix + ;; + i*86:skyos:*:*) + SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'` + GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL + ;; + i*86:rdos:*:*) + GUESS=$UNAME_MACHINE-pc-rdos + ;; + i*86:Fiwix:*:*) + GUESS=$UNAME_MACHINE-pc-fiwix + ;; + *:AROS:*:*) + GUESS=$UNAME_MACHINE-unknown-aros + ;; + x86_64:VMkernel:*:*) + GUESS=$UNAME_MACHINE-unknown-esx + ;; + amd64:Isilon\ OneFS:*:*) + GUESS=x86_64-unknown-onefs + ;; + *:Unleashed:*:*) + GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE + ;; + *:Ironclad:*:*) + GUESS=$UNAME_MACHINE-unknown-ironclad + ;; +esac + +# Do we have a guess based on uname results? +if test "x$GUESS" != x; then + echo "$GUESS" + exit +fi + +# No uname command or uname output not recognized. +set_cc_for_build +cat > "$dummy.c" < +#include +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#include +#if defined(_SIZE_T_) || defined(SIGLOST) +#include +#endif +#endif +#endif +int +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); +#endif + +#if defined (vax) +#if !defined (ultrix) +#include +#if defined (BSD) +#if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +#else +#if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#endif +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#else +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname un; + uname (&un); + printf ("vax-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("vax-dec-ultrix\n"); exit (0); +#endif +#endif +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname *un; + uname (&un); + printf ("mips-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("mips-dec-ultrix\n"); exit (0); +#endif +#endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. +test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } + +echo "$0: unable to guess system type" >&2 + +case $UNAME_MACHINE:$UNAME_SYSTEM in + mips:Linux | mips64:Linux) + # If we got here on MIPS GNU/Linux, output extra information. + cat >&2 <&2 <&2 </dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = "$UNAME_MACHINE" +UNAME_RELEASE = "$UNAME_RELEASE" +UNAME_SYSTEM = "$UNAME_SYSTEM" +UNAME_VERSION = "$UNAME_VERSION" +EOF +fi + +exit 1 + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/$GNUSTEP_MAKEFILES/config.sub b/$GNUSTEP_MAKEFILES/config.sub new file mode 100755 index 00000000..4aaae46f --- /dev/null +++ b/$GNUSTEP_MAKEFILES/config.sub @@ -0,0 +1,2354 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright 1992-2024 Free Software Foundation, Inc. + +# shellcheck disable=SC2006,SC2268,SC2162 # see below for rationale + +timestamp='2024-05-27' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). + + +# Please send patches to . +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# https://git.savannah.gnu.org/cgit/config.git/plain/config.sub + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +# The "shellcheck disable" line above the timestamp inhibits complaints +# about features and limitations of the classic Bourne shell that were +# superseded or lifted in POSIX. However, this script identifies a wide +# variety of pre-POSIX systems that do not have POSIX shells at all, and +# even some reasonably current systems (Solaris 10 as case-in-point) still +# have a pre-POSIX /bin/sh. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS + +Canonicalize a configuration name. + +Options: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright 1992-2024 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try '$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo "$1" + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Split fields of configuration type +saved_IFS=$IFS +IFS="-" read field1 field2 field3 field4 <&2 + exit 1 + ;; + *-*-*-*) + basic_machine=$field1-$field2 + basic_os=$field3-$field4 + ;; + *-*-*) + # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two + # parts + maybe_os=$field2-$field3 + case $maybe_os in + cloudabi*-eabi* \ + | kfreebsd*-gnu* \ + | knetbsd*-gnu* \ + | kopensolaris*-gnu* \ + | linux-* \ + | managarm-* \ + | netbsd*-eabi* \ + | netbsd*-gnu* \ + | nto-qnx* \ + | os2-emx* \ + | rtmk-nova* \ + | storm-chaos* \ + | uclinux-gnu* \ + | uclinux-uclibc* \ + | windows-* ) + basic_machine=$field1 + basic_os=$maybe_os + ;; + android-linux) + basic_machine=$field1-unknown + basic_os=linux-android + ;; + *) + basic_machine=$field1-$field2 + basic_os=$field3 + ;; + esac + ;; + *-*) + case $field1-$field2 in + # Shorthands that happen to contain a single dash + convex-c[12] | convex-c3[248]) + basic_machine=$field2-convex + basic_os= + ;; + decstation-3100) + basic_machine=mips-dec + basic_os= + ;; + *-*) + # Second component is usually, but not always the OS + case $field2 in + # Do not treat sunos as a manufacturer + sun*os*) + basic_machine=$field1 + basic_os=$field2 + ;; + # Manufacturers + 3100* \ + | 32* \ + | 3300* \ + | 3600* \ + | 7300* \ + | acorn \ + | altos* \ + | apollo \ + | apple \ + | atari \ + | att* \ + | axis \ + | be \ + | bull \ + | cbm \ + | ccur \ + | cisco \ + | commodore \ + | convergent* \ + | convex* \ + | cray \ + | crds \ + | dec* \ + | delta* \ + | dg \ + | digital \ + | dolphin \ + | encore* \ + | gould \ + | harris \ + | highlevel \ + | hitachi* \ + | hp \ + | ibm* \ + | intergraph \ + | isi* \ + | knuth \ + | masscomp \ + | microblaze* \ + | mips* \ + | motorola* \ + | ncr* \ + | news \ + | next \ + | ns \ + | oki \ + | omron* \ + | pc533* \ + | rebel \ + | rom68k \ + | rombug \ + | semi \ + | sequent* \ + | siemens \ + | sgi* \ + | siemens \ + | sim \ + | sni \ + | sony* \ + | stratus \ + | sun \ + | sun[234]* \ + | tektronix \ + | tti* \ + | ultra \ + | unicom* \ + | wec \ + | winbond \ + | wrs) + basic_machine=$field1-$field2 + basic_os= + ;; + zephyr*) + basic_machine=$field1-unknown + basic_os=$field2 + ;; + *) + basic_machine=$field1 + basic_os=$field2 + ;; + esac + ;; + esac + ;; + *) + # Convert single-component short-hands not valid as part of + # multi-component configurations. + case $field1 in + 386bsd) + basic_machine=i386-pc + basic_os=bsd + ;; + a29khif) + basic_machine=a29k-amd + basic_os=udi + ;; + adobe68k) + basic_machine=m68010-adobe + basic_os=scout + ;; + alliant) + basic_machine=fx80-alliant + basic_os= + ;; + altos | altos3068) + basic_machine=m68k-altos + basic_os= + ;; + am29k) + basic_machine=a29k-none + basic_os=bsd + ;; + amdahl) + basic_machine=580-amdahl + basic_os=sysv + ;; + amiga) + basic_machine=m68k-unknown + basic_os= + ;; + amigaos | amigados) + basic_machine=m68k-unknown + basic_os=amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + basic_os=sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + basic_os=sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + basic_os=bsd + ;; + aros) + basic_machine=i386-pc + basic_os=aros + ;; + aux) + basic_machine=m68k-apple + basic_os=aux + ;; + balance) + basic_machine=ns32k-sequent + basic_os=dynix + ;; + blackfin) + basic_machine=bfin-unknown + basic_os=linux + ;; + cegcc) + basic_machine=arm-unknown + basic_os=cegcc + ;; + cray) + basic_machine=j90-cray + basic_os=unicos + ;; + crds | unos) + basic_machine=m68k-crds + basic_os= + ;; + da30) + basic_machine=m68k-da30 + basic_os= + ;; + decstation | pmax | pmin | dec3100 | decstatn) + basic_machine=mips-dec + basic_os= + ;; + delta88) + basic_machine=m88k-motorola + basic_os=sysv3 + ;; + dicos) + basic_machine=i686-pc + basic_os=dicos + ;; + djgpp) + basic_machine=i586-pc + basic_os=msdosdjgpp + ;; + ebmon29k) + basic_machine=a29k-amd + basic_os=ebmon + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + basic_os=ose + ;; + gmicro) + basic_machine=tron-gmicro + basic_os=sysv + ;; + go32) + basic_machine=i386-pc + basic_os=go32 + ;; + h8300hms) + basic_machine=h8300-hitachi + basic_os=hms + ;; + h8300xray) + basic_machine=h8300-hitachi + basic_os=xray + ;; + h8500hms) + basic_machine=h8500-hitachi + basic_os=hms + ;; + harris) + basic_machine=m88k-harris + basic_os=sysv3 + ;; + hp300 | hp300hpux) + basic_machine=m68k-hp + basic_os=hpux + ;; + hp300bsd) + basic_machine=m68k-hp + basic_os=bsd + ;; + hppaosf) + basic_machine=hppa1.1-hp + basic_os=osf + ;; + hppro) + basic_machine=hppa1.1-hp + basic_os=proelf + ;; + i386mach) + basic_machine=i386-mach + basic_os=mach + ;; + isi68 | isi) + basic_machine=m68k-isi + basic_os=sysv + ;; + m68knommu) + basic_machine=m68k-unknown + basic_os=linux + ;; + magnum | m3230) + basic_machine=mips-mips + basic_os=sysv + ;; + merlin) + basic_machine=ns32k-utek + basic_os=sysv + ;; + mingw64) + basic_machine=x86_64-pc + basic_os=mingw64 + ;; + mingw32) + basic_machine=i686-pc + basic_os=mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + basic_os=mingw32ce + ;; + monitor) + basic_machine=m68k-rom68k + basic_os=coff + ;; + morphos) + basic_machine=powerpc-unknown + basic_os=morphos + ;; + moxiebox) + basic_machine=moxie-unknown + basic_os=moxiebox + ;; + msdos) + basic_machine=i386-pc + basic_os=msdos + ;; + msys) + basic_machine=i686-pc + basic_os=msys + ;; + mvs) + basic_machine=i370-ibm + basic_os=mvs + ;; + nacl) + basic_machine=le32-unknown + basic_os=nacl + ;; + ncr3000) + basic_machine=i486-ncr + basic_os=sysv4 + ;; + netbsd386) + basic_machine=i386-pc + basic_os=netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + basic_os=linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + basic_os=newsos + ;; + news1000) + basic_machine=m68030-sony + basic_os=newsos + ;; + necv70) + basic_machine=v70-nec + basic_os=sysv + ;; + nh3000) + basic_machine=m68k-harris + basic_os=cxux + ;; + nh[45]000) + basic_machine=m88k-harris + basic_os=cxux + ;; + nindy960) + basic_machine=i960-intel + basic_os=nindy + ;; + mon960) + basic_machine=i960-intel + basic_os=mon960 + ;; + nonstopux) + basic_machine=mips-compaq + basic_os=nonstopux + ;; + os400) + basic_machine=powerpc-ibm + basic_os=os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + basic_os=ose + ;; + os68k) + basic_machine=m68k-none + basic_os=os68k + ;; + paragon) + basic_machine=i860-intel + basic_os=osf + ;; + parisc) + basic_machine=hppa-unknown + basic_os=linux + ;; + psp) + basic_machine=mipsallegrexel-sony + basic_os=psp + ;; + pw32) + basic_machine=i586-unknown + basic_os=pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + basic_os=rdos + ;; + rdos32) + basic_machine=i386-pc + basic_os=rdos + ;; + rom68k) + basic_machine=m68k-rom68k + basic_os=coff + ;; + sa29200) + basic_machine=a29k-amd + basic_os=udi + ;; + sei) + basic_machine=mips-sei + basic_os=seiux + ;; + sequent) + basic_machine=i386-sequent + basic_os= + ;; + sps7) + basic_machine=m68k-bull + basic_os=sysv2 + ;; + st2000) + basic_machine=m68k-tandem + basic_os= + ;; + stratus) + basic_machine=i860-stratus + basic_os=sysv4 + ;; + sun2) + basic_machine=m68000-sun + basic_os= + ;; + sun2os3) + basic_machine=m68000-sun + basic_os=sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + basic_os=sunos4 + ;; + sun3) + basic_machine=m68k-sun + basic_os= + ;; + sun3os3) + basic_machine=m68k-sun + basic_os=sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + basic_os=sunos4 + ;; + sun4) + basic_machine=sparc-sun + basic_os= + ;; + sun4os3) + basic_machine=sparc-sun + basic_os=sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + basic_os=sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + basic_os=solaris2 + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + basic_os= + ;; + sv1) + basic_machine=sv1-cray + basic_os=unicos + ;; + symmetry) + basic_machine=i386-sequent + basic_os=dynix + ;; + t3e) + basic_machine=alphaev5-cray + basic_os=unicos + ;; + t90) + basic_machine=t90-cray + basic_os=unicos + ;; + toad1) + basic_machine=pdp10-xkl + basic_os=tops20 + ;; + tpf) + basic_machine=s390x-ibm + basic_os=tpf + ;; + udi29k) + basic_machine=a29k-amd + basic_os=udi + ;; + ultra3) + basic_machine=a29k-nyu + basic_os=sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + basic_os=none + ;; + vaxv) + basic_machine=vax-dec + basic_os=sysv + ;; + vms) + basic_machine=vax-dec + basic_os=vms + ;; + vsta) + basic_machine=i386-pc + basic_os=vsta + ;; + vxworks960) + basic_machine=i960-wrs + basic_os=vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + basic_os=vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + basic_os=vxworks + ;; + xbox) + basic_machine=i686-pc + basic_os=mingw32 + ;; + ymp) + basic_machine=ymp-cray + basic_os=unicos + ;; + *) + basic_machine=$1 + basic_os= + ;; + esac + ;; +esac + +# Decode 1-component or ad-hoc basic machines +case $basic_machine in + # Here we handle the default manufacturer of certain CPU types. It is in + # some cases the only manufacturer, in others, it is the most popular. + w89k) + cpu=hppa1.1 + vendor=winbond + ;; + op50n) + cpu=hppa1.1 + vendor=oki + ;; + op60c) + cpu=hppa1.1 + vendor=oki + ;; + ibm*) + cpu=i370 + vendor=ibm + ;; + orion105) + cpu=clipper + vendor=highlevel + ;; + mac | mpw | mac-mpw) + cpu=m68k + vendor=apple + ;; + pmac | pmac-mpw) + cpu=powerpc + vendor=apple + ;; + + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + cpu=m68000 + vendor=att + ;; + 3b*) + cpu=we32k + vendor=att + ;; + bluegene*) + cpu=powerpc + vendor=ibm + basic_os=cnk + ;; + decsystem10* | dec10*) + cpu=pdp10 + vendor=dec + basic_os=tops10 + ;; + decsystem20* | dec20*) + cpu=pdp10 + vendor=dec + basic_os=tops20 + ;; + delta | 3300 | delta-motorola | 3300-motorola | motorola-delta | motorola-3300) + cpu=m68k + vendor=motorola + ;; + # This used to be dpx2*, but that gets the RS6000-based + # DPX/20 and the x86-based DPX/2-100 wrong. See + # https://oldskool.silicium.org/stations/bull_dpx20.htm + # https://www.feb-patrimoine.com/english/bull_dpx2.htm + # https://www.feb-patrimoine.com/english/unix_and_bull.htm + dpx2 | dpx2[23]00 | dpx2[23]xx) + cpu=m68k + vendor=bull + ;; + dpx2100 | dpx21xx) + cpu=i386 + vendor=bull + ;; + dpx20) + cpu=rs6000 + vendor=bull + ;; + encore | umax | mmax) + cpu=ns32k + vendor=encore + ;; + elxsi) + cpu=elxsi + vendor=elxsi + basic_os=${basic_os:-bsd} + ;; + fx2800) + cpu=i860 + vendor=alliant + ;; + genix) + cpu=ns32k + vendor=ns + ;; + h3050r* | hiux*) + cpu=hppa1.1 + vendor=hitachi + basic_os=hiuxwe2 + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + cpu=m68000 + vendor=hp + ;; + hp9k3[2-9][0-9]) + cpu=m68k + vendor=hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + cpu=hppa1.1 + vendor=hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + i*86v32) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv32 + ;; + i*86v4*) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv4 + ;; + i*86v) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv + ;; + i*86sol2) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=solaris2 + ;; + j90 | j90-cray) + cpu=j90 + vendor=cray + basic_os=${basic_os:-unicos} + ;; + iris | iris4d) + cpu=mips + vendor=sgi + case $basic_os in + irix*) + ;; + *) + basic_os=irix4 + ;; + esac + ;; + miniframe) + cpu=m68000 + vendor=convergent + ;; + *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) + cpu=m68k + vendor=atari + basic_os=mint + ;; + news-3600 | risc-news) + cpu=mips + vendor=sony + basic_os=newsos + ;; + next | m*-next) + cpu=m68k + vendor=next + ;; + np1) + cpu=np1 + vendor=gould + ;; + op50n-* | op60c-*) + cpu=hppa1.1 + vendor=oki + basic_os=proelf + ;; + pa-hitachi) + cpu=hppa1.1 + vendor=hitachi + basic_os=hiuxwe2 + ;; + pbd) + cpu=sparc + vendor=tti + ;; + pbb) + cpu=m68k + vendor=tti + ;; + pc532) + cpu=ns32k + vendor=pc532 + ;; + pn) + cpu=pn + vendor=gould + ;; + power) + cpu=power + vendor=ibm + ;; + ps2) + cpu=i386 + vendor=ibm + ;; + rm[46]00) + cpu=mips + vendor=siemens + ;; + rtpc | rtpc-*) + cpu=romp + vendor=ibm + ;; + sde) + cpu=mipsisa32 + vendor=sde + basic_os=${basic_os:-elf} + ;; + simso-wrs) + cpu=sparclite + vendor=wrs + basic_os=vxworks + ;; + tower | tower-32) + cpu=m68k + vendor=ncr + ;; + vpp*|vx|vx-*) + cpu=f301 + vendor=fujitsu + ;; + w65) + cpu=w65 + vendor=wdc + ;; + w89k-*) + cpu=hppa1.1 + vendor=winbond + basic_os=proelf + ;; + none) + cpu=none + vendor=none + ;; + leon|leon[3-9]) + cpu=sparc + vendor=$basic_machine + ;; + leon-*|leon[3-9]-*) + cpu=sparc + vendor=`echo "$basic_machine" | sed 's/-.*//'` + ;; + + *-*) + saved_IFS=$IFS + IFS="-" read cpu vendor <&2 + exit 1 + ;; + esac + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $vendor in + digital*) + vendor=dec + ;; + commodore*) + vendor=cbm + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if test x"$basic_os" != x +then + +# First recognize some ad-hoc cases, or perhaps split kernel-os, or else just +# set os. +obj= +case $basic_os in + gnu/linux*) + kernel=linux + os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'` + ;; + os2-emx) + kernel=os2 + os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'` + ;; + nto-qnx*) + kernel=nto + os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'` + ;; + *-*) + saved_IFS=$IFS + IFS="-" read kernel os <&2 + fi + ;; + *) + echo "Invalid configuration '$1': OS '$os' not recognized" 1>&2 + exit 1 + ;; +esac + +case $obj in + aout* | coff* | elf* | pe*) + ;; + '') + # empty is fine + ;; + *) + echo "Invalid configuration '$1': Machine code format '$obj' not recognized" 1>&2 + exit 1 + ;; +esac + +# Here we handle the constraint that a (synthetic) cpu and os are +# valid only in combination with each other and nowhere else. +case $cpu-$os in + # The "javascript-unknown-ghcjs" triple is used by GHC; we + # accept it here in order to tolerate that, but reject any + # variations. + javascript-ghcjs) + ;; + javascript-* | *-ghcjs) + echo "Invalid configuration '$1': cpu '$cpu' is not valid with os '$os$obj'" 1>&2 + exit 1 + ;; +esac + +# As a final step for OS-related things, validate the OS-kernel combination +# (given a valid OS), if there is a kernel. +case $kernel-$os-$obj in + linux-gnu*- | linux-android*- | linux-dietlibc*- | linux-llvm*- \ + | linux-mlibc*- | linux-musl*- | linux-newlib*- \ + | linux-relibc*- | linux-uclibc*- | linux-ohos*- ) + ;; + uclinux-uclibc*- | uclinux-gnu*- ) + ;; + managarm-mlibc*- | managarm-kernel*- ) + ;; + windows*-msvc*-) + ;; + -dietlibc*- | -llvm*- | -mlibc*- | -musl*- | -newlib*- | -relibc*- \ + | -uclibc*- ) + # These are just libc implementations, not actual OSes, and thus + # require a kernel. + echo "Invalid configuration '$1': libc '$os' needs explicit kernel." 1>&2 + exit 1 + ;; + -kernel*- ) + echo "Invalid configuration '$1': '$os' needs explicit kernel." 1>&2 + exit 1 + ;; + *-kernel*- ) + echo "Invalid configuration '$1': '$kernel' does not support '$os'." 1>&2 + exit 1 + ;; + *-msvc*- ) + echo "Invalid configuration '$1': '$os' needs 'windows'." 1>&2 + exit 1 + ;; + kfreebsd*-gnu*- | knetbsd*-gnu*- | netbsd*-gnu*- | kopensolaris*-gnu*-) + ;; + vxworks-simlinux- | vxworks-simwindows- | vxworks-spe-) + ;; + nto-qnx*-) + ;; + os2-emx-) + ;; + rtmk-nova-) + ;; + *-eabi*- | *-gnueabi*-) + ;; + none--*) + # None (no kernel, i.e. freestanding / bare metal), + # can be paired with an machine code file format + ;; + -*-) + # Blank kernel with real OS is always fine. + ;; + --*) + # Blank kernel and OS with real machine code file format is always fine. + ;; + *-*-*) + echo "Invalid configuration '$1': Kernel '$kernel' not known to work with OS '$os'." 1>&2 + exit 1 + ;; +esac + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +case $vendor in + unknown) + case $cpu-$os in + *-riscix*) + vendor=acorn + ;; + *-sunos* | *-solaris*) + vendor=sun + ;; + *-cnk* | *-aix*) + vendor=ibm + ;; + *-beos*) + vendor=be + ;; + *-hpux*) + vendor=hp + ;; + *-mpeix*) + vendor=hp + ;; + *-hiux*) + vendor=hitachi + ;; + *-unos*) + vendor=crds + ;; + *-dgux*) + vendor=dg + ;; + *-luna*) + vendor=omron + ;; + *-genix*) + vendor=ns + ;; + *-clix*) + vendor=intergraph + ;; + *-mvs* | *-opened*) + vendor=ibm + ;; + *-os400*) + vendor=ibm + ;; + s390-* | s390x-*) + vendor=ibm + ;; + *-ptx*) + vendor=sequent + ;; + *-tpf*) + vendor=ibm + ;; + *-vxsim* | *-vxworks* | *-windiss*) + vendor=wrs + ;; + *-aux*) + vendor=apple + ;; + *-hms*) + vendor=hitachi + ;; + *-mpw* | *-macos*) + vendor=apple + ;; + *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*) + vendor=atari + ;; + *-vos*) + vendor=stratus + ;; + esac + ;; +esac + +echo "$cpu-$vendor${kernel:+-$kernel}${os:+-$os}${obj:+-$obj}" +exit + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/ChangeLog b/ChangeLog index 1b802df8..11690c2b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,195 @@ * configure * configure.ac: Autoupdate and autoconf. +2026-05-15 James Carthew + + * Headers/wayland/WaylandOpenGL.h: + * Headers/wayland/WaylandServer.h: + * Source/wayland/WaylandDragView.m: + * Source/wayland/WaylandGLContext.m: + * Source/wayland/WaylandServer+Cursor.m: + * Source/wayland/WaylandServer+Keyboard.m: + * Source/wayland/WaylandServer.m: + Wayland: fix mouse tracking over OpenGL subsurfaces with multiple GL + views. Replace per-window subsurface offset with a per-surface + wl_surface_binding stored as wl_surface user data so each + WaylandGLContext has independent offset tracking. + pointer_handle_enter reads the offset via surface_get_offset() and + stores it in pointer.focus_offset_x/y; pointer_handle_motion applies + the stored offset for correct parent-window coordinate translation. + Folds in the keyboard_handle_enter NULL-surface guard. + + * Source/wayland/WaylandDragView.m: + Wayland: fix in-process drag-and-drop in GNUstep applications. + postDragEvent: was discarding all events except NSLeftMouseUp once + the Wayland data device was available; pass NSAppKitDefined events + through to super so in-process draggingEntered:/draggingUpdated:/ + performDragOperation: callbacks reach the target view. Fix + device_enter/motion/leave to skip same-process re-posting. + + * Source/wayland/WaylandServer+Cursor.m: + Wayland: fill all wl_pointer_listener slots to prevent SIGABRT. + wayland-client 1.24 defines 11 event slots; add no-op stubs for + frame, axis_source, axis_stop, and axis_discrete handlers. + + * Source/wayland/WaylandServer+Seat.m: + Wayland: add wl_seat.name handler to prevent SIGABRT on startup. + Bumping the seat binding to v5 causes wl_seat.name to be delivered + during the initial roundtrip. Add seat_handle_name stub so + libwayland does not abort on a NULL dispatcher slot. + + * Tools/wayland-smoke-test.sh: + Wayland: extend smoke-test harness to Milestone 5 (buffer lifecycle + and stability checks). + + * Headers/cairo/WaylandCairoShmSurface.h: + * Source/cairo/WaylandCairoShmSurface.m: + * Source/wayland/WaylandServer+Xdgshell.m: + * Source/wayland/WaylandServer.m: + Wayland M5: buffer lifecycle hardening and surface destruction + ordering. Add needs_repaint flag and owner back-pointers to + pool_buffer. Fix FD leak in finishBuffer. Re-attach buffers on + compositor release when a repaint is pending. Guard against size + mismatch and destroyed surfaces in the release callback. + + * Headers/wayland/WaylandServer.h: + * Source/wayland/WaylandServer+Output.m: + * Source/wayland/WaylandServer.m: + Wayland M4: output hotplug and scale/geometry reconfiguration. Add + effective_width/height, name, description, and configured fields to + struct output. Rewrite output_listener: recompute logical dimensions + for scale and rotation, clamp windows to new bounds, and post + NSApplicationDidChangeScreenParametersNotification. Bind wl_output + at version 4. + + * Headers/wayland/WaylandServer.h: + * Source/wayland/WaylandServer+Cursor.m: + * Source/wayland/WaylandServer.m: + Wayland M3: extra mouse buttons and per-frame scroll accumulation. + Map BTN_SIDE/EXTRA/FORWARD/BACK/TASK to NSOtherMouseDown/Up. Fix + buttonNumber to use button-BTN_LEFT offset. Bump wl_seat to v5 and + accumulate axis deltas per compositor frame before dispatching a + single NSScrollWheel event. + + * Headers/wayland/WaylandInputServer.h: + * Headers/wayland/WaylandServer.h: + * Source/wayland/GNUmakefile: + * Source/wayland/WaylandInputServer.m: + * Source/wayland/WaylandServer+Keyboard.m: + * Source/wayland/WaylandServer.m: + * Source/wayland/text-input-unstable-v3-protocol.c: + Wayland M2: IME/preedit support via zwp_text_input_v3. Generate and + wire text-input-unstable-v3 protocol. Fix XKB UTF-8 conversion by + replacing the broken &sym cast with xkb_state_key_get_utf8. Add + WaylandInputServer for preedit spot/rect management. Bind + text_input_manager and create zwp_text_input_v3 from the seat. + + * Headers/wayland/WaylandDragView.h: + * Headers/wayland/WaylandServer.h: + * Source/cairo/WaylandCairoShmSurface.m: + * Source/wayland/WaylandDragView.m: + * Source/wayland/WaylandInputServer.m: + * Source/wayland/WaylandServer+Cursor.m: + * Source/wayland/WaylandServer+Keyboard.m: + * Source/wayland/WaylandServer+Output.m: + * Source/wayland/WaylandServer.m: + * Tools/wayland-smoke-test.sh: + Wayland M0+M1: add targeted debug log categories and inter-process + DnD. Replace bare NSDebugLog with NSDebugMLLog/NSDebugFLLog across + Wayland sources (categories: WaylandDnD, WaylandIME, WaylandPointer, + WaylandScroll, WaylandOutput). Add Tools/wayland-smoke-test.sh + integration harness. Implement inter-process drag-and-drop via + wl_data_device: bind data_device_manager, wire source/offer + listeners, implement draggingSourceOperationMask, + addDragTypes:toWindow:, and performDragOperation:. + + * Headers/wayland/WaylandServer.h: + * Source/wayland/GNUmakefile: + * Source/wayland/WaylandServer+Cursor.m: + * Source/wayland/WaylandServer+Xdgshell.m: + * Source/wayland/WaylandServer.m: + Wayland: update popup menu handling; add xdg-decoration-unstable-v1 + protocol support for server-side window decorations. + +2026-04-30 Joe Maloney + + * Headers/opal/OpalSurface.h: + * Source/opal/OpalContext.m: + * Source/opal/OpalFontInfo.m: + * Source/opal/OpalGState.m: + * Source/opal/OpalSurface.m: + Opal backend: restore functionality. Fix font hinting, gradients, + blend modes, gray-to-RGB colour conversion, and lazy surface + creation. Fix window movement trails by flushing backing and X11 + contexts in handleExposeRect. Make X11 surface attachment + self-contained. + +2026-04-30 James Carthew + + * Headers/wayland/WaylandDragView.h: + * Headers/wayland/WaylandInputServer.h: + * Headers/wayland/WaylandOpenGL.h: + * Headers/wayland/WaylandServer.h: + * Source/wayland/GNUmakefile: + * Source/wayland/WaylandDragView.m: + * Source/wayland/WaylandGLContext.m: + * Source/wayland/WaylandGLPixelFormat.m: + * Source/wayland/WaylandInputServer.m: + * Source/wayland/WaylandServer+Keyboard.m: + * Source/wayland/WaylandServer+Xdgshell.m: + * Source/wayland/WaylandServer.m: + Wayland: complete OpenGL subsurface integration. Use wl_subsurface + to host GL views, implement frame callbacks and damage tracking, fix + context sharing, and integrate WaylandInputServer for input method + support alongside GL contexts. + +2026-04-20 James Carthew + + * Source/wayland/WaylandServer+Cursor.m: + Wayland: fix cursor handling when moving between GL subsurfaces and + main window surfaces. + +2026-04-19 James Carthew + + * Source/wayland/WaylandGLContext.m: + * Source/wayland/WaylandServer+Cursor.m: + Wayland: fix crash in cursor handling when the pointer enters a GL + subsurface. + + * Headers/wayland/WaylandOpenGL.h: + * Headers/wayland/WaylandServer.h: + * Source/GNUmakefile.preamble: + * Source/wayland/GNUmakefile.preamble: + * Source/wayland/WaylandGLContext.m: + * Source/wayland/WaylandServer.m: + Wayland: complete OpenGL context implementation. Add EGL surface + creation on wl_subsurface, frame-callback-driven buffer swap, and + usesOpenGL tracking in struct window. + +2026-04-18 James Carthew + + * Source/wayland/WaylandGLContext.m: + Wayland: fix noisy GL attach failures on unmapped surfaces; plug a + retain-count leak on early error paths in WaylandGLContext. + + * Source/wayland/WaylandGLContext.m: + Wayland: rebind OpenGL context to the correct native window when a + view is reparented before makeCurrentContext is called. + + * Headers/wayland/WaylandOpenGL.h: + * Headers/wayland/WaylandServer.h: + * Source/GNUmakefile.preamble: + * Source/wayland/GNUmakefile: + * Source/wayland/GNUmakefile.preamble: + * Source/wayland/WaylandGLContext.m: + * Source/wayland/WaylandGLPixelFormat.m: + * Source/wayland/WaylandServer.m: + * configure.ac: + Wayland: initial OpenGL support via EGL and wl_subsurface. Add new + WaylandGLContext and WaylandGLPixelFormat classes implementing + NSOpenGLContext/NSOpenGLPixelFormat for the Wayland backend. Link + against libGL, libEGL, and libwayland-egl. + 2025-12-18 Richard Frith-Macdonald * Source/x11/XGServer.m: diff --git a/Headers/cairo/WaylandCairoShmSurface.h b/Headers/cairo/WaylandCairoShmSurface.h index 5fe871bc..8ce6d5fd 100644 --- a/Headers/cairo/WaylandCairoShmSurface.h +++ b/Headers/cairo/WaylandCairoShmSurface.h @@ -41,6 +41,13 @@ struct pool_buffer void *data; size_t size; bool busy; + + /* Repaint-on-release: set when handleExposeRect skips attach because + * the compositor still holds the buffer. The release callback re-attaches + * and commits the buffer so the missed frame is not lost. */ + bool needs_repaint; + struct wl_surface *owner_surface; /* the wl_surface this buffer is on */ + struct wl_display *owner_display; /* for flushing after re-attach */ }; struct pool_buffer * @@ -51,6 +58,9 @@ createShmBuffer(int width, int height, struct wl_shm *shm); struct pool_buffer *pbuffer; } - (void) destroySurface; +/** Clear the wl_surface back-pointer so the release callback does not + * write to a destroyed Wayland proxy after destroySurfaceRole:. */ +- (void) clearOwnerSurface; @end #endif diff --git a/Headers/wayland/WaylandDragView.h b/Headers/wayland/WaylandDragView.h new file mode 100644 index 00000000..7eb814bb --- /dev/null +++ b/Headers/wayland/WaylandDragView.h @@ -0,0 +1,47 @@ +/* WaylandDragView - Drag and Drop for Wayland backend + + Copyright (C) 2024 Free Software Foundation, Inc. + + This file is part of the GNUstep Backend. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; see the file COPYING.LIB. + If not, see or write to the + Free Software Foundation, 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _WaylandDragView_h_INCLUDE +#define _WaylandDragView_h_INCLUDE + +#include +#include +#include +#include + +@interface WaylandDragView : GSDragView + ++ (id) sharedDragView; + +- (void) updateDragInfoFromEvent: (NSEvent *)event; +- (void) resetDragInfo; + +/** Set up NSDraggingInfo state for an inbound drag from an external app. + * Called from the wl_data_device.enter C callback before posting + * GSAppKitDraggingEnter to the target window. */ +- (void) setupInboundDragWithPasteboard: (NSPasteboard *)pb + operation: (NSDragOperation)op; + +@end + +#endif /* _WaylandDragView_h_INCLUDE */ diff --git a/Headers/wayland/WaylandInputServer.h b/Headers/wayland/WaylandInputServer.h new file mode 100644 index 00000000..d507b780 --- /dev/null +++ b/Headers/wayland/WaylandInputServer.h @@ -0,0 +1,57 @@ +/* WaylandInputServer - Keyboard input handling for Wayland backend + + Copyright (C) 2024 Free Software Foundation, Inc. + + This file is part of the GNUstep Backend. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; see the file COPYING.LIB. + If not, see or write to the + Free Software Foundation, 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _WaylandInputServer_h_INCLUDE +#define _WaylandInputServer_h_INCLUDE + +#include +#include "wayland/WaylandServer.h" + +@interface WaylandInputServer : NSInputServer +{ + id delegate; + NSString *server_name; + int focused_window_id; + WaylandConfig *wlconfig; /* back-pointer for IME geometry calls */ +} + +- (id) initWithDelegate: (id)aDelegate name: (NSString *)name; +- (void) setFocusedWindowId: (int)windowId; +- (int) focusedWindowId; +- (void) setWlconfig: (WaylandConfig *)config; + +@end + +@interface WaylandInputServer (InputMethod) +- (NSString *) inputMethodStyle; +- (NSString *) fontSize: (int *)size; +- (BOOL) clientWindowRect: (NSRect *)rect; +- (BOOL) statusArea: (NSRect *)rect; +- (BOOL) preeditArea: (NSRect *)rect; +- (BOOL) preeditSpot: (NSPoint *)p; +- (BOOL) setStatusArea: (NSRect *)rect; +- (BOOL) setPreeditArea: (NSRect *)rect; +- (BOOL) setPreeditSpot: (NSPoint *)p; +@end + +#endif /* _WaylandInputServer_h_INCLUDE */ diff --git a/Headers/wayland/WaylandOpenGL.h b/Headers/wayland/WaylandOpenGL.h new file mode 100644 index 00000000..f9c6d5dc --- /dev/null +++ b/Headers/wayland/WaylandOpenGL.h @@ -0,0 +1,120 @@ +/* -*-ObjC-*- */ +/* WaylandOpenGL - NSOpenGL management for Wayland backend + + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of the GNUstep Backend. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; see the file COPYING.LIB. + If not, see or write to the + Free Software Foundation, 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _GNUstep_H_WaylandOpenGL_ +#define _GNUstep_H_WaylandOpenGL_ + +#include +#include +#include +#include + +@class NSView; +struct wl_egl_window; +struct window; + +@interface WaylandGLContext : NSOpenGLContext +{ + NSOpenGLPixelFormat *_pixelFormat; + NSView *_view; + NSOpenGLContext *_shareContext; + struct window *_window; + struct wl_surface *_glSurface; + struct wl_subsurface *_glSubsurface; + struct wl_surface_binding *_glSurfaceBinding; /* malloc'd binding for _glSurface */ + struct wl_egl_window *_eglWindow; + EGLDisplay _eglDisplay; + EGLContext _eglContext; + EGLSurface _eglSurface; + int _swapInterval; + + /* EGL/GL extension support */ + BOOL _extensionsLoaded; + BOOL _hasDmaBufImport; + BOOL _hasDmaBufImportModifiers; + BOOL _hasExternalTexture; + PFNEGLCREATEIMAGEKHRPROC _pfnCreateImageKHR; + PFNEGLDESTROYIMAGEKHRPROC _pfnDestroyImageKHR; + PFNEGLQUERYDMABUFFORMATSEXTPROC _pfnQueryDmaBufFormats; + PFNEGLQUERYDMABUFMODIFIERSEXTPROC _pfnQueryDmaBufModifiers; + /* GL_OES_EGL_image — stored as void* to avoid pulling in GLES2 headers */ + void *_pfnGLImageTargetTexture2D; +} + +/* Returns YES if EGL_EXT_image_dma_buf_import is available. */ +- (BOOL)supportsDmaBufImport; + +/* Returns YES if GL_OES_EGL_image / GL_OES_EGL_image_external are available. */ +- (BOOL)supportsExternalTexture; + +/* Returns the EGLDisplay used by this context (EGL_NO_DISPLAY if not yet initialised). */ +- (EGLDisplay)eglDisplay; + +/* + * Create an EGLImageKHR backed by a single-plane DMA-BUF. + * fourcc is a DRM FourCC pixel format code (e.g. DRM_FORMAT_ARGB8888). + * Returns EGL_NO_IMAGE_KHR on failure. + */ +- (EGLImageKHR)createEGLImageFromDmaBufFd:(int)fd + width:(int)width + height:(int)height + stride:(int)stride + offset:(int)offset + fourcc:(uint32_t)fourcc; + +/* + * Same as above but also passes the 64-bit DRM format modifier. + * Requires EGL_EXT_image_dma_buf_import_modifiers on the display. + */ +- (EGLImageKHR)createEGLImageFromDmaBufFd:(int)fd + width:(int)width + height:(int)height + stride:(int)stride + offset:(int)offset + fourcc:(uint32_t)fourcc + modifier:(uint64_t)modifier; + +/* Destroy an EGLImageKHR previously created by the methods above. */ +- (void)destroyEGLImage:(EGLImageKHR)image; + +/* + * Bind image to the GL_TEXTURE_EXTERNAL_OES texture object texId. + * The caller must bind texId to GL_TEXTURE_EXTERNAL_OES before calling this, + * or call glBindTexture(GL_TEXTURE_EXTERNAL_OES, texId) themselves. + * Requires supportsExternalTexture == YES. + */ +- (void)bindEGLImage:(EGLImageKHR)image toExternalTexture:(unsigned int)texId; + +@end + +@interface WaylandGLPixelFormat : NSOpenGLPixelFormat +{ + NSOpenGLPixelFormatAttribute *_attributes; + NSUInteger _attributeCount; +} + +- (EGLConfig)eglConfigForDisplay:(EGLDisplay)eglDisplay; +@end + +#endif diff --git a/Headers/wayland/WaylandServer.h b/Headers/wayland/WaylandServer.h index 1f033651..bf38333e 100644 --- a/Headers/wayland/WaylandServer.h +++ b/Headers/wayland/WaylandServer.h @@ -42,6 +42,40 @@ #include "wayland/xdg-shell-client-protocol.h" #include "wayland/wlr-layer-shell-client-protocol.h" +#include "wayland/xdg-decoration-unstable-v1-client-protocol.h" +#include "wayland/text-input-unstable-v3-client-protocol.h" + +/* User data stored on every wl_surface owned by GNUstep. + * For main window surfaces offset_x/y are 0.0. + * For GL subsurfaces they hold the subsurface position within the parent window + * (Wayland Y-down coordinates), so pointer event coordinates can be translated + * to parent-window space. Each WaylandGLContext malloc's its own binding so + * multiple GL views in the same window each have independent offsets. */ +struct wl_surface_binding +{ + struct window *window; + float offset_x; + float offset_y; +}; + +/* Retrieve the window from any GNUstep-owned wl_surface. */ +static inline struct window * +surface_get_window(struct wl_surface *surface) +{ + struct wl_surface_binding *b + = (struct wl_surface_binding *)wl_surface_get_user_data(surface); + return b ? b->window : NULL; +} + +/* Retrieve the subsurface-to-window offset; returns (0,0) for main surfaces. */ +static inline void +surface_get_offset(struct wl_surface *surface, float *ox, float *oy) +{ + struct wl_surface_binding *b + = (struct wl_surface_binding *)wl_surface_get_user_data(surface); + *ox = b ? b->offset_x : 0.0f; + *oy = b ? b->offset_y : 0.0f; +} struct pointer { @@ -59,9 +93,24 @@ struct pointer uint32_t axis_source; - uint32_t serial; - struct window *focus; - struct window *captured; + uint32_t serial; + struct window *focus; + struct window *captured; + /* Subsurface-to-window offset captured at pointer-enter time. + * Zero for main window surfaces; non-zero when the pointer entered a GL + * subsurface. Applied to sx/sy in motion/button handlers so all AppKit + * coordinates are expressed in parent-window space. */ + float focus_offset_x; + float focus_offset_y; + + /* Per-frame axis accumulation (cleared after each wl_pointer.frame event). + * Populated by axis/axis_discrete; dispatched as one NSScrollWheel in frame. */ + BOOL frame_has_axis; + float frame_deltaX; + float frame_deltaY; + int frame_discrete_x; /* discrete scroll steps this frame, horizontal */ + int frame_discrete_y; /* discrete scroll steps this frame, vertical */ + uint32_t frame_time; /* timestamp of the last axis event in this frame */ }; @@ -75,15 +124,17 @@ struct cursor typedef struct _WaylandConfig { - struct wl_display *display; - struct wl_registry *registry; - struct wl_compositor *compositor; - struct wl_shell *shell; - struct wl_shm *shm; - struct wl_seat *seat; - struct wl_keyboard *keyboard; - struct xdg_wm_base *wm_base; - struct zwlr_layer_shell_v1 *layer_shell; + struct wl_display *display; + struct wl_registry *registry; + struct wl_compositor *compositor; + struct wl_shell *shell; + struct wl_shm *shm; + struct wl_seat *seat; + struct wl_keyboard *keyboard; + struct xdg_wm_base *wm_base; + struct zwlr_layer_shell_v1 *layer_shell; + struct wl_subcompositor *subcompositor; + struct zxdg_decoration_manager_v1 *decoration_manager; int seat_version; struct wl_list output_list; @@ -95,6 +146,11 @@ typedef struct _WaylandConfig // last event serial from pointer or keyboard uint32_t event_serial; +// cursor global position tracking (output-relative, Wayland Y-down) + BOOL cursor_global_valid; + float cursor_global_x; + float cursor_global_y; + // cursor struct wl_cursor_theme *cursor_theme; @@ -105,6 +161,9 @@ typedef struct _WaylandConfig struct pointer pointer; float mouse_scroll_multiplier; +// keyboard focus (set by keyboard_handle_enter/leave) + struct window *keyboard_focus; + // keyboard struct xkb_context *xkb_context; struct @@ -117,6 +176,42 @@ typedef struct _WaylandConfig } xkb; int modifiers; + /* zwp_text_input_v3 — input method / preedit support */ + struct zwp_text_input_manager_v3 *text_input_manager; + struct zwp_text_input_v3 *text_input; + BOOL text_input_active; /* enabled for current surface */ + + /* Preedit geometry (set by AppKit via WaylandInputServer setters) */ + NSPoint ime_preedit_spot; /* cursor position for IM popup, in screen coords */ + NSRect ime_preedit_rect; /* preedit bounding rect */ + + /* Pending IM events, applied together in the done callback */ + char *ime_pending_preedit; /* NULL when no active preedit */ + int32_t ime_preedit_cursor_begin; + int32_t ime_preedit_cursor_end; + char *ime_pending_commit; /* NULL when no pending commit */ + uint32_t ime_serial; /* most recent done serial */ + + /* wl_data_device — selection and drag-and-drop */ + struct wl_data_device_manager *data_device_manager; + struct wl_data_device *data_device; + int data_device_manager_version; + + /* DnD outbound: we are the drag source */ + struct wl_data_source *dnd_source; /* NULL when no outbound drag is active */ + + /* DnD inbound: pending/current offer from an external app */ + struct wl_data_offer *dnd_offer; + char **dnd_offer_mimes; /* strdup'd, NULL when empty */ + int dnd_offer_mime_count; + int dnd_offer_mime_cap; + uint32_t dnd_offer_source_actions; + uint32_t dnd_current_action; + float dnd_x; /* surface-local cursor pos */ + float dnd_y; + struct window *dnd_target; /* GNUstep window under cursor */ + BOOL dnd_incoming; /* YES between enter and leave/drop */ + } WaylandConfig; struct output @@ -125,14 +220,19 @@ struct output struct wl_output *output; uint32_t server_output_id; struct wl_list link; - int alloc_x; - int alloc_y; - int width; - int height; + int alloc_x; /* compositor layout origin X (physical pixels) */ + int alloc_y; /* compositor layout origin Y (physical pixels) */ + int width; /* current mode width (physical pixels) */ + int height; /* current mode height (physical pixels) */ + int effective_width; /* logical width = width / scale, swap for rot */ + int effective_height; /* logical height = height / scale, swap for rot */ int transform; int scale; char *make; char *model; + char *name; /* human-readable connector name (wl_output v4) */ + char *description; /* human-readable description (wl_output v4) */ + BOOL configured; /* YES after the first handle_done has fired */ void *user_data; }; @@ -149,6 +249,12 @@ struct window BOOL moving; BOOL resizing; BOOL ignoreMouse; + BOOL usesOpenGL; + BOOL global_pos_known; // saved_pos_x/y hold a reliable output-relative origin + + /* Binding embedded in the struct for this window's own wl_surface. + * Set once at surface-creation time; offset_x/y are always 0,0. */ + struct wl_surface_binding surface_binding; float pos_x; float pos_y; @@ -159,13 +265,16 @@ struct window int is_out; int level; - struct wl_surface *surface; - struct xdg_surface *xdg_surface; - struct xdg_toplevel *toplevel; - struct xdg_popup *popup; - struct xdg_positioner *positioner; + int parent_id; + + struct wl_surface *surface; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *toplevel; + struct xdg_popup *popup; + struct xdg_positioner *positioner; struct zwlr_layer_surface_v1 *layer_surface; - struct output *output; + struct zxdg_toplevel_decoration_v1 *decoration; + struct output *output; CairoSurface *wcs; }; @@ -177,18 +286,37 @@ struct window * to handle the case where NULL is used. */ struct window *get_window_with_id(WaylandConfig *wlconfig, int winid); +float WaylandToNS(struct window *window, float wl_y); @interface WaylandServer : GSDisplayServer { WaylandConfig *wlconfig; BOOL _mouseInitialized; + id inputServer; } @end -@interface -WaylandServer (Cursor) -- (void)initializeMouseIfRequired; +@interface WaylandServer (Cursor) +- (void) initializeMouseIfRequired; +@end + +@interface WaylandServer (DragAndDrop) +- (id ) dragInfo; +- (BOOL) addDragTypes: (NSArray *)types toWindow: (NSWindow *)win; +- (BOOL) removeDragTypes: (NSArray *)types fromWindow: (NSWindow *)win; +@end + +@interface WaylandServer (InputMethod) +- (NSString *) inputMethodStyle; +- (NSString *) fontSize: (int *)size; +- (BOOL) clientWindowRect: (NSRect *)rect; +- (BOOL) statusArea: (NSRect *)rect; +- (BOOL) preeditArea: (NSRect *)rect; +- (BOOL) preeditSpot: (NSPoint *)p; +- (BOOL) setStatusArea: (NSRect *)rect; +- (BOOL) setPreeditArea: (NSRect *)rect; +- (BOOL) setPreeditSpot: (NSPoint *)p; @end #endif /* _WaylandServer_h_INCLUDE */ diff --git a/Headers/wayland/text-input-unstable-v3-client-protocol.h b/Headers/wayland/text-input-unstable-v3-client-protocol.h new file mode 100644 index 00000000..6a853f25 --- /dev/null +++ b/Headers/wayland/text-input-unstable-v3-client-protocol.h @@ -0,0 +1,1103 @@ +/* Generated by wayland-scanner 1.24.0 */ + +#ifndef TEXT_INPUT_UNSTABLE_V3_CLIENT_PROTOCOL_H +#define TEXT_INPUT_UNSTABLE_V3_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_text_input_unstable_v3 The text_input_unstable_v3 protocol + * Protocol for composing text + * + * @section page_desc_text_input_unstable_v3 Description + * + * This protocol allows compositors to act as input methods and to send text + * to applications. A text input object is used to manage state of what are + * typically text entry fields in the application. + * + * This document adheres to the RFC 2119 when using words like "must", + * "should", "may", etc. + * + * Warning! The protocol described in this file is experimental and + * backward incompatible changes may be made. Backward compatible changes + * may be added together with the corresponding interface version bump. + * Backward incompatible changes are done by bumping the version number in + * the protocol and interface names and resetting the interface version. + * Once the protocol is to be declared stable, the 'z' prefix and the + * version number in the protocol and interface names are removed and the + * interface version number is reset. + * + * @section page_ifaces_text_input_unstable_v3 Interfaces + * - @subpage page_iface_zwp_text_input_v3 - text input + * - @subpage page_iface_zwp_text_input_manager_v3 - text input manager + * @section page_copyright_text_input_unstable_v3 Copyright + *
+ *
+ * Copyright © 2012, 2013 Intel Corporation
+ * Copyright © 2015, 2016 Jan Arne Petersen
+ * Copyright © 2017, 2018 Red Hat, Inc.
+ * Copyright © 2018       Purism SPC
+ *
+ * Permission to use, copy, modify, distribute, and sell this
+ * software and its documentation for any purpose is hereby granted
+ * without fee, provided that the above copyright notice appear in
+ * all copies and that both that copyright notice and this permission
+ * notice appear in supporting documentation, and that the name of
+ * the copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ * THIS SOFTWARE.
+ * 
+ */ +struct wl_seat; +struct wl_surface; +struct zwp_text_input_manager_v3; +struct zwp_text_input_v3; + +#ifndef ZWP_TEXT_INPUT_V3_INTERFACE +#define ZWP_TEXT_INPUT_V3_INTERFACE +/** + * @page page_iface_zwp_text_input_v3 zwp_text_input_v3 + * @section page_iface_zwp_text_input_v3_desc Description + * + * The zwp_text_input_v3 interface represents text input and input methods + * associated with a seat. It provides enter/leave events to follow the + * text input focus for a seat. + * + * Requests are used to enable/disable the text-input object and set + * state information like surrounding and selected text or the content type. + * The information about the entered text is sent to the text-input object + * via the preedit_string and commit_string events. + * + * Text is valid UTF-8 encoded, indices and lengths are in bytes. Indices + * must not point to middle bytes inside a code point: they must either + * point to the first byte of a code point or to the end of the buffer. + * Lengths must be measured between two valid indices. + * + * Focus moving throughout surfaces will result in the emission of + * zwp_text_input_v3.enter and zwp_text_input_v3.leave events. The focused + * surface must commit zwp_text_input_v3.enable and + * zwp_text_input_v3.disable requests as the keyboard focus moves across + * editable and non-editable elements of the UI. Those two requests are not + * expected to be paired with each other, the compositor must be able to + * handle consecutive series of the same request. + * + * State is sent by the state requests (set_surrounding_text, + * set_content_type and set_cursor_rectangle) and a commit request. After an + * enter event or disable request all state information is invalidated and + * needs to be resent by the client. + * @section page_iface_zwp_text_input_v3_api API + * See @ref iface_zwp_text_input_v3. + */ +/** + * @defgroup iface_zwp_text_input_v3 The zwp_text_input_v3 interface + * + * The zwp_text_input_v3 interface represents text input and input methods + * associated with a seat. It provides enter/leave events to follow the + * text input focus for a seat. + * + * Requests are used to enable/disable the text-input object and set + * state information like surrounding and selected text or the content type. + * The information about the entered text is sent to the text-input object + * via the preedit_string and commit_string events. + * + * Text is valid UTF-8 encoded, indices and lengths are in bytes. Indices + * must not point to middle bytes inside a code point: they must either + * point to the first byte of a code point or to the end of the buffer. + * Lengths must be measured between two valid indices. + * + * Focus moving throughout surfaces will result in the emission of + * zwp_text_input_v3.enter and zwp_text_input_v3.leave events. The focused + * surface must commit zwp_text_input_v3.enable and + * zwp_text_input_v3.disable requests as the keyboard focus moves across + * editable and non-editable elements of the UI. Those two requests are not + * expected to be paired with each other, the compositor must be able to + * handle consecutive series of the same request. + * + * State is sent by the state requests (set_surrounding_text, + * set_content_type and set_cursor_rectangle) and a commit request. After an + * enter event or disable request all state information is invalidated and + * needs to be resent by the client. + */ +extern const struct wl_interface zwp_text_input_v3_interface; +#endif +#ifndef ZWP_TEXT_INPUT_MANAGER_V3_INTERFACE +#define ZWP_TEXT_INPUT_MANAGER_V3_INTERFACE +/** + * @page page_iface_zwp_text_input_manager_v3 zwp_text_input_manager_v3 + * @section page_iface_zwp_text_input_manager_v3_desc Description + * + * A factory for text-input objects. This object is a global singleton. + * @section page_iface_zwp_text_input_manager_v3_api API + * See @ref iface_zwp_text_input_manager_v3. + */ +/** + * @defgroup iface_zwp_text_input_manager_v3 The zwp_text_input_manager_v3 interface + * + * A factory for text-input objects. This object is a global singleton. + */ +extern const struct wl_interface zwp_text_input_manager_v3_interface; +#endif + +#ifndef ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_ENUM +#define ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_ENUM +/** + * @ingroup iface_zwp_text_input_v3 + * text change reason + * + * Reason for the change of surrounding text or cursor posision. + */ +enum zwp_text_input_v3_change_cause { + /** + * input method caused the change + */ + ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_INPUT_METHOD = 0, + /** + * something else than the input method caused the change + */ + ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_OTHER = 1, +}; +#endif /* ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_ENUM */ + +#ifndef ZWP_TEXT_INPUT_V3_CONTENT_HINT_ENUM +#define ZWP_TEXT_INPUT_V3_CONTENT_HINT_ENUM +/** + * @ingroup iface_zwp_text_input_v3 + * content hint + * + * Content hint is a bitmask to allow to modify the behavior of the text + * input. + */ +enum zwp_text_input_v3_content_hint { + /** + * no special behavior + */ + ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE = 0x0, + /** + * suggest word completions + */ + ZWP_TEXT_INPUT_V3_CONTENT_HINT_COMPLETION = 0x1, + /** + * suggest word corrections + */ + ZWP_TEXT_INPUT_V3_CONTENT_HINT_SPELLCHECK = 0x2, + /** + * switch to uppercase letters at the start of a sentence + */ + ZWP_TEXT_INPUT_V3_CONTENT_HINT_AUTO_CAPITALIZATION = 0x4, + /** + * prefer lowercase letters + */ + ZWP_TEXT_INPUT_V3_CONTENT_HINT_LOWERCASE = 0x8, + /** + * prefer uppercase letters + */ + ZWP_TEXT_INPUT_V3_CONTENT_HINT_UPPERCASE = 0x10, + /** + * prefer casing for titles and headings (can be language dependent) + */ + ZWP_TEXT_INPUT_V3_CONTENT_HINT_TITLECASE = 0x20, + /** + * characters should be hidden + */ + ZWP_TEXT_INPUT_V3_CONTENT_HINT_HIDDEN_TEXT = 0x40, + /** + * typed text should not be stored + */ + ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA = 0x80, + /** + * just Latin characters should be entered + */ + ZWP_TEXT_INPUT_V3_CONTENT_HINT_LATIN = 0x100, + /** + * the text input is multiline + */ + ZWP_TEXT_INPUT_V3_CONTENT_HINT_MULTILINE = 0x200, + /** + * an on-screen way to fill in the input is already provided by the client + * @since 2 + */ + ZWP_TEXT_INPUT_V3_CONTENT_HINT_ON_SCREEN_INPUT_PROVIDED = 0x400, + /** + * prefer not offering emoji support + * @since 2 + */ + ZWP_TEXT_INPUT_V3_CONTENT_HINT_NO_EMOJI = 0x800, + /** + * the text input will display preedit text in place + * @since 2 + */ + ZWP_TEXT_INPUT_V3_CONTENT_HINT_PREEDIT_SHOWN = 0x1000, +}; +/** + * @ingroup iface_zwp_text_input_v3 + */ +#define ZWP_TEXT_INPUT_V3_CONTENT_HINT_ON_SCREEN_INPUT_PROVIDED_SINCE_VERSION 2 +/** + * @ingroup iface_zwp_text_input_v3 + */ +#define ZWP_TEXT_INPUT_V3_CONTENT_HINT_NO_EMOJI_SINCE_VERSION 2 +/** + * @ingroup iface_zwp_text_input_v3 + */ +#define ZWP_TEXT_INPUT_V3_CONTENT_HINT_PREEDIT_SHOWN_SINCE_VERSION 2 +#endif /* ZWP_TEXT_INPUT_V3_CONTENT_HINT_ENUM */ + +#ifndef ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_ENUM +#define ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_ENUM +/** + * @ingroup iface_zwp_text_input_v3 + * content purpose + * + * The content purpose allows to specify the primary purpose of a text + * input. + * + * This allows an input method to show special purpose input panels with + * extra characters or to disallow some characters. + */ +enum zwp_text_input_v3_content_purpose { + /** + * default input, allowing all characters + */ + ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL = 0, + /** + * allow only alphabetic characters + */ + ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_ALPHA = 1, + /** + * allow only digits + */ + ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_DIGITS = 2, + /** + * input a number (including decimal separator and sign) + */ + ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NUMBER = 3, + /** + * input a phone number + */ + ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PHONE = 4, + /** + * input an URL + */ + ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_URL = 5, + /** + * input an email address + */ + ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_EMAIL = 6, + /** + * input a name of a person + */ + ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NAME = 7, + /** + * input a password (combine with sensitive_data hint) + */ + ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PASSWORD = 8, + /** + * input is a numeric password (combine with sensitive_data hint) + */ + ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PIN = 9, + /** + * input a date + */ + ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_DATE = 10, + /** + * input a time + */ + ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TIME = 11, + /** + * input a date and time + */ + ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_DATETIME = 12, + /** + * input for a terminal + */ + ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL = 13, +}; +#endif /* ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_ENUM */ + +#ifndef ZWP_TEXT_INPUT_V3_ERROR_ENUM +#define ZWP_TEXT_INPUT_V3_ERROR_ENUM +enum zwp_text_input_v3_error { + /** + * an invalid or duplicate action was specified + */ + ZWP_TEXT_INPUT_V3_ERROR_INVALID_ACTION = 0, +}; +#endif /* ZWP_TEXT_INPUT_V3_ERROR_ENUM */ + +#ifndef ZWP_TEXT_INPUT_V3_ACTION_ENUM +#define ZWP_TEXT_INPUT_V3_ACTION_ENUM +/** + * @ingroup iface_zwp_text_input_v3 + * action + * + * A possible action to perform on a text input. + * + * The submit action is intended for input entries that expect some sort of + * activation after user interaction, e.g. the URL entry in a browser. + */ +enum zwp_text_input_v3_action { + /** + * no action + */ + ZWP_TEXT_INPUT_V3_ACTION_NONE = 0, + /** + * the action is submitted + */ + ZWP_TEXT_INPUT_V3_ACTION_SUBMIT = 1, +}; +#endif /* ZWP_TEXT_INPUT_V3_ACTION_ENUM */ + +#ifndef ZWP_TEXT_INPUT_V3_PREEDIT_HINT_ENUM +#define ZWP_TEXT_INPUT_V3_PREEDIT_HINT_ENUM +/** + * @ingroup iface_zwp_text_input_v3 + * preedit style hint + * + * Style hints for the preedit string. + */ +enum zwp_text_input_v3_preedit_hint { + /** + * simple pre-edit text style, typically underlined + */ + ZWP_TEXT_INPUT_V3_PREEDIT_HINT_WHOLE = 1, + /** + * hint for a selected piece of text, e.g. per-character navigation and composition + */ + ZWP_TEXT_INPUT_V3_PREEDIT_HINT_SELECTION = 2, + /** + * predicted text, not typed by the user + */ + ZWP_TEXT_INPUT_V3_PREEDIT_HINT_PREDICTION = 3, + /** + * prefixed text not being currently edited, e.g. prior to a 'selection' section + */ + ZWP_TEXT_INPUT_V3_PREEDIT_HINT_PREFIX = 4, + /** + * suffixed text not being currently edited, e.g. after a 'selection' section + */ + ZWP_TEXT_INPUT_V3_PREEDIT_HINT_SUFFIX = 5, + /** + * spelling error + */ + ZWP_TEXT_INPUT_V3_PREEDIT_HINT_SPELLING_ERROR = 6, + /** + * wrong composition, e.g. user input that can not be transliterated + */ + ZWP_TEXT_INPUT_V3_PREEDIT_HINT_COMPOSE_ERROR = 7, +}; +#endif /* ZWP_TEXT_INPUT_V3_PREEDIT_HINT_ENUM */ + +/** + * @ingroup iface_zwp_text_input_v3 + * @struct zwp_text_input_v3_listener + */ +struct zwp_text_input_v3_listener { + /** + * enter event + * + * Notification that this seat's text-input focus is on a certain + * surface. + * + * If client has created multiple text input objects, compositor + * must send this event to all of them. + * + * When the seat has the keyboard capability the text-input focus + * follows the keyboard focus. This event sets the current surface + * for the text-input object. + */ + void (*enter)(void *data, + struct zwp_text_input_v3 *zwp_text_input_v3, + struct wl_surface *surface); + /** + * leave event + * + * Notification that this seat's text-input focus is no longer on + * a certain surface. The client should reset any preedit string + * previously set. + * + * The leave notification clears the current surface. It is sent + * before the enter notification for the new focus. After leave + * event, compositor must ignore requests from any text input + * instances until next enter event. + * + * When the seat has the keyboard capability the text-input focus + * follows the keyboard focus. + */ + void (*leave)(void *data, + struct zwp_text_input_v3 *zwp_text_input_v3, + struct wl_surface *surface); + /** + * pre-edit + * + * Notify when a new composing text (pre-edit) should be set at + * the current cursor position. Any previously set composing text + * must be removed. Any previously existing selected text must be + * removed. + * + * The argument text contains the pre-edit string buffer. + * + * The parameters cursor_begin and cursor_end are counted in bytes + * relative to the beginning of the submitted text buffer. Cursor + * should be hidden when both are equal to -1. + * + * They could be represented by the client as a line if both values + * are the same, or as a text highlight otherwise. + * + * Values set with this event are double-buffered. They must be + * applied and reset to initial on the next zwp_text_input_v3.done + * event. + * + * The initial value of text is an empty string, and cursor_begin, + * cursor_end and cursor_hidden are all 0. + */ + void (*preedit_string)(void *data, + struct zwp_text_input_v3 *zwp_text_input_v3, + const char *text, + int32_t cursor_begin, + int32_t cursor_end); + /** + * text commit + * + * Notify when text should be inserted into the editor widget. + * The text to commit could be either just a single character after + * a key press or the result of some composing (pre-edit). + * + * Values set with this event are double-buffered. They must be + * applied and reset to initial on the next zwp_text_input_v3.done + * event. + * + * The initial value of text is an empty string. + */ + void (*commit_string)(void *data, + struct zwp_text_input_v3 *zwp_text_input_v3, + const char *text); + /** + * delete surrounding text + * + * Notify when the text around the current cursor position should + * be deleted. + * + * Before_length and after_length are the number of bytes before + * and after the current cursor index (excluding the selection) to + * delete. + * + * If a preedit text is present, in effect before_length is counted + * from the beginning of it, and after_length from its end (see + * done event sequence). + * + * Values set with this event are double-buffered. They must be + * applied and reset to initial on the next zwp_text_input_v3.done + * event. + * + * The initial values of both before_length and after_length are 0. + * @param before_length length of text before current cursor position + * @param after_length length of text after current cursor position + */ + void (*delete_surrounding_text)(void *data, + struct zwp_text_input_v3 *zwp_text_input_v3, + uint32_t before_length, + uint32_t after_length); + /** + * apply changes + * + * Instruct the application to apply changes to state requested + * by the preedit_string, commit_string delete_surrounding_text, + * and action events. + * + * The state relating to these events is double-buffered, and each + * one modifies the pending state. This event replaces the current + * state with the pending state. + * + * The application must proceed by evaluating the changes in the + * following order: + * + * 1. Replace existing preedit string with the cursor. 2. Delete + * requested surrounding text. 3. Insert commit string with the + * cursor at its end. 4. Calculate surrounding text to send. 5. + * Insert new preedit text in cursor position. 6. Place cursor + * inside preedit text. 7. Perform the requested action. + * + * The serial number reflects the last state of the + * zwp_text_input_v3 object known to the compositor. The value of + * the serial argument must be equal to the number of commit + * requests already issued on that object. + * + * When the client receives a done event with a serial different + * than the number of past commit requests, it must proceed with + * evaluating and applying the changes as normal, except it should + * not change the current state of the zwp_text_input_v3 object. + * All pending state requests (set_surrounding_text, + * set_content_type and set_cursor_rectangle) on the + * zwp_text_input_v3 object should be sent and committed after + * receiving a zwp_text_input_v3.done event with a matching serial. + */ + void (*done)(void *data, + struct zwp_text_input_v3 *zwp_text_input_v3, + uint32_t serial); + /** + * action performed + * + * An action was performed on this text input. + * + * Values set with this event are double-buffered. They must be + * applied and reset to initial on the next zwp_text_input_v3.done + * event. + * + * The initial value of action is none. + * @param action action performed + * @param serial serial number of the action event + * @since 2 + */ + void (*action)(void *data, + struct zwp_text_input_v3 *zwp_text_input_v3, + uint32_t action, + uint32_t serial); + /** + * notify of language selection + * + * Notify the application of language used by the input method. + * + * This event will be sent on creation if known and for all + * subsequent changes. + * + * The language should be specified as an IETF BCP 47 tag. Setting + * an empty string will reset any known language back to the + * default unknown state. + * @param language new language set by IME + * @since 2 + */ + void (*language)(void *data, + struct zwp_text_input_v3 *zwp_text_input_v3, + const char *language); + /** + * pre-edit + * + * Notify of contextual hints for the pre-edit string. This event + * is always sent together with a zwp_text_input_v3.preedit_string + * event. + * + * The parameters start and end are counted in bytes relative to + * the beginning of the text buffer submitted through + * zwp_text_input_v3.preedit_string, and represent the substring in + * the pre-edit text affected by the hint. + * + * Multiple events may be submitted if the preedit string has + * different sections. The extent of hints may overlap. The parts + * of the preedit string that are not covered by any + * zwp_text_input_v3.preedit_hint event, the text will be + * considered unhinted. This is also the case if no preedit_hint + * event is sent. + * + * Clients should provide recognizable visuals to these hints. if + * they are unable to comply with this requisition, it may be + * preferable for them keep the preedit_shown content hint + * disabled. + * + * Values set with this event are double-buffered. They must be + * applied and reset on the next zwp_text_input_v3.done event. + * @param start starting point of the affected substring + * @param end end point of the affected substring + * @param hint hint to apply + * @since 2 + */ + void (*preedit_hint)(void *data, + struct zwp_text_input_v3 *zwp_text_input_v3, + uint32_t start, + uint32_t end, + uint32_t hint); +}; + +/** + * @ingroup iface_zwp_text_input_v3 + */ +static inline int +zwp_text_input_v3_add_listener(struct zwp_text_input_v3 *zwp_text_input_v3, + const struct zwp_text_input_v3_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwp_text_input_v3, + (void (**)(void)) listener, data); +} + +#define ZWP_TEXT_INPUT_V3_DESTROY 0 +#define ZWP_TEXT_INPUT_V3_ENABLE 1 +#define ZWP_TEXT_INPUT_V3_DISABLE 2 +#define ZWP_TEXT_INPUT_V3_SET_SURROUNDING_TEXT 3 +#define ZWP_TEXT_INPUT_V3_SET_TEXT_CHANGE_CAUSE 4 +#define ZWP_TEXT_INPUT_V3_SET_CONTENT_TYPE 5 +#define ZWP_TEXT_INPUT_V3_SET_CURSOR_RECTANGLE 6 +#define ZWP_TEXT_INPUT_V3_COMMIT 7 +#define ZWP_TEXT_INPUT_V3_SET_AVAILABLE_ACTIONS 8 +#define ZWP_TEXT_INPUT_V3_SHOW_INPUT_PANEL 9 +#define ZWP_TEXT_INPUT_V3_HIDE_INPUT_PANEL 10 + +/** + * @ingroup iface_zwp_text_input_v3 + */ +#define ZWP_TEXT_INPUT_V3_ENTER_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_text_input_v3 + */ +#define ZWP_TEXT_INPUT_V3_LEAVE_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_text_input_v3 + */ +#define ZWP_TEXT_INPUT_V3_PREEDIT_STRING_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_text_input_v3 + */ +#define ZWP_TEXT_INPUT_V3_COMMIT_STRING_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_text_input_v3 + */ +#define ZWP_TEXT_INPUT_V3_DELETE_SURROUNDING_TEXT_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_text_input_v3 + */ +#define ZWP_TEXT_INPUT_V3_DONE_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_text_input_v3 + */ +#define ZWP_TEXT_INPUT_V3_ACTION_SINCE_VERSION 2 +/** + * @ingroup iface_zwp_text_input_v3 + */ +#define ZWP_TEXT_INPUT_V3_LANGUAGE_SINCE_VERSION 2 +/** + * @ingroup iface_zwp_text_input_v3 + */ +#define ZWP_TEXT_INPUT_V3_PREEDIT_HINT_SINCE_VERSION 2 + +/** + * @ingroup iface_zwp_text_input_v3 + */ +#define ZWP_TEXT_INPUT_V3_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_text_input_v3 + */ +#define ZWP_TEXT_INPUT_V3_ENABLE_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_text_input_v3 + */ +#define ZWP_TEXT_INPUT_V3_DISABLE_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_text_input_v3 + */ +#define ZWP_TEXT_INPUT_V3_SET_SURROUNDING_TEXT_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_text_input_v3 + */ +#define ZWP_TEXT_INPUT_V3_SET_TEXT_CHANGE_CAUSE_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_text_input_v3 + */ +#define ZWP_TEXT_INPUT_V3_SET_CONTENT_TYPE_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_text_input_v3 + */ +#define ZWP_TEXT_INPUT_V3_SET_CURSOR_RECTANGLE_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_text_input_v3 + */ +#define ZWP_TEXT_INPUT_V3_COMMIT_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_text_input_v3 + */ +#define ZWP_TEXT_INPUT_V3_SET_AVAILABLE_ACTIONS_SINCE_VERSION 2 +/** + * @ingroup iface_zwp_text_input_v3 + */ +#define ZWP_TEXT_INPUT_V3_SHOW_INPUT_PANEL_SINCE_VERSION 2 +/** + * @ingroup iface_zwp_text_input_v3 + */ +#define ZWP_TEXT_INPUT_V3_HIDE_INPUT_PANEL_SINCE_VERSION 2 + +/** @ingroup iface_zwp_text_input_v3 */ +static inline void +zwp_text_input_v3_set_user_data(struct zwp_text_input_v3 *zwp_text_input_v3, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwp_text_input_v3, user_data); +} + +/** @ingroup iface_zwp_text_input_v3 */ +static inline void * +zwp_text_input_v3_get_user_data(struct zwp_text_input_v3 *zwp_text_input_v3) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwp_text_input_v3); +} + +static inline uint32_t +zwp_text_input_v3_get_version(struct zwp_text_input_v3 *zwp_text_input_v3) +{ + return wl_proxy_get_version((struct wl_proxy *) zwp_text_input_v3); +} + +/** + * @ingroup iface_zwp_text_input_v3 + * + * Destroy the wp_text_input object. Also disables all surfaces enabled + * through this wp_text_input object. + */ +static inline void +zwp_text_input_v3_destroy(struct zwp_text_input_v3 *zwp_text_input_v3) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_text_input_v3, + ZWP_TEXT_INPUT_V3_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_text_input_v3), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zwp_text_input_v3 + * + * Requests text input on the surface previously obtained from the enter + * event. + * + * This request must be issued every time the focused text input changes + * to a new one, including within the current surface. Use + * zwp_text_input_v3.disable when there is no longer any input focus on + * the current surface. + * + * Clients must not enable more than one text input on the single seat + * and should disable the current text input before enabling the new one. + * Requests to enable a text input when another text input is enabled + * on the same seat must be ignored by compositor. + * + * This request resets all state associated with previous enable, disable, + * set_surrounding_text, set_text_change_cause, set_content_type, and + * set_cursor_rectangle requests, as well as the state associated with + * preedit_string, commit_string, and delete_surrounding_text events. + * + * The set_surrounding_text, set_content_type and set_cursor_rectangle + * requests must follow if the text input supports the necessary + * functionality. + * + * State set with this request is double-buffered. It will get applied on + * the next zwp_text_input_v3.commit request, and stay valid until the + * next committed enable or disable request. + * + * The changes must be applied by the compositor after issuing a + * zwp_text_input_v3.commit request. + */ +static inline void +zwp_text_input_v3_enable(struct zwp_text_input_v3 *zwp_text_input_v3) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_text_input_v3, + ZWP_TEXT_INPUT_V3_ENABLE, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_text_input_v3), 0); +} + +/** + * @ingroup iface_zwp_text_input_v3 + * + * Explicitly disable text input on the current surface (typically when + * there is no focus on any text entry inside the surface). + * + * State set with this request is double-buffered. It will get applied on + * the next zwp_text_input_v3.commit request. + */ +static inline void +zwp_text_input_v3_disable(struct zwp_text_input_v3 *zwp_text_input_v3) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_text_input_v3, + ZWP_TEXT_INPUT_V3_DISABLE, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_text_input_v3), 0); +} + +/** + * @ingroup iface_zwp_text_input_v3 + * + * Sets the surrounding plain text around the input, excluding the preedit + * text. + * + * The client should notify the compositor of any changes in any of the + * values carried with this request, including changes caused by handling + * incoming text-input events as well as changes caused by other + * mechanisms like keyboard typing. + * + * If the client is unaware of the text around the cursor, it should not + * issue this request, to signify lack of support to the compositor. + * + * Text is UTF-8 encoded, and should include the cursor position, the + * complete selection and additional characters before and after them. + * There is a maximum length of wayland messages, so text can not be + * longer than 4000 bytes. + * + * Cursor is the byte offset of the cursor within text buffer. + * + * Anchor is the byte offset of the selection anchor within text buffer. + * If there is no selected text, anchor is the same as cursor. + * + * If any preedit text is present, it is replaced with a cursor for the + * purpose of this event. + * + * Values set with this request are double-buffered. They will get applied + * on the next zwp_text_input_v3.commit request, and stay valid until the + * next committed enable or disable request. + * + * The initial state for affected fields is empty, meaning that the text + * input does not support sending surrounding text. If the empty values + * get applied, subsequent attempts to change them may have no effect. + */ +static inline void +zwp_text_input_v3_set_surrounding_text(struct zwp_text_input_v3 *zwp_text_input_v3, const char *text, int32_t cursor, int32_t anchor) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_text_input_v3, + ZWP_TEXT_INPUT_V3_SET_SURROUNDING_TEXT, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_text_input_v3), 0, text, cursor, anchor); +} + +/** + * @ingroup iface_zwp_text_input_v3 + * + * Tells the compositor why the text surrounding the cursor changed. + * + * Whenever the client detects an external change in text, cursor, or + * anchor posision, it must issue this request to the compositor. This + * request is intended to give the input method a chance to update the + * preedit text in an appropriate way, e.g. by removing it when the user + * starts typing with a keyboard. + * + * cause describes the source of the change. + * + * The value set with this request is double-buffered. It must be applied + * and reset to initial at the next zwp_text_input_v3.commit request. + * + * The initial value of cause is input_method. + */ +static inline void +zwp_text_input_v3_set_text_change_cause(struct zwp_text_input_v3 *zwp_text_input_v3, uint32_t cause) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_text_input_v3, + ZWP_TEXT_INPUT_V3_SET_TEXT_CHANGE_CAUSE, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_text_input_v3), 0, cause); +} + +/** + * @ingroup iface_zwp_text_input_v3 + * + * Sets the content purpose and content hint. While the purpose is the + * basic purpose of an input field, the hint flags allow to modify some of + * the behavior. + * + * Values set with this request are double-buffered. They will get applied + * on the next zwp_text_input_v3.commit request. + * Subsequent attempts to update them may have no effect. The values + * remain valid until the next committed enable or disable request. + * + * The initial value for hint is none, and the initial value for purpose + * is normal. + */ +static inline void +zwp_text_input_v3_set_content_type(struct zwp_text_input_v3 *zwp_text_input_v3, uint32_t hint, uint32_t purpose) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_text_input_v3, + ZWP_TEXT_INPUT_V3_SET_CONTENT_TYPE, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_text_input_v3), 0, hint, purpose); +} + +/** + * @ingroup iface_zwp_text_input_v3 + * + * Marks an area around the cursor as a x, y, width, height rectangle in + * surface local coordinates. + * + * Allows the compositor to put a window with word suggestions near the + * cursor, without obstructing the text being input. + * + * If the client is unaware of the position of edited text, it should not + * issue this request, to signify lack of support to the compositor. + * + * Values set with this request are double-buffered. They will get applied + * on the next zwp_text_input_v3.commit request, and stay valid until the + * next committed enable or disable request. + * + * The initial values describing a cursor rectangle are empty. That means + * the text input does not support describing the cursor area. If the + * empty values get applied, subsequent attempts to change them may have + * no effect. + * + * As of version 2, the zwp_text_input_v3.commit request does not apply + * values sent with this request. Instead, it stores them in a separate + * "committed" area. The committed values, if still valid, get applied on + * the next wl_surface.commit request on the surface with text-input focus. + * Both committed and applied values get invalidated on: + * + * - the next committed enable or disable request, or + * - a change of the focused surface of the text-input (leave or enter events). + * + * This double stage application allows the compositor to position + * the input method popup in the same frame as the contents + * of the text on the surface are updated. + */ +static inline void +zwp_text_input_v3_set_cursor_rectangle(struct zwp_text_input_v3 *zwp_text_input_v3, int32_t x, int32_t y, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_text_input_v3, + ZWP_TEXT_INPUT_V3_SET_CURSOR_RECTANGLE, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_text_input_v3), 0, x, y, width, height); +} + +/** + * @ingroup iface_zwp_text_input_v3 + * + * Atomically applies state changes recently sent to the compositor. + * + * The commit request establishes and updates the state of the client, and + * must be issued after any changes to apply them. + * + * Text input state (enabled status, content purpose, content hint, + * surrounding text and change cause, cursor rectangle) is conceptually + * double-buffered within the context of a text input, i.e. between a + * committed enable request and the following committed enable or disable + * request. + * + * Protocol requests modify the pending state, as opposed to the current + * state in use by the input method. A commit request atomically applies + * all pending state, replacing the current state. After commit, the new + * pending state is as documented for each related request. + * + * Requests are applied in the order of arrival. + * + * Neither current nor pending state are modified unless noted otherwise. + * + * The compositor must count the number of commit requests coming from + * each zwp_text_input_v3 object and use the count as the serial in done + * events. + */ +static inline void +zwp_text_input_v3_commit(struct zwp_text_input_v3 *zwp_text_input_v3) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_text_input_v3, + ZWP_TEXT_INPUT_V3_COMMIT, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_text_input_v3), 0); +} + +/** + * @ingroup iface_zwp_text_input_v3 + * + * Set the actions available for this text input. + * + * Values set with this request are double-buffered. They will get applied + * on the next zwp_text_input_v3.commit request. + * + * If the available_actions array contains the none action, or contains the + * same action multiple times, the compositor must raise the invalid_action + * protocol error. + * + * Initially, no actions are available. + */ +static inline void +zwp_text_input_v3_set_available_actions(struct zwp_text_input_v3 *zwp_text_input_v3, struct wl_array *available_actions) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_text_input_v3, + ZWP_TEXT_INPUT_V3_SET_AVAILABLE_ACTIONS, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_text_input_v3), 0, available_actions); +} + +/** + * @ingroup iface_zwp_text_input_v3 + * + * Requests an input panel to be shown (e.g. a on-screen keyboard). + * + * This request only hints the desired interaction pattern from the + * client side, and its effect may be ignored by compositors given + * other environmental factors. Repeated calls will be ignored. + */ +static inline void +zwp_text_input_v3_show_input_panel(struct zwp_text_input_v3 *zwp_text_input_v3) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_text_input_v3, + ZWP_TEXT_INPUT_V3_SHOW_INPUT_PANEL, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_text_input_v3), 0); +} + +/** + * @ingroup iface_zwp_text_input_v3 + * + * Requests an input panel to be hidden. + * + * This request only hints the desired interaction pattern from the + * client side, and its effect may be ignored by compositors given + * other environmental factors. Repeated calls will be ignored. + */ +static inline void +zwp_text_input_v3_hide_input_panel(struct zwp_text_input_v3 *zwp_text_input_v3) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_text_input_v3, + ZWP_TEXT_INPUT_V3_HIDE_INPUT_PANEL, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_text_input_v3), 0); +} + +#define ZWP_TEXT_INPUT_MANAGER_V3_DESTROY 0 +#define ZWP_TEXT_INPUT_MANAGER_V3_GET_TEXT_INPUT 1 + + +/** + * @ingroup iface_zwp_text_input_manager_v3 + */ +#define ZWP_TEXT_INPUT_MANAGER_V3_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_text_input_manager_v3 + */ +#define ZWP_TEXT_INPUT_MANAGER_V3_GET_TEXT_INPUT_SINCE_VERSION 1 + +/** @ingroup iface_zwp_text_input_manager_v3 */ +static inline void +zwp_text_input_manager_v3_set_user_data(struct zwp_text_input_manager_v3 *zwp_text_input_manager_v3, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwp_text_input_manager_v3, user_data); +} + +/** @ingroup iface_zwp_text_input_manager_v3 */ +static inline void * +zwp_text_input_manager_v3_get_user_data(struct zwp_text_input_manager_v3 *zwp_text_input_manager_v3) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwp_text_input_manager_v3); +} + +static inline uint32_t +zwp_text_input_manager_v3_get_version(struct zwp_text_input_manager_v3 *zwp_text_input_manager_v3) +{ + return wl_proxy_get_version((struct wl_proxy *) zwp_text_input_manager_v3); +} + +/** + * @ingroup iface_zwp_text_input_manager_v3 + * + * Destroy the wp_text_input_manager object. + */ +static inline void +zwp_text_input_manager_v3_destroy(struct zwp_text_input_manager_v3 *zwp_text_input_manager_v3) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_text_input_manager_v3, + ZWP_TEXT_INPUT_MANAGER_V3_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_text_input_manager_v3), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zwp_text_input_manager_v3 + * + * Creates a new text-input object for a given seat. + */ +static inline struct zwp_text_input_v3 * +zwp_text_input_manager_v3_get_text_input(struct zwp_text_input_manager_v3 *zwp_text_input_manager_v3, struct wl_seat *seat) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) zwp_text_input_manager_v3, + ZWP_TEXT_INPUT_MANAGER_V3_GET_TEXT_INPUT, &zwp_text_input_v3_interface, wl_proxy_get_version((struct wl_proxy *) zwp_text_input_manager_v3), 0, NULL, seat); + + return (struct zwp_text_input_v3 *) id; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Headers/wayland/xdg-decoration-unstable-v1-client-protocol.h b/Headers/wayland/xdg-decoration-unstable-v1-client-protocol.h new file mode 100644 index 00000000..d424d17e --- /dev/null +++ b/Headers/wayland/xdg-decoration-unstable-v1-client-protocol.h @@ -0,0 +1,400 @@ +/* Generated by wayland-scanner 1.24.0 */ + +#ifndef XDG_DECORATION_UNSTABLE_V1_CLIENT_PROTOCOL_H +#define XDG_DECORATION_UNSTABLE_V1_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_xdg_decoration_unstable_v1 The xdg_decoration_unstable_v1 protocol + * @section page_ifaces_xdg_decoration_unstable_v1 Interfaces + * - @subpage page_iface_zxdg_decoration_manager_v1 - window decoration manager + * - @subpage page_iface_zxdg_toplevel_decoration_v1 - decoration object for a toplevel surface + * @section page_copyright_xdg_decoration_unstable_v1 Copyright + *
+ *
+ * Copyright © 2018 Simon Ser
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ */ +struct xdg_toplevel; +struct zxdg_decoration_manager_v1; +struct zxdg_toplevel_decoration_v1; + +#ifndef ZXDG_DECORATION_MANAGER_V1_INTERFACE +#define ZXDG_DECORATION_MANAGER_V1_INTERFACE +/** + * @page page_iface_zxdg_decoration_manager_v1 zxdg_decoration_manager_v1 + * @section page_iface_zxdg_decoration_manager_v1_desc Description + * + * This interface allows a compositor to announce support for server-side + * decorations. + * + * A window decoration is a set of window controls as deemed appropriate by + * the party managing them, such as user interface components used to move, + * resize and change a window's state. + * + * A client can use this protocol to request being decorated by a supporting + * compositor. + * + * If compositor and client do not negotiate the use of a server-side + * decoration using this protocol, clients continue to self-decorate as they + * see fit. + * + * Warning! The protocol described in this file is experimental and + * backward incompatible changes may be made. Backward compatible changes + * may be added together with the corresponding interface version bump. + * Backward incompatible changes are done by bumping the version number in + * the protocol and interface names and resetting the interface version. + * Once the protocol is to be declared stable, the 'z' prefix and the + * version number in the protocol and interface names are removed and the + * interface version number is reset. + * @section page_iface_zxdg_decoration_manager_v1_api API + * See @ref iface_zxdg_decoration_manager_v1. + */ +/** + * @defgroup iface_zxdg_decoration_manager_v1 The zxdg_decoration_manager_v1 interface + * + * This interface allows a compositor to announce support for server-side + * decorations. + * + * A window decoration is a set of window controls as deemed appropriate by + * the party managing them, such as user interface components used to move, + * resize and change a window's state. + * + * A client can use this protocol to request being decorated by a supporting + * compositor. + * + * If compositor and client do not negotiate the use of a server-side + * decoration using this protocol, clients continue to self-decorate as they + * see fit. + * + * Warning! The protocol described in this file is experimental and + * backward incompatible changes may be made. Backward compatible changes + * may be added together with the corresponding interface version bump. + * Backward incompatible changes are done by bumping the version number in + * the protocol and interface names and resetting the interface version. + * Once the protocol is to be declared stable, the 'z' prefix and the + * version number in the protocol and interface names are removed and the + * interface version number is reset. + */ +extern const struct wl_interface zxdg_decoration_manager_v1_interface; +#endif +#ifndef ZXDG_TOPLEVEL_DECORATION_V1_INTERFACE +#define ZXDG_TOPLEVEL_DECORATION_V1_INTERFACE +/** + * @page page_iface_zxdg_toplevel_decoration_v1 zxdg_toplevel_decoration_v1 + * @section page_iface_zxdg_toplevel_decoration_v1_desc Description + * + * The decoration object allows the compositor to toggle server-side window + * decorations for a toplevel surface. The client can request to switch to + * another mode. + * + * The xdg_toplevel_decoration object must be destroyed before its + * xdg_toplevel. + * @section page_iface_zxdg_toplevel_decoration_v1_api API + * See @ref iface_zxdg_toplevel_decoration_v1. + */ +/** + * @defgroup iface_zxdg_toplevel_decoration_v1 The zxdg_toplevel_decoration_v1 interface + * + * The decoration object allows the compositor to toggle server-side window + * decorations for a toplevel surface. The client can request to switch to + * another mode. + * + * The xdg_toplevel_decoration object must be destroyed before its + * xdg_toplevel. + */ +extern const struct wl_interface zxdg_toplevel_decoration_v1_interface; +#endif + +#define ZXDG_DECORATION_MANAGER_V1_DESTROY 0 +#define ZXDG_DECORATION_MANAGER_V1_GET_TOPLEVEL_DECORATION 1 + + +/** + * @ingroup iface_zxdg_decoration_manager_v1 + */ +#define ZXDG_DECORATION_MANAGER_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_decoration_manager_v1 + */ +#define ZXDG_DECORATION_MANAGER_V1_GET_TOPLEVEL_DECORATION_SINCE_VERSION 1 + +/** @ingroup iface_zxdg_decoration_manager_v1 */ +static inline void +zxdg_decoration_manager_v1_set_user_data(struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zxdg_decoration_manager_v1, user_data); +} + +/** @ingroup iface_zxdg_decoration_manager_v1 */ +static inline void * +zxdg_decoration_manager_v1_get_user_data(struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zxdg_decoration_manager_v1); +} + +static inline uint32_t +zxdg_decoration_manager_v1_get_version(struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zxdg_decoration_manager_v1); +} + +/** + * @ingroup iface_zxdg_decoration_manager_v1 + * + * Destroy the decoration manager. This doesn't destroy objects created + * with the manager. + */ +static inline void +zxdg_decoration_manager_v1_destroy(struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_decoration_manager_v1, + ZXDG_DECORATION_MANAGER_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_decoration_manager_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zxdg_decoration_manager_v1 + * + * Create a new decoration object associated with the given toplevel. + * + * For objects of version 1, creating an xdg_toplevel_decoration from an + * xdg_toplevel which has a buffer attached or committed is a client + * error, and any attempts by a client to attach or manipulate a buffer + * prior to the first xdg_toplevel_decoration.configure event must also be + * treated as errors. + * + * For objects of version 2 or newer, creating an xdg_toplevel_decoration + * from an xdg_toplevel which has a buffer attached or committed is + * allowed. The initial decoration mode of the surface if a buffer is + * already attached depends on whether a xdg_toplevel_decoration object + * has been associated with the surface or not prior to this request. + * + * If an xdg_toplevel_decoration was associated with the surface, then + * destroyed without a surface commit, the previous decoration mode is + * retained. + * + * If no xdg_toplevel_decoration was associated with the surface prior to + * this request, or if a surface commit has been performed after a previous + * xdg_toplevel_decoration object associated with the surface was + * destroyed, the decoration mode is assumed to be client-side. + */ +static inline struct zxdg_toplevel_decoration_v1 * +zxdg_decoration_manager_v1_get_toplevel_decoration(struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1, struct xdg_toplevel *toplevel) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) zxdg_decoration_manager_v1, + ZXDG_DECORATION_MANAGER_V1_GET_TOPLEVEL_DECORATION, &zxdg_toplevel_decoration_v1_interface, wl_proxy_get_version((struct wl_proxy *) zxdg_decoration_manager_v1), 0, NULL, toplevel); + + return (struct zxdg_toplevel_decoration_v1 *) id; +} + +#ifndef ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ENUM +#define ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ENUM +enum zxdg_toplevel_decoration_v1_error { + /** + * xdg_toplevel has a buffer attached before configure + */ + ZXDG_TOPLEVEL_DECORATION_V1_ERROR_UNCONFIGURED_BUFFER = 0, + /** + * xdg_toplevel already has a decoration object + */ + ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ALREADY_CONSTRUCTED = 1, + /** + * xdg_toplevel destroyed before the decoration object + */ + ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ORPHANED = 2, + /** + * invalid mode + */ + ZXDG_TOPLEVEL_DECORATION_V1_ERROR_INVALID_MODE = 3, +}; +#endif /* ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ENUM */ + +#ifndef ZXDG_TOPLEVEL_DECORATION_V1_MODE_ENUM +#define ZXDG_TOPLEVEL_DECORATION_V1_MODE_ENUM +/** + * @ingroup iface_zxdg_toplevel_decoration_v1 + * window decoration modes + * + * These values describe window decoration modes. + */ +enum zxdg_toplevel_decoration_v1_mode { + /** + * no server-side window decoration + */ + ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE = 1, + /** + * server-side window decoration + */ + ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE = 2, +}; +#endif /* ZXDG_TOPLEVEL_DECORATION_V1_MODE_ENUM */ + +/** + * @ingroup iface_zxdg_toplevel_decoration_v1 + * @struct zxdg_toplevel_decoration_v1_listener + */ +struct zxdg_toplevel_decoration_v1_listener { + /** + * notify a decoration mode change + * + * The configure event configures the effective decoration mode. + * The configured state should not be applied immediately. Clients + * must send an ack_configure in response to this event. See + * xdg_surface.configure and xdg_surface.ack_configure for details. + * + * A configure event can be sent at any time. The specified mode + * must be obeyed by the client. + * @param mode the decoration mode + */ + void (*configure)(void *data, + struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, + uint32_t mode); +}; + +/** + * @ingroup iface_zxdg_toplevel_decoration_v1 + */ +static inline int +zxdg_toplevel_decoration_v1_add_listener(struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, + const struct zxdg_toplevel_decoration_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zxdg_toplevel_decoration_v1, + (void (**)(void)) listener, data); +} + +#define ZXDG_TOPLEVEL_DECORATION_V1_DESTROY 0 +#define ZXDG_TOPLEVEL_DECORATION_V1_SET_MODE 1 +#define ZXDG_TOPLEVEL_DECORATION_V1_UNSET_MODE 2 + +/** + * @ingroup iface_zxdg_toplevel_decoration_v1 + */ +#define ZXDG_TOPLEVEL_DECORATION_V1_CONFIGURE_SINCE_VERSION 1 + +/** + * @ingroup iface_zxdg_toplevel_decoration_v1 + */ +#define ZXDG_TOPLEVEL_DECORATION_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_toplevel_decoration_v1 + */ +#define ZXDG_TOPLEVEL_DECORATION_V1_SET_MODE_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_toplevel_decoration_v1 + */ +#define ZXDG_TOPLEVEL_DECORATION_V1_UNSET_MODE_SINCE_VERSION 1 + +/** @ingroup iface_zxdg_toplevel_decoration_v1 */ +static inline void +zxdg_toplevel_decoration_v1_set_user_data(struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zxdg_toplevel_decoration_v1, user_data); +} + +/** @ingroup iface_zxdg_toplevel_decoration_v1 */ +static inline void * +zxdg_toplevel_decoration_v1_get_user_data(struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zxdg_toplevel_decoration_v1); +} + +static inline uint32_t +zxdg_toplevel_decoration_v1_get_version(struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_decoration_v1); +} + +/** + * @ingroup iface_zxdg_toplevel_decoration_v1 + * + * Switch back to a mode without any server-side decorations at the next + * commit, unless a new xdg_toplevel_decoration is created for the surface + * first. + */ +static inline void +zxdg_toplevel_decoration_v1_destroy(struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_decoration_v1, + ZXDG_TOPLEVEL_DECORATION_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_decoration_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zxdg_toplevel_decoration_v1 + * + * Set the toplevel surface decoration mode. This informs the compositor + * that the client prefers the provided decoration mode. + * + * After requesting a decoration mode, the compositor will respond by + * emitting an xdg_surface.configure event. The client should then update + * its content, drawing it without decorations if the received mode is + * server-side decorations. The client must also acknowledge the configure + * when committing the new content (see xdg_surface.ack_configure). + * + * The compositor can decide not to use the client's mode and enforce a + * different mode instead. + * + * Clients whose decoration mode depend on the xdg_toplevel state may send + * a set_mode request in response to an xdg_surface.configure event and wait + * for the next xdg_surface.configure event to prevent unwanted state. + * Such clients are responsible for preventing configure loops and must + * make sure not to send multiple successive set_mode requests with the + * same decoration mode. + * + * If an invalid mode is supplied by the client, the invalid_mode protocol + * error is raised by the compositor. + */ +static inline void +zxdg_toplevel_decoration_v1_set_mode(struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, uint32_t mode) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_decoration_v1, + ZXDG_TOPLEVEL_DECORATION_V1_SET_MODE, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_decoration_v1), 0, mode); +} + +/** + * @ingroup iface_zxdg_toplevel_decoration_v1 + * + * Unset the toplevel surface decoration mode. This informs the compositor + * that the client doesn't prefer a particular decoration mode. + * + * This request has the same semantics as set_mode. + */ +static inline void +zxdg_toplevel_decoration_v1_unset_mode(struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_decoration_v1, + ZXDG_TOPLEVEL_DECORATION_V1_UNSET_MODE, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_decoration_v1), 0); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Source/GNUmakefile.preamble b/Source/GNUmakefile.preamble index b4b47aee..911449d9 100644 --- a/Source/GNUmakefile.preamble +++ b/Source/GNUmakefile.preamble @@ -45,7 +45,7 @@ CONFIG_SYSTEM_INCL += $(GRAPHIC_CFLAGS) # Additional library directories the linker should search #ADDITIONAL_LIB_DIRS += -CONFIG_SYSTEM_LIB_DIR += $(GRAPHIC_LFLAGS) +CONFIG_SYSTEM_LIB_DIR += $(GRAPHIC_LFLAGS) -lGL -lEGL -lwayland-egl # # Flags for compiling as a bundle or library (if the system depends diff --git a/Source/cairo/WaylandCairoShmSurface.m b/Source/cairo/WaylandCairoShmSurface.m index 712816e2..93b20548 100644 --- a/Source/cairo/WaylandCairoShmSurface.m +++ b/Source/cairo/WaylandCairoShmSurface.m @@ -1,12 +1,21 @@ /* WaylandCairoSurface - WaylandCairoShmSurface - A cairo surface backed by a wayland - shared memory buffer. - After the wayland surface is configured, the buffer needs to be - attached to the surface. Subsequent changes to the cairo surface - needs to be notified to the wayland server using wl_surface_damage - and wl_surface_commit. The buffer is freed after the compositor - releases it and the cairo surface is not in use. + WaylandCairoShmSurface - A cairo surface backed by a Wayland shared-memory + buffer. + + Buffer lifecycle: + 1. createShmBuffer — allocate SHM fd, mmap, create wl_shm_pool + wl_buffer. + Destroy the pool immediately (safe; the buffer keeps the mapping alive). + 2. initWithDevice — attach the buffer to the wl_surface and commit with a + full-surface damage region. Only after xdg_surface_configure (i.e. + window->configured == YES). + 3. handleExposeRect — re-attach with the actual damage rect on every + AppKit expose. If the compositor still holds the buffer (busy == true), + record needs_repaint and return; the release callback will re-commit. + 4. buffer_handle_release — compositor released the buffer. If needs_repaint + is set, immediately re-attach + commit to avoid losing the missed frame. + 5. dealloc / finishBuffer — destroy the wl_buffer, unmap memory, close the + FD, and free the struct once the compositor has released it. Copyright (C) 2020 Free Software Foundation, Inc. @@ -45,73 +54,82 @@ #include -static const enum wl_shm_format wl_fmt = WL_SHM_FORMAT_ARGB8888; -static const cairo_format_t cairo_fmt = CAIRO_FORMAT_ARGB32; +static const enum wl_shm_format wl_fmt = WL_SHM_FORMAT_ARGB8888; +static const cairo_format_t cairo_fmt = CAIRO_FORMAT_ARGB32; + +/* ── Buffer lifetime ─────────────────────────────────────────────────────── */ + +/* Free the pool_buffer once both conditions hold: + * (a) the compositor has released the wl_buffer (busy == false), and + * (b) nobody is rendering into the cairo surface (surface == NULL). + * Also closes the SHM file descriptor to prevent FD leaks. */ static void finishBuffer(struct pool_buffer *buf) { - // The buffer can be deleted if it has been released by the compositor - // and if not used by the cairo surface - if(buf == NULL || buf->busy || buf->surface != NULL) - { + if (buf == NULL || buf->busy || buf->surface != NULL) return; - } + if (buf->buffer) { wl_buffer_destroy(buf->buffer); + buf->buffer = NULL; } if (buf->data) { munmap(buf->data, buf->size); + buf->data = NULL; + } + if (buf->poolfd >= 0) + { + close(buf->poolfd); + buf->poolfd = -1; } free(buf); - return; } +/* Compositor released the buffer. If a repaint was queued while the buffer + * was busy, re-attach and commit now so the frame is not permanently lost. */ static void buffer_handle_release(void *data, struct wl_buffer *wl_buffer) { struct pool_buffer *buffer = data; buffer->busy = false; - // If the buffer was not released before dealloc + + if (buffer->needs_repaint && buffer->owner_surface) + { + buffer->needs_repaint = false; + buffer->busy = true; + + wl_surface_attach(buffer->owner_surface, wl_buffer, 0, 0); + wl_surface_damage(buffer->owner_surface, 0, 0, + (int32_t)buffer->width, (int32_t)buffer->height); + wl_surface_commit(buffer->owner_surface); + + if (buffer->owner_display) + wl_display_flush(buffer->owner_display); + + /* Don't call finishBuffer — we just re-submitted the buffer. */ + return; + } + finishBuffer(buffer); } static const struct wl_buffer_listener buffer_listener = { - // Sent by the compositor when it's no longer using a buffer .release = buffer_handle_release, }; -// Creates a file descriptor for the compositor to share pixel buffers + +/* ── SHM buffer allocation ───────────────────────────────────────────────── */ + +/* Creates an anonymous in-memory file suitable for sharing with the + * compositor via wl_shm. memfd_create is preferred over mkstemp because + * it never touches the filesystem and automatically cleans up on close. */ static int createPoolFile(off_t size) { - static const char template[] = "/gnustep-shared-XXXXXX"; - const char *path; - char *name; - int fd; - - path = getenv("XDG_RUNTIME_DIR"); - if (!path) - { - errno = ENOENT; - return -1; - } - - name = malloc(strlen(path) + sizeof(template)); - if (!name) - { - return -1; - } - - strcpy(name, path); - strcat(name, template); - - fd = memfd_create(name, MFD_CLOEXEC); - - free(name); - + int fd = memfd_create("gnustep-wl-shm", MFD_CLOEXEC); if (fd < 0) return -1; @@ -128,104 +146,136 @@ createShmBuffer(int width, int height, struct wl_shm *shm) { uint32_t stride = cairo_format_stride_for_width(cairo_fmt, width); - size_t size = stride * height; + size_t size = stride * height; + + if (size == 0) + return NULL; + + struct pool_buffer *buf = calloc(1, sizeof(struct pool_buffer)); + if (!buf) + return NULL; - struct pool_buffer * buf = malloc(sizeof(struct pool_buffer)); + buf->poolfd = -1; - void *data = NULL; - if (size > 0) + buf->poolfd = createPoolFile(size); + if (buf->poolfd < 0) { - buf->poolfd = createPoolFile(size); - if (buf->poolfd == -1) - { - return NULL; - } - - data - = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, buf->poolfd, 0); - if (data == MAP_FAILED) - { - return NULL; - } - - buf->pool = wl_shm_create_pool(shm, buf->poolfd, size); - buf->buffer = wl_shm_pool_create_buffer(buf->pool, 0, width, height, - stride, wl_fmt); - wl_buffer_add_listener(buf->buffer, &buffer_listener, buf); + free(buf); + return NULL; } - else - { - return NULL; - } - - buf->data = data; - buf->size = size; - buf->width = width; - buf->height = height; - buf->surface = cairo_image_surface_create_for_data(data, cairo_fmt, width, height, stride); - - if(buf->pool) - { - wl_shm_pool_destroy(buf->pool); - } + + buf->data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, buf->poolfd, 0); + if (buf->data == MAP_FAILED) + { + close(buf->poolfd); + free(buf); + return NULL; + } + + /* Create the pool and immediately the buffer. The pool can be destroyed + * right after — the buffer retains the memory mapping independently. */ + struct wl_shm_pool *pool = wl_shm_create_pool(shm, buf->poolfd, size); + buf->buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, wl_fmt); + wl_shm_pool_destroy(pool); + /* buf->pool is left NULL — pool is gone, nothing to free later. */ + + if (!buf->buffer) + { + munmap(buf->data, size); + close(buf->poolfd); + free(buf); + return NULL; + } + + wl_buffer_add_listener(buf->buffer, &buffer_listener, buf); + + buf->size = size; + buf->width = width; + buf->height = height; + buf->surface = cairo_image_surface_create_for_data( + buf->data, cairo_fmt, width, height, stride); + return buf; } + +/* ── WaylandCairoShmSurface ──────────────────────────────────────────────── */ + @implementation WaylandCairoShmSurface -{ - struct pool_buffer *pbuffer; -} + - (id)initWithDevice:(void *)device { - struct window *window = (struct window *) device; - NSDebugLog(@"WaylandCairoShmSurface: initWithDevice win=%d", - window->window_id); + struct window *window = (struct window *)device; + NSDebugLog(@"WaylandCairoShmSurface: initWithDevice win=%d", window->window_id); gsDevice = device; - pbuffer = createShmBuffer(window->width, window->height, window->wlconfig->shm); - + pbuffer = createShmBuffer((int)window->width, (int)window->height, + window->wlconfig->shm); if (pbuffer == NULL) { - NSDebugLog(@"failed to obtain buffer"); + NSDebugLog(@"WaylandCairoShmSurface: failed to allocate SHM buffer"); return nil; } _surface = pbuffer->surface; - - window->buffer_needs_attach = YES; if (_surface == NULL) { - NSDebugLog(@"can't create cairo surface"); + NSDebugLog(@"WaylandCairoShmSurface: failed to create cairo surface"); + finishBuffer(pbuffer); + pbuffer = NULL; return nil; } + /* Wire back-pointers so the release callback can re-commit missed frames. */ + pbuffer->owner_surface = window->surface; + pbuffer->owner_display = window->wlconfig->display; + + window->wcs = self; + if (window->configured) { - // we can attach a buffer to the surface only if the surface is configured - // this is usually done in the configure event handler - // in case of resize of an already configured surface - // we should reattach the new allocated buffer - NSDebugLog(@"wl_surface_attach: win=%d", - window->window_id); + /* Attach the fresh buffer with a full-surface damage region. + * wl_surface_damage is mandatory before commit — without it the + * compositor treats the commit as a no-op for rendering purposes. */ window->buffer_needs_attach = NO; + pbuffer->busy = true; wl_surface_attach(window->surface, pbuffer->buffer, 0, 0); + wl_surface_damage(window->surface, 0, 0, + (int32_t)window->width, (int32_t)window->height); wl_surface_commit(window->surface); } - window->wcs = self; + else + { + window->buffer_needs_attach = YES; + } return self; } - (void)dealloc { - struct window *window = (struct window *) gsDevice; - NSDebugLog(@"WaylandCairoSurface: dealloc win=%d", window->window_id); + struct window *window = (struct window *)gsDevice; + NSDebugLog(@"WaylandCairoShmSurface: dealloc win=%d", window->window_id); + + /* Detach this surface from the window so it won't be used again. */ + if (window->wcs == self) + window->wcs = nil; + + /* Sever the Cairo→buffer link; the buffer may still be compositor-held. */ cairo_surface_destroy(_surface); - _surface = NULL; + _surface = NULL; pbuffer->surface = NULL; - // try to free the buffer if already released by the compositor + + /* Clear back-pointers so the release callback doesn't touch freed data. */ + pbuffer->owner_surface = NULL; + pbuffer->owner_display = NULL; + pbuffer->needs_repaint = false; + + /* Free immediately if the compositor has already released the buffer; + * otherwise finishBuffer defers until the release callback fires. */ finishBuffer(pbuffer); + pbuffer = NULL; [super dealloc]; } @@ -233,38 +283,92 @@ - (void)dealloc - (NSSize)size { if (_surface == NULL) - { - return NSZeroSize; - } + return NSZeroSize; return NSMakeSize(cairo_image_surface_get_width(_surface), - cairo_image_surface_get_height(_surface)); + cairo_image_surface_get_height(_surface)); } - (void)handleExposeRect:(NSRect)rect { - struct window *window = (struct window *) gsDevice; - NSDebugLog(@"[CairoSurface handleExposeRect] %d", window->window_id); + struct window *window = (struct window *)gsDevice; - window->buffer_needs_attach = YES; + if (!window->configured) + { + window->buffer_needs_attach = YES; + return; + } - if (window->configured) + /* If the buffer dimensions no longer match the window (e.g. after a + * compositor-driven resize), the old buffer is stale. Mark it for + * repaint-on-release so no frame is lost, then bail out — AppKit will + * allocate a correctly-sized WaylandCairoShmSurface via the next + * setWindowdevice:forContext: call. */ + if (pbuffer->width != (uint32_t)window->width + || pbuffer->height != (uint32_t)window->height) { - window->buffer_needs_attach = NO; - wl_surface_attach(window->surface, pbuffer->buffer, 0, 0); - NSDebugLog(@"[%d] updating region: %d,%d %fx%f", window->window_id, 0, 0, - window->width, window->height); - // FIXME we should update only the damaged area defined as x,y,width, - // height at the moment it doesnt work - wl_surface_damage(window->surface, 0, 0, window->width, window->height); - wl_surface_commit(window->surface); - wl_display_dispatch_pending(window->wlconfig->display); - wl_display_flush(window->wlconfig->display); + NSDebugLog(@"[%d] handleExposeRect: size mismatch buf=%dx%d win=%dx%d — " + @"deferring until resize completes", + window->window_id, + pbuffer->width, pbuffer->height, + (int)window->width, (int)window->height); + pbuffer->needs_repaint = true; + return; } + + /* If the compositor still owns the buffer, queue a repaint for the moment + * it returns it. Attaching a busy buffer causes a protocol error. */ + if (pbuffer->busy) + { + NSDebugLog(@"[%d] handleExposeRect: buffer busy — queuing repaint", + window->window_id); + pbuffer->needs_repaint = true; + return; + } + + /* Attach, mark the actual damaged region, and commit. + * Using the precise rect (converted to integer coordinates) reduces the + * compositor's repaint area for partial-surface updates. */ + int dx = (int)NSMinX(rect); + int dy = (int)(window->height - NSMaxY(rect)); /* flip Y: AppKit → Wayland */ + int dw = (int)NSWidth(rect) + 1; /* +1: cover sub-pixel edges */ + int dh = (int)NSHeight(rect) + 1; + + /* Clamp to buffer dimensions. */ + if (dx < 0) dx = 0; + if (dy < 0) dy = 0; + if (dx + dw > (int)pbuffer->width) dw = (int)pbuffer->width - dx; + if (dy + dh > (int)pbuffer->height) dh = (int)pbuffer->height - dy; + + NSDebugLog(@"[%d] handleExposeRect: attach+damage (%d,%d %dx%d)", + window->window_id, dx, dy, dw, dh); + + pbuffer->needs_repaint = false; + pbuffer->busy = true; + window->buffer_needs_attach = NO; + + wl_surface_attach(window->surface, pbuffer->buffer, 0, 0); + wl_surface_damage(window->surface, dx, dy, dw, dh); + wl_surface_commit(window->surface); + wl_display_dispatch_pending(window->wlconfig->display); + wl_display_flush(window->wlconfig->display); } - (void)destroySurface { - // noop this is an offscreen surface - // no need to destroy it when not visible + /* Offscreen surface — no-op. Destruction is handled in dealloc. */ } + +- (void)clearOwnerSurface +{ + /* Called from destroySurfaceRole: just before wl_surface_destroy. + * Prevents the buffer_handle_release callback from writing to the + * about-to-be-freed proxy. */ + if (pbuffer) + { + pbuffer->owner_surface = NULL; + pbuffer->owner_display = NULL; + pbuffer->needs_repaint = false; + } +} + @end diff --git a/Source/wayland/GNUmakefile b/Source/wayland/GNUmakefile index a106b5e8..25951877 100644 --- a/Source/wayland/GNUmakefile +++ b/Source/wayland/GNUmakefile @@ -41,6 +41,8 @@ wayland_LOCALIZED_RESOURCE_FILES = \ wayland_C_FILES = \ xdg-shell-protocol.c \ wlr-layer-shell-protocol.c \ +xdg-decoration-unstable-v1-protocol.c \ +text-input-unstable-v3-protocol.c \ # The Objective-C source files to be compiled wayland_OBJC_FILES = \ @@ -51,6 +53,10 @@ WaylandServer+Keyboard.m \ WaylandServer+Seat.m \ WaylandServer+Xdgshell.m \ WaylandServer+Layershell.m \ +WaylandGLContext.m \ +WaylandGLPixelFormat.m \ +WaylandInputServer.m \ +WaylandDragView.m \ -include GNUmakefile.preamble diff --git a/Source/wayland/GNUmakefile.preamble b/Source/wayland/GNUmakefile.preamble index 8cde639f..5d43eb77 100644 --- a/Source/wayland/GNUmakefile.preamble +++ b/Source/wayland/GNUmakefile.preamble @@ -42,7 +42,7 @@ ADDITIONAL_INCLUDE_DIRS += -I../../Headers \ -I../$(GNUSTEP_TARGET_DIR) $(GRAPHIC_CFLAGS) # Additional LDFLAGS to pass to the linker -ADDITIONAL_LDFLAGS = +ADDITIONAL_LDFLAGS = -lGL -lEGL -lwayland-egl # Additional library directories the linker should search ADDITIONAL_LIB_DIRS = diff --git a/Source/wayland/WaylandDragView.m b/Source/wayland/WaylandDragView.m new file mode 100644 index 00000000..cc4df251 --- /dev/null +++ b/Source/wayland/WaylandDragView.m @@ -0,0 +1,1118 @@ +/* WaylandDragView - Drag and Drop for Wayland backend + + Copyright (C) 2024 Free Software Foundation, Inc. + + This file is part of the GNUstep Backend. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; see the file COPYING.LIB. + If not, see or write to the + Free Software Foundation, 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "wayland/WaylandServer.h" +#include "wayland/WaylandDragView.h" + +/* Private category to expose wlconfig from WaylandServer */ +@interface WaylandServer (DragViewAccess) +- (WaylandConfig *) wlconfig; +@end + +@implementation WaylandServer (DragViewAccess) +- (WaylandConfig *) wlconfig +{ + return wlconfig; +} +@end + + +/* ── MIME ↔ pasteboard type mapping ──────────────────────────────────────── */ + +static const struct { const char *mime; const char *pboard; } kMimeMap[] = { + { "text/plain;charset=utf-8", "NSStringPboardType" }, + { "text/plain", "NSStringPboardType" }, + { "text/uri-list", "NSFilenamesPboardType" }, + { "application/rtf", "NSRTFPboardType" }, + { "image/tiff", "NSTIFFPboardType" }, + { "image/png", "NSPNGPboardType" }, + { NULL, NULL } +}; + +static const char * +mime_for_pboard_type(NSString *pt) +{ + if ([pt isEqual: @"NSStringPboardType"] || [pt isEqual: NSPasteboardTypeString]) + return "text/plain;charset=utf-8"; + if ([pt isEqual: @"NSFilenamesPboardType"]) + return "text/uri-list"; + if ([pt isEqual: @"NSRTFPboardType"]) + return "application/rtf"; + if ([pt isEqual: @"NSTIFFPboardType"]) + return "image/tiff"; + if ([pt isEqual: @"NSPNGPboardType"]) + return "image/png"; + return NULL; +} + +static NSString * +pboard_type_for_mime(const char *mime) +{ + for (int i = 0; kMimeMap[i].mime; i++) + if (strcasecmp(mime, kMimeMap[i].mime) == 0) + return [NSString stringWithUTF8String: kMimeMap[i].pboard]; + return nil; +} + + +/* ── Action mapping ──────────────────────────────────────────────────────── */ + +static uint32_t +ns_op_to_wl_actions(NSDragOperation op) +{ + uint32_t a = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + if (op & NSDragOperationCopy) a |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + if (op & NSDragOperationMove) a |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + if (op & NSDragOperationDelete) a |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + if (op & NSDragOperationGeneric) a |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + return a ? a : WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; +} + +static NSDragOperation +wl_action_to_ns(uint32_t action) +{ + switch (action) { + case WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE: return NSDragOperationMove; + case WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY: + default: return NSDragOperationCopy; + } +} + + +/* ── Inbound offer MIME helpers ───────────────────────────────────────────── */ + +static void +dnd_offer_mimes_free(WaylandConfig *wlconfig) +{ + if (wlconfig->dnd_offer_mimes) + { + for (int i = 0; i < wlconfig->dnd_offer_mime_count; i++) + free(wlconfig->dnd_offer_mimes[i]); + free(wlconfig->dnd_offer_mimes); + wlconfig->dnd_offer_mimes = NULL; + } + wlconfig->dnd_offer_mime_count = 0; + wlconfig->dnd_offer_mime_cap = 0; +} + +static void +dnd_offer_mime_append(WaylandConfig *wlconfig, const char *mime) +{ + if (wlconfig->dnd_offer_mime_count >= wlconfig->dnd_offer_mime_cap) + { + int cap = wlconfig->dnd_offer_mime_cap ? wlconfig->dnd_offer_mime_cap * 2 : 8; + wlconfig->dnd_offer_mimes = realloc(wlconfig->dnd_offer_mimes, + cap * sizeof(char *)); + wlconfig->dnd_offer_mime_cap = cap; + } + wlconfig->dnd_offer_mimes[wlconfig->dnd_offer_mime_count++] = strdup(mime); +} + +/* Return the first MIME type in the offer that we know how to receive. + Populates *pboardType if non-NULL. Returns NULL if nothing matches. */ +static const char * +best_mime_for_offer(WaylandConfig *wlconfig, NSString **pboardType) +{ + static const char *preferred[] = { + "text/plain;charset=utf-8", "text/plain", "text/uri-list", + "application/rtf", "image/tiff", "image/png", NULL + }; + for (int p = 0; preferred[p]; p++) + for (int i = 0; i < wlconfig->dnd_offer_mime_count; i++) + if (strcasecmp(wlconfig->dnd_offer_mimes[i], preferred[p]) == 0) + { + NSString *pt = pboard_type_for_mime(preferred[p]); + if (pt) + { + if (pboardType) *pboardType = pt; + return preferred[p]; + } + } + return NULL; +} + + +/* ── Read all data from a pipe FD into NSData ─────────────────────────────── */ + +static NSData * +read_fd_to_data(int fd) +{ + size_t cap = 4096, total = 0; + char *buf = malloc(cap); + if (!buf) { close(fd); return nil; } + + ssize_t n; + while ((n = read(fd, buf + total, cap - total)) > 0) + { + total += n; + if (total == cap) + { + cap *= 2; + char *nb = realloc(buf, cap); + if (!nb) { free(buf); close(fd); return nil; } + buf = nb; + } + } + close(fd); + + NSData *d = [NSData dataWithBytes: buf length: total]; + free(buf); + return d; +} + + +/* ── Post a fake NSLeftMouseUp to exit GSDragView's event loop ────────────── */ + +static void +post_fake_mouse_up(void) +{ + NSEvent *ev = + [NSEvent mouseEventWithType: NSLeftMouseUp + location: NSZeroPoint + modifierFlags: 0 + timestamp: [NSDate timeIntervalSinceReferenceDate] + windowNumber: 0 + context: nil + eventNumber: 0 + clickCount: 1 + pressure: 0.0 + buttonNumber: 0 + deltaX: 0 + deltaY: 0 + deltaZ: 0]; + [NSApp postEvent: ev atStart: YES]; +} + + +/* ── wl_data_source listener (outbound drag) ──────────────────────────────── */ + +static void +data_source_target(void *data, struct wl_data_source *source, const char *mime) +{ + NSDebugFLLog(@"WaylandDnD", @"data_source_target: %s", mime ? mime : "(none)"); +} + +static void +data_source_send(void *data, struct wl_data_source *source, + const char *mime_type, int32_t fd) +{ + NSDebugFLLog(@"WaylandDnD", @"data_source_send: %s", mime_type); + + WaylandDragView *dv = [WaylandDragView sharedDragView]; + NSPasteboard *pb = [dv draggingPasteboard]; + + if (strcmp(mime_type, "text/plain;charset=utf-8") == 0 + || strcmp(mime_type, "text/plain") == 0) + { + NSString *s = [pb stringForType: NSStringPboardType]; + if (!s) s = [pb stringForType: NSPasteboardTypeString]; + if (s) + { + const char *utf8 = [s UTF8String]; + write(fd, utf8, strlen(utf8)); + } + } + else if (strcmp(mime_type, "text/uri-list") == 0) + { + NSArray *names = [pb propertyListForType: @"NSFilenamesPboardType"]; + if (names) + { + NSMutableString *list = [NSMutableString string]; + for (NSString *path in names) + { + NSURL *url = [NSURL fileURLWithPath: path]; + [list appendFormat: @"%@\r\n", [url absoluteString]]; + } + const char *utf8 = [list UTF8String]; + write(fd, utf8, strlen(utf8)); + } + } + else + { + /* Generic binary fallback: look for a pasteboard type whose MIME matches */ + NSString *pt = pboard_type_for_mime(mime_type); + if (pt) + { + NSData *d = [pb dataForType: pt]; + if (d) write(fd, [d bytes], [d length]); + } + } + + close(fd); +} + +static void +data_source_cancelled(void *data, struct wl_data_source *source) +{ + NSDebugFLLog(@"WaylandDnD", @"data_source_cancelled"); + WaylandConfig *wlconfig = data; + wlconfig->dnd_source = NULL; + post_fake_mouse_up(); +} + +static void +data_source_dnd_drop_performed(void *data, struct wl_data_source *source) +{ + NSDebugFLLog(@"WaylandDnD", @"data_source_dnd_drop_performed"); + /* Wait for dnd_finished before exiting the drag loop. */ +} + +static void +data_source_dnd_finished(void *data, struct wl_data_source *source) +{ + NSDebugFLLog(@"WaylandDnD", @"data_source_dnd_finished"); + WaylandConfig *wlconfig = data; + wlconfig->dnd_source = NULL; + post_fake_mouse_up(); +} + +static void +data_source_action(void *data, struct wl_data_source *source, uint32_t action) +{ + NSDebugFLLog(@"WaylandDnD", @"data_source_action: %u", action); + WaylandConfig *wlconfig = data; + wlconfig->dnd_current_action = action; +} + +static const struct wl_data_source_listener data_source_listener = { + data_source_target, + data_source_send, + data_source_cancelled, + data_source_dnd_drop_performed, + data_source_dnd_finished, + data_source_action, +}; + + +/* ── wl_data_offer listener (inbound MIME accumulation) ──────────────────── */ + +static void +data_offer_offer(void *data, struct wl_data_offer *offer, const char *mime_type) +{ + WaylandConfig *wlconfig = data; + NSDebugFLLog(@"WaylandDnD", @"data_offer_offer: %s", mime_type); + dnd_offer_mime_append(wlconfig, mime_type); +} + +static void +data_offer_source_actions(void *data, struct wl_data_offer *offer, + uint32_t source_actions) +{ + WaylandConfig *wlconfig = data; + wlconfig->dnd_offer_source_actions = source_actions; + NSDebugFLLog(@"WaylandDnD", @"data_offer_source_actions: 0x%x", source_actions); +} + +static void +data_offer_action(void *data, struct wl_data_offer *offer, uint32_t dnd_action) +{ + WaylandConfig *wlconfig = data; + wlconfig->dnd_current_action = dnd_action; + NSDebugFLLog(@"WaylandDnD", @"data_offer_action: %u", dnd_action); +} + +static const struct wl_data_offer_listener data_offer_listener = { + data_offer_offer, + data_offer_source_actions, + data_offer_action, +}; + + +/* ── wl_data_device listener (inbound drag events) ───────────────────────── */ + +static void +device_data_offer(void *data, struct wl_data_device *device, + struct wl_data_offer *offer) +{ + WaylandConfig *wlconfig = data; + NSDebugFLLog(@"WaylandDnD", @"device_data_offer: %p", (void *)offer); + + /* Reset MIME list for this new offer; offer_offer callbacks follow. */ + dnd_offer_mimes_free(wlconfig); + wlconfig->dnd_offer = offer; + wlconfig->dnd_offer_source_actions = 0; + wlconfig->dnd_current_action = 0; + wl_data_offer_add_listener(offer, &data_offer_listener, wlconfig); +} + +static void +device_enter(void *data, struct wl_data_device *device, + uint32_t serial, struct wl_surface *surface, + wl_fixed_t x_fixed, wl_fixed_t y_fixed, + struct wl_data_offer *offer) +{ + WaylandConfig *wlconfig = data; + float x = wl_fixed_to_double(x_fixed); + float y = wl_fixed_to_double(y_fixed); + + wlconfig->dnd_x = x; + wlconfig->dnd_y = y; + wlconfig->dnd_incoming = YES; + wlconfig->event_serial = serial; + + struct window *window = surface ? surface_get_window(surface) : NULL; + wlconfig->dnd_target = window; + + NSDebugFLLog(@"WaylandDnD", @"device_enter: win=%d pos=(%g,%g)", + window ? window->window_id : -1, x, y); + + if (!offer || !window) + return; + + /* Detect in-process drag: we are both the source (dnd_source != NULL) and + * the target. The two cases need different treatment: + * + * in-process — deliver events directly to the window via sendEvent: so + * they bypass GSDragView's running event loop. Do NOT call + * setupInboundDragWithPasteboard: — it would overwrite + * dragPasteboard and break data_source_send. The shared + * WaylandDragView already carries the correct source info. + * + * inter-process — post events to the app queue (GSDragView loop picks them + * up) and set up the drag view as the inbound NSDraggingInfo. + */ + BOOL inProcess = (wlconfig->dnd_source != NULL); + + if (!inProcess) + { + /* Find the best MIME type we can receive from an external source. */ + NSString *pboardType = nil; + const char *mime = best_mime_for_offer(wlconfig, &pboardType); + + if (mime) + { + wl_data_offer_accept(offer, serial, mime); + + if (wlconfig->data_device_manager_version >= 3) + { + uint32_t myActions = ns_op_to_wl_actions( + NSDragOperationCopy | NSDragOperationMove); + wl_data_offer_set_actions(offer, myActions, + WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY); + } + } + else + { + wl_data_offer_accept(offer, serial, NULL); + } + + /* Set up the shared drag view as NSDraggingInfo for AppKit. */ + WaylandDragView *dv = [WaylandDragView sharedDragView]; + [dv setupInboundDragWithPasteboard: + [NSPasteboard pasteboardWithName: NSDragPboard] + operation: wl_action_to_ns( + wlconfig->dnd_offer_source_actions + ? wlconfig->dnd_offer_source_actions + : WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)]; + } + + NSWindow *nswindow = GSWindowWithNumber(window->window_id); + if (!nswindow) + return; + + NSPoint nsPos = NSMakePoint(x, window->height - y); + NSEvent *ev = + [NSEvent otherEventWithType: NSAppKitDefined + location: nsPos + modifierFlags: 0 + timestamp: [[NSDate date] timeIntervalSinceReferenceDate] + windowNumber: window->window_id + context: nil + subtype: GSAppKitDraggingEnter + data1: 0 + data2: 0]; + + /* Deliver directly for in-process; queue for inter-process. */ + if (inProcess) + [nswindow sendEvent: ev]; + else + [NSApp postEvent: ev atStart: NO]; +} + +static void +device_leave(void *data, struct wl_data_device *device) +{ + WaylandConfig *wlconfig = data; + NSDebugFLLog(@"WaylandDnD", @"device_leave"); + + struct window *window = wlconfig->dnd_target; + wlconfig->dnd_incoming = NO; + wlconfig->dnd_target = NULL; + + if (window) + { + NSWindow *nswindow = GSWindowWithNumber(window->window_id); + if (nswindow) + { + BOOL inProcess = (wlconfig->dnd_source != NULL); + NSEvent *ev = + [NSEvent otherEventWithType: NSAppKitDefined + location: NSZeroPoint + modifierFlags: 0 + timestamp: [[NSDate date] timeIntervalSinceReferenceDate] + windowNumber: window->window_id + context: nil + subtype: GSAppKitDraggingExit + data1: 0 + data2: 0]; + if (inProcess) + [nswindow sendEvent: ev]; + else + [NSApp postEvent: ev atStart: NO]; + } + } + + if (wlconfig->dnd_offer) + { + wl_data_offer_destroy(wlconfig->dnd_offer); + wlconfig->dnd_offer = NULL; + dnd_offer_mimes_free(wlconfig); + } +} + +static void +device_motion(void *data, struct wl_data_device *device, + uint32_t time, wl_fixed_t x_fixed, wl_fixed_t y_fixed) +{ + WaylandConfig *wlconfig = data; + float x = wl_fixed_to_double(x_fixed); + float y = wl_fixed_to_double(y_fixed); + wlconfig->dnd_x = x; + wlconfig->dnd_y = y; + + struct window *window = wlconfig->dnd_target; + if (!window) + return; + + NSDebugFLLog(@"WaylandDnD", @"device_motion: (%g,%g)", x, y); + + BOOL inProcess = (wlconfig->dnd_source != NULL); + + /* Keep accepting so the compositor knows we still want the drag + * (only meaningful for inter-process; in-process has no offer to negotiate). */ + if (!inProcess && wlconfig->dnd_offer) + { + NSString *pt = nil; + const char *mime = best_mime_for_offer(wlconfig, &pt); + if (mime) + wl_data_offer_accept(wlconfig->dnd_offer, wlconfig->event_serial, mime); + } + + NSWindow *nswindow = GSWindowWithNumber(window->window_id); + if (!nswindow) + return; + + NSPoint nsPos = NSMakePoint(x, window->height - y); + NSEvent *ev = + [NSEvent otherEventWithType: NSAppKitDefined + location: nsPos + modifierFlags: 0 + timestamp: (NSTimeInterval)time / 1000.0 + windowNumber: window->window_id + context: nil + subtype: GSAppKitDraggingUpdate + data1: (NSInteger)NSDragOperationCopy + data2: 0]; + + if (inProcess) + [nswindow sendEvent: ev]; + else + [NSApp postEvent: ev atStart: NO]; +} + +static void +device_drop(void *data, struct wl_data_device *device) +{ + WaylandConfig *wlconfig = data; + NSDebugFLLog(@"WaylandDnD", @"device_drop"); + + struct window *window = wlconfig->dnd_target; + + if (!window || !wlconfig->dnd_offer) + { + if (wlconfig->dnd_offer) + { + wl_data_offer_destroy(wlconfig->dnd_offer); + wlconfig->dnd_offer = NULL; + dnd_offer_mimes_free(wlconfig); + } + wlconfig->dnd_incoming = NO; + return; + } + + BOOL inProcess = (wlconfig->dnd_source != NULL); + + NSWindow *nswindow = GSWindowWithNumber(window->window_id); + + if (inProcess) + { + /* In-process drop: source and target are the same Wayland client. + * + * We cannot go through wl_data_offer_receive + pipe here: receiving + * triggers the compositor to call wl_data_source.send back to us, but + * we are blocked inside a dispatch callback and cannot re-enter the + * event loop to process that send event — a deadlock. + * + * Instead, copy the types and data from the source pasteboard (which + * WaylandDragView still holds from the dragImage: call) directly to + * NSDragPboard so the target view gets the correct data. */ + WaylandDragView *dv = [WaylandDragView sharedDragView]; + NSPasteboard *srcPboard = [dv draggingPasteboard]; + NSPasteboard *pboard = [NSPasteboard pasteboardWithName: NSDragPboard]; + + NSArray *types = [srcPboard types]; + if ([types count] > 0) + { + [pboard declareTypes: types owner: nil]; + for (NSString *type in types) + { + NSData *d = [srcPboard dataForType: type]; + if (d) + [pboard setData: d forType: type]; + } + } + + if (nswindow) + { + NSDragOperation op = wl_action_to_ns(wlconfig->dnd_current_action); + if (op == NSDragOperationNone) + op = NSDragOperationCopy; + NSPoint nsPos = + NSMakePoint(wlconfig->dnd_x, window->height - wlconfig->dnd_y); + NSEvent *ev = + [NSEvent otherEventWithType: NSAppKitDefined + location: nsPos + modifierFlags: 0 + timestamp: [[NSDate date] timeIntervalSinceReferenceDate] + windowNumber: window->window_id + context: nil + subtype: GSAppKitDraggingDrop + data1: (NSInteger)op + data2: 0]; + [nswindow sendEvent: ev]; + } + } + else + { + /* Inter-process drop: receive data through the pipe. */ + NSString *pboardType = nil; + const char *mime = best_mime_for_offer(wlconfig, &pboardType); + + if (!mime || !pboardType) + goto cleanup; + + int pipefd[2]; + if (pipe(pipefd) < 0) + goto cleanup; + + wl_data_offer_receive(wlconfig->dnd_offer, mime, pipefd[1]); + close(pipefd[1]); + wl_display_flush(wlconfig->display); + + NSData *rawData = read_fd_to_data(pipefd[0]); + + NSPasteboard *pboard = [NSPasteboard pasteboardWithName: NSDragPboard]; + [pboard declareTypes: @[pboardType] owner: nil]; + + if ([pboardType isEqual: @"NSStringPboardType"] + || [pboardType isEqual: NSPasteboardTypeString]) + { + NSString *s = [[NSString alloc] + initWithData: rawData encoding: NSUTF8StringEncoding]; + if (!s) + s = [[NSString alloc] + initWithData: rawData encoding: NSISOLatin1StringEncoding]; + if (s) + { + [pboard setString: s forType: pboardType]; + [s release]; + } + } + else if ([pboardType isEqual: @"NSFilenamesPboardType"]) + { + NSString *raw = [[NSString alloc] + initWithData: rawData encoding: NSUTF8StringEncoding]; + NSArray *lines = [raw componentsSeparatedByCharactersInSet: + [NSCharacterSet newlineCharacterSet]]; + NSMutableArray *paths = [NSMutableArray array]; + for (NSString *line in lines) + { + NSString *trimmed = [line stringByTrimmingCharactersInSet: + [NSCharacterSet whitespaceCharacterSet]]; + if ([trimmed length] == 0 || [trimmed hasPrefix: @"#"]) + continue; + NSURL *url = [NSURL URLWithString: trimmed]; + if ([url isFileURL]) + [paths addObject: [url path]]; + else if ([trimmed hasPrefix: @"/"]) + [paths addObject: trimmed]; + } + [pboard setPropertyList: paths forType: @"NSFilenamesPboardType"]; + [raw release]; + } + else + { + [pboard setData: rawData forType: pboardType]; + } + + if (nswindow) + { + NSDragOperation op = wl_action_to_ns(wlconfig->dnd_current_action); + NSPoint nsPos = + NSMakePoint(wlconfig->dnd_x, window->height - wlconfig->dnd_y); + NSEvent *ev = + [NSEvent otherEventWithType: NSAppKitDefined + location: nsPos + modifierFlags: 0 + timestamp: [[NSDate date] timeIntervalSinceReferenceDate] + windowNumber: window->window_id + context: nil + subtype: GSAppKitDraggingDrop + data1: (NSInteger)op + data2: 0]; + [NSApp postEvent: ev atStart: NO]; + } + + /* Finish the offer (v3+). */ + if (wlconfig->data_device_manager_version >= 3) + wl_data_offer_finish(wlconfig->dnd_offer); + } + +cleanup: + + wl_data_offer_destroy(wlconfig->dnd_offer); + wlconfig->dnd_offer = NULL; + dnd_offer_mimes_free(wlconfig); + wlconfig->dnd_incoming = NO; +} + +static void +device_selection(void *data, struct wl_data_device *device, + struct wl_data_offer *offer) +{ + WaylandConfig *wlconfig = data; + NSDebugFLLog(@"WaylandDnD", @"device_selection: %p", (void *)offer); + + /* Clipboard selection is outside M1 scope. Destroy the offer if it's the + * same as the pending dnd_offer (which means it was a selection, not DnD). */ + if (offer && offer == wlconfig->dnd_offer) + { + wl_data_offer_destroy(offer); + wlconfig->dnd_offer = NULL; + dnd_offer_mimes_free(wlconfig); + } + else if (offer) + { + wl_data_offer_destroy(offer); + } +} + +const struct wl_data_device_listener data_device_listener = { + device_data_offer, + device_enter, + device_leave, + device_motion, + device_drop, + device_selection, +}; + + +/* ── Lightweight NSWindow subclass for the drag icon ─────────────────────── */ + +@interface WaylandRawWindow : NSWindow +@end + +@implementation WaylandRawWindow + +- (BOOL) canBecomeMainWindow { return NO; } +- (BOOL) canBecomeKeyWindow { return NO; } + +- (void) _initDefaults +{ + [super _initDefaults]; + [self setReleasedWhenClosed: NO]; + [self setExcludedFromWindowsMenu: YES]; +} + +- (void) orderWindow: (NSWindowOrderingMode)place relativeTo: (NSInteger)otherWin +{ + [super orderWindow: place relativeTo: otherWin]; + [self setLevel: NSPopUpMenuWindowLevel]; +} + +@end + + +/* ── WaylandDragView ─────────────────────────────────────────────────────── */ + +@interface WaylandDragView () +{ + void *_dragCursorCid; + BOOL _waylandExternalDragActive; /* YES after wl_data_device_start_drag */ +} +@end + + +@implementation WaylandDragView + +static WaylandDragView *sharedDragView = nil; + ++ (id) sharedDragView +{ + if (sharedDragView == nil) + sharedDragView = [WaylandDragView new]; + return sharedDragView; +} + ++ (Class) windowClass +{ + return [WaylandRawWindow class]; +} + +- (void) updateDragInfoFromEvent: (NSEvent *)event +{ + destWindow = [event window]; + dragPoint = [event locationInWindow]; + dragSequence = [event timestamp]; + dragMask = [event data2]; +} + +- (void) resetDragInfo +{ + DESTROY(dragPasteboard); +} + +/* Called from device_enter to set up NSDraggingInfo for an inbound drag. */ +- (void) setupInboundDragWithPasteboard: (NSPasteboard *)pb + operation: (NSDragOperation)op +{ + ASSIGN(dragPasteboard, pb); + dragSource = nil; + destExternal = YES; + dragMask = op; + operationMask = NSDragOperationAll; +} + + +/* ── Outbound drag ───────────────────────────────────────────────────────── + * + * Override dragImage: to call wl_data_device_start_drag before letting + * GSDragView run its event loop. The data_source callbacks post a fake + * NSLeftMouseUp to exit the loop when the compositor signals completion. + */ +- (void) dragImage: (NSImage *)anImage + at: (NSPoint)screenLocation + offset: (NSSize)initialOffset + event: (NSEvent *)event + pasteboard: (NSPasteboard *)pboard + source: (id)sourceObject + slideBack: (BOOL)slideFlag +{ + WaylandConfig *wlconfig = [(WaylandServer *)GSCurrentServer() wlconfig]; + + if (!wlconfig->data_device || !wlconfig->data_device_manager) + { + NSDebugMLLog(@"WaylandDnD", + @"WaylandDragView: no wl_data_device — skipping external drag"); + [super dragImage: anImage at: screenLocation offset: initialOffset + event: event pasteboard: pboard source: sourceObject + slideBack: slideFlag]; + return; + } + + /* Find the origin surface (the surface the drag started on). */ + int originWinNum = [event windowNumber]; + struct window *originWin = get_window_with_id(wlconfig, originWinNum); + struct wl_surface *originSurface = originWin ? originWin->surface : NULL; + + if (!originSurface) + { + NSDebugMLLog(@"WaylandDnD", + @"WaylandDragView: no origin surface for window %d", originWinNum); + [super dragImage: anImage at: screenLocation offset: initialOffset + event: event pasteboard: pboard source: sourceObject + slideBack: slideFlag]; + return; + } + + /* Create the data source and offer all MIME types from the pasteboard. */ + struct wl_data_source *source = + wl_data_device_manager_create_data_source(wlconfig->data_device_manager); + if (!source) + { + NSDebugMLLog(@"WaylandDnD", @"WaylandDragView: failed to create wl_data_source"); + [super dragImage: anImage at: screenLocation offset: initialOffset + event: event pasteboard: pboard source: sourceObject + slideBack: slideFlag]; + return; + } + + wl_data_source_add_listener(source, &data_source_listener, wlconfig); + + for (NSString *pt in [pboard types]) + { + const char *mime = mime_for_pboard_type(pt); + if (mime) + { + wl_data_source_offer(source, mime); + /* Also offer the plain ASCII variant for text so legacy apps can receive it. */ + if (strcmp(mime, "text/plain;charset=utf-8") == 0) + wl_data_source_offer(source, "text/plain"); + } + } + + if (wlconfig->data_device_manager_version >= 3) + { + NSDragOperation srcMask = + [sourceObject draggingSourceOperationMaskForLocal: NO]; + wl_data_source_set_actions(source, ns_op_to_wl_actions(srcMask)); + } + + wlconfig->dnd_source = source; + + /* The drag serial comes from the button-press event that triggered this drag. */ + uint32_t serial = (uint32_t)[event eventNumber]; + + wl_data_device_start_drag(wlconfig->data_device, source, + originSurface, + NULL, /* icon surface — cursor is set via wl_pointer */ + serial); + wl_display_flush(wlconfig->display); + + _waylandExternalDragActive = YES; + + NSDebugMLLog(@"WaylandDnD", + @"WaylandDragView: wl_data_device_start_drag (serial=%u)", serial); + + /* Let GSDragView run its standard event loop. The data_source callbacks + * (cancelled / dnd_finished) will post a fake NSLeftMouseUp to exit it. */ + [super dragImage: anImage at: screenLocation offset: initialOffset + event: event pasteboard: pboard source: sourceObject + slideBack: slideFlag]; + + _waylandExternalDragActive = NO; + + /* Clean up the source if the compositor did not fire dnd_finished + * (e.g., version < 3 compositor). */ + if (wlconfig->dnd_source) + { + wl_data_source_destroy(wlconfig->dnd_source); + wlconfig->dnd_source = NULL; + } +} + +- (void) postDragEvent: (NSEvent *)theEvent +{ + if (!_waylandExternalDragActive) + { + [super postDragEvent: theEvent]; + return; + } + + /* During a Wayland-driven drag the compositor stops delivering pointer + * events, so only two event types matter in GSDragView's loop: + * + * NSLeftMouseUp — fake event posted by data_source callbacks to exit + * the loop when the drag ends (cancel or finish). + * + * NSAppKitDefined — GSAppKitDraggingEnter/Update/Drop events generated + * by the inter-process device_enter/motion/drop callbacks + * and posted to the app queue. Forward them through + * super so AppKit routes them to the target window's + * dragging protocol methods (draggingEntered: etc.). + * In-process events go directly via sendEvent: and + * never reach here. + * + * Everything else is suppressed — no pointer motion arrives in this mode. */ + switch ([theEvent type]) + { + case NSLeftMouseUp: + isDragging = NO; + break; + case NSAppKitDefined: + [super postDragEvent: theEvent]; + break; + default: + break; + } +} + +- (void) sendExternalEvent: (GSAppKitSubtype)subtype + action: (NSDragOperation)action + position: (NSPoint)eventLocation + timestamp: (NSTimeInterval)time + toWindow: (int)dWindowNumber +{ + /* The Wayland compositor manages the external drag entirely after + * wl_data_device_start_drag — no protocol messages need to be sent here. */ + NSDebugMLLog(@"WaylandDnD", + @"WaylandDragView: sendExternalEvent (subtype=%d) — handled by compositor", + (int)subtype); +} + + +/* ── Drag icon (outbound) ─────────────────────────────────────────────────── */ + +- (void) _setupWindowFor: (NSImage *)anImage + mousePosition: (NSPoint)mPoint + imagePosition: (NSPoint)iPoint +{ + if (anImage == nil) + anImage = [NSImage imageNamed: @"common_Close"]; + + NSSize imageSize = [anImage size]; + + [dragCell setImage: anImage]; + dragPosition = mPoint; + newPosition = mPoint; + offset.width = mPoint.x - iPoint.x; + offset.height = mPoint.y - iPoint.y; + + NSPoint hotspot; + hotspot.x = offset.width; + hotspot.y = imageSize.height - offset.height; + if (hotspot.x < 0) hotspot.x = 0; + if (hotspot.y < 0) hotspot.y = 0; + + NSDebugMLLog(@"WaylandDnD", @"WaylandDragView: drag cursor hotspot=(%g,%g)", + hotspot.x, hotspot.y); + + WaylandServer *server = (WaylandServer *)GSCurrentServer(); + [server imagecursor: hotspot : anImage : &_dragCursorCid]; + if (_dragCursorCid != NULL) + [server setcursor: _dragCursorCid]; +} + +- (void) _clearupWindow +{ + WaylandServer *server = (WaylandServer *)GSCurrentServer(); + + void *arrowCid = NULL; + [server standardcursor: GSArrowCursor : &arrowCid]; + if (arrowCid != NULL) + [server setcursor: arrowCid]; + + if (_dragCursorCid != NULL) + { + [server freecursor: _dragCursorCid]; + _dragCursorCid = NULL; + } +} + +- (void) _moveDraggedImageToNewPosition +{ + dragPosition = newPosition; +} + +- (NSWindow *) windowAcceptingDnDunder: (NSPoint)p + windowRef: (int *)mouseWindowRef +{ + WaylandConfig *wlconfig = + [(WaylandServer *)GSCurrentServer() wlconfig]; + struct window *window; + struct output *output = NULL; + + wl_list_for_each(output, &wlconfig->output_list, link) + break; + + if (output == NULL) + { + if (mouseWindowRef) *mouseWindowRef = 0; + return nil; + } + + int dragWinNum = (_window != nil) ? [_window windowNumber] : -1; + + struct window *candidate = NULL; + wl_list_for_each(window, &wlconfig->window_list, link) + { + if (window->window_id == dragWinNum) + continue; + if (window->ignoreMouse || window->terminated || !window->configured) + continue; + + float ns_x = window->pos_x; + float ns_y = output->height - window->pos_y - window->height; + + if (p.x >= ns_x && p.x < ns_x + window->width + && p.y >= ns_y && p.y < ns_y + window->height) + { + NSWindow *nswindow = GSWindowWithNumber(window->window_id); + if (nswindow == nil) continue; + NSCountedSet *dragTypes = + [GSCurrentServer() dragTypesForWindow: nswindow]; + if ([dragTypes count] > 0) + candidate = window; + } + } + + if (candidate != NULL) + { + if (mouseWindowRef) *mouseWindowRef = candidate->window_id; + return GSWindowWithNumber(candidate->window_id); + } + + if (mouseWindowRef) *mouseWindowRef = 0; + return nil; +} + +@end + + +/* ── WaylandServer (DragAndDrop) ─────────────────────────────────────────── */ + +@implementation WaylandServer (DragAndDrop) + +- (id ) dragInfo +{ + return [WaylandDragView sharedDragView]; +} + +- (BOOL) addDragTypes: (NSArray *)types toWindow: (NSWindow *)win +{ + return [super addDragTypes: types toWindow: win]; +} + +- (BOOL) removeDragTypes: (NSArray *)types fromWindow: (NSWindow *)win +{ + return [super removeDragTypes: types fromWindow: win]; +} + +@end diff --git a/Source/wayland/WaylandGLContext.m b/Source/wayland/WaylandGLContext.m new file mode 100644 index 00000000..5c71bd6b --- /dev/null +++ b/Source/wayland/WaylandGLContext.m @@ -0,0 +1,773 @@ +/* -*- mode:ObjC -*- + WaylandGLContext - backend implementation of NSOpenGLContext using EGL + + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of the GNUstep Backend. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; see the file COPYING.LIB. + If not, see or write to the + Free Software Foundation, 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wayland/WaylandServer.h" +#include "wayland/WaylandOpenGL.h" + +static WaylandGLContext *currentGLContext; + +@implementation WaylandGLContext + ++ (void)clearCurrentContext +{ + if (currentGLContext != nil && currentGLContext->_eglDisplay != EGL_NO_DISPLAY) + { + eglMakeCurrent(currentGLContext->_eglDisplay, + EGL_NO_SURFACE, + EGL_NO_SURFACE, + EGL_NO_CONTEXT); + } + currentGLContext = nil; +} + ++ (NSOpenGLContext *)currentContext +{ + return currentGLContext; +} + +- (void *)CGLContextObj +{ + return (void *)_eglContext; +} + +- (void)copyAttributesFromContext:(NSOpenGLContext *)context + withMask:(unsigned long)mask +{ + (void)context; + (void)mask; +} + +- (id)initWithCGLContextObj:(void *)context +{ + NSDebugMLLog(@"OpenGL", @"initWithCGLContextObj is not supported on Wayland (%p)", context); + [self release]; + return nil; +} + +- (BOOL)_ensureDisplayAndContextWithShare:(NSOpenGLContext *)share +{ + EGLint major; + EGLint minor; + EGLConfig eglConfig; + EGLContext shareContext = EGL_NO_CONTEXT; + EGLint glesContextAttrs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; + EGLint *contextAttrs = NULL; + struct wl_display *wlDisplay; + + if (_eglDisplay != EGL_NO_DISPLAY && _eglContext != EGL_NO_CONTEXT) + { + return YES; + } + + if (_window == NULL && [self _attachToWindowIfNeeded] == NO) + { + return NO; + } + + wlDisplay = NULL; + if (_window != NULL && _window->wlconfig != NULL) + { + wlDisplay = _window->wlconfig->display; + } + + if (wlDisplay == NULL) + { + NSDebugMLLog(@"OpenGL", @"Cannot create EGL display without an attached Wayland window"); + return NO; + } + + _eglDisplay = eglGetDisplay((EGLNativeDisplayType)wlDisplay); + if (_eglDisplay == EGL_NO_DISPLAY) + { + NSDebugMLLog(@"OpenGL", @"eglGetDisplay failed"); + return NO; + } + + if (eglInitialize(_eglDisplay, &major, &minor) == EGL_FALSE) + { + NSDebugMLLog(@"OpenGL", @"eglInitialize failed"); + return NO; + } + + if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) + { + NSDebugMLLog(@"OpenGL", @"eglBindAPI(EGL_OPENGL_API) failed, trying GLES2"); + if (eglBindAPI(EGL_OPENGL_ES_API) == EGL_FALSE) + { + NSDebugMLLog(@"OpenGL", @"eglBindAPI failed for OpenGL and GLES"); + return NO; + } + contextAttrs = glesContextAttrs; + } + + eglConfig = [(WaylandGLPixelFormat *)_pixelFormat eglConfigForDisplay:_eglDisplay]; + if (eglConfig == NULL) + { + return NO; + } + + if (share != nil && [share isKindOfClass:[WaylandGLContext class]]) + { + shareContext = ((WaylandGLContext *)share)->_eglContext; + } + + _eglContext = eglCreateContext(_eglDisplay, eglConfig, shareContext, contextAttrs); + if (_eglContext == EGL_NO_CONTEXT) + { + NSDebugMLLog(@"OpenGL", @"eglCreateContext failed"); + return NO; + } + + return YES; +} + +- (BOOL)_attachToWindowIfNeeded +{ + GSDisplayServer *server; + NSWindow *window; + struct window *newWindow; + + if (_view == nil) + { + return NO; + } + + window = [_view window]; + if (window == nil) + { + return NO; + } + + server = GSCurrentServer(); + newWindow = (struct window *)[server windowDevice:[window windowNumber]]; + if (newWindow == NULL) + { + return NO; + } + + if (_window != newWindow) + { + if (_window != NULL) + { + _window->usesOpenGL = NO; + } + _window = newWindow; + [self _destroySurface]; + } + + return YES; +} + +- (void)_computeViewGeometry:(NSRect *)outFrame +{ + NSRect bounds = [_view bounds]; + NSRect frame = [_view convertRect:bounds toView:nil]; + /* AppKit Y-up → Wayland Y-down: flip origin.y relative to window height */ + frame.origin.y = _window->height - NSMaxY(frame); + + static BOOL _loggedOnce = NO; + if (!_loggedOnce) + { + _loggedOnce = YES; + NSLog(@"WaylandGL: _computeViewGeometry:" + @" viewBounds=%@ convertedFrame=%@ windowH=%.0f" + @" → waylandFrame=%@", + NSStringFromRect(bounds), + NSStringFromRect([_view convertRect:bounds toView:nil]), + (double)_window->height, + NSStringFromRect(frame)); + } + + *outFrame = frame; +} + +- (BOOL)_ensureSurface +{ + EGLConfig eglConfig; + NSRect viewFrame; + struct wl_surface *renderSurface; + int subW, subH, subX, subY; + + if (_window == NULL || _window->surface == NULL) + { + return NO; + } + + [self _computeViewGeometry:&viewFrame]; + subX = (int)NSMinX(viewFrame); + subY = (int)NSMinY(viewFrame); + subW = (int)NSWidth(viewFrame); + subH = (int)NSHeight(viewFrame); + if (subW <= 0 || subH <= 0) + { + return NO; + } + + if (_glSurface == NULL) + { + WaylandConfig *wlconfig = _window->wlconfig; + + if (wlconfig->subcompositor == NULL) + { + NSLog(@"WaylandGL: _ensureSurface: no subcompositor — using window surface directly"); + renderSurface = _window->surface; + _window->usesOpenGL = YES; + } + else + { + _glSurface = wl_compositor_create_surface(wlconfig->compositor); + if (_glSurface == NULL) + { + NSLog(@"WaylandGL: _ensureSurface: wl_compositor_create_surface failed"); + return NO; + } + + _glSubsurface = wl_subcompositor_get_subsurface( + wlconfig->subcompositor, _glSurface, _window->surface); + if (_glSubsurface == NULL) + { + NSLog(@"WaylandGL: _ensureSurface: wl_subcompositor_get_subsurface failed"); + wl_surface_destroy(_glSurface); + _glSurface = NULL; + return NO; + } + + wl_subsurface_set_desync(_glSubsurface); + wl_subsurface_set_position(_glSubsurface, subX, subY); + _glSurfaceBinding = (struct wl_surface_binding *) + malloc(sizeof(struct wl_surface_binding)); + _glSurfaceBinding->window = _window; + _glSurfaceBinding->offset_x = (float)subX; + _glSurfaceBinding->offset_y = (float)subY; + wl_surface_set_user_data(_glSurface, _glSurfaceBinding); + wl_surface_commit(_window->surface); + wl_display_flush(wlconfig->display); + + NSLog(@"WaylandGL: _ensureSurface: subsurface created glSurface=%p pos=(%d,%d) size=(%dx%d)", + _glSurface, subX, subY, subW, subH); + renderSurface = _glSurface; + } + } + else + { + renderSurface = _glSurface; + } + + if (_eglWindow == NULL) + { + _eglWindow = wl_egl_window_create(renderSurface, subW, subH); + if (_eglWindow == NULL) + { + NSDebugMLLog(@"OpenGL", @"wl_egl_window_create failed"); + return NO; + } + } + + if (_eglSurface != EGL_NO_SURFACE) + { + return YES; + } + + eglConfig = [(WaylandGLPixelFormat *)_pixelFormat eglConfigForDisplay:_eglDisplay]; + _eglSurface = eglCreateWindowSurface(_eglDisplay, + eglConfig, + (EGLNativeWindowType)_eglWindow, + NULL); + + if (_eglSurface == EGL_NO_SURFACE) + { + NSLog(@"WaylandGL: _ensureSurface: eglCreateWindowSurface failed (0x%x)", eglGetError()); + return NO; + } + + if (_swapInterval >= 0) + { + eglSwapInterval(_eglDisplay, _swapInterval); + } + + NSLog(@"WaylandGL: _ensureSurface: EGL surface ready eglSurface=%p eglWindow=%p", + _eglSurface, _eglWindow); + return YES; +} + +- (void)_destroySurface +{ + if (_eglDisplay != EGL_NO_DISPLAY && _eglSurface != EGL_NO_SURFACE) + { + eglDestroySurface(_eglDisplay, _eglSurface); + _eglSurface = EGL_NO_SURFACE; + } + + if (_eglWindow != NULL) + { + wl_egl_window_destroy(_eglWindow); + _eglWindow = NULL; + } + + if (_glSubsurface != NULL) + { + wl_subsurface_destroy(_glSubsurface); + _glSubsurface = NULL; + } + + if (_glSurface != NULL) + { + wl_surface_destroy(_glSurface); + _glSurface = NULL; + } + + if (_glSurfaceBinding != NULL) + { + free(_glSurfaceBinding); + _glSurfaceBinding = NULL; + } +} + +- (void)_loadExtensions +{ + const char *eglExts; + const char *glExts; + + if (_eglDisplay == EGL_NO_DISPLAY || _eglContext == EGL_NO_CONTEXT) + return; + + _extensionsLoaded = YES; + + eglExts = eglQueryString(_eglDisplay, EGL_EXTENSIONS); + if (eglExts == NULL) + eglExts = ""; + + if (strstr(eglExts, "EGL_EXT_image_dma_buf_import") != NULL) + { + _pfnCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC) + eglGetProcAddress("eglCreateImageKHR"); + _pfnDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC) + eglGetProcAddress("eglDestroyImageKHR"); + if (_pfnCreateImageKHR != NULL && _pfnDestroyImageKHR != NULL) + { + _hasDmaBufImport = YES; + NSDebugMLLog(@"OpenGL", @"WaylandGL: EGL_EXT_image_dma_buf_import available"); + } + } + + if (_hasDmaBufImport + && strstr(eglExts, "EGL_EXT_image_dma_buf_import_modifiers") != NULL) + { + _pfnQueryDmaBufFormats = (PFNEGLQUERYDMABUFFORMATSEXTPROC) + eglGetProcAddress("eglQueryDmaBufFormatsEXT"); + _pfnQueryDmaBufModifiers = (PFNEGLQUERYDMABUFMODIFIERSEXTPROC) + eglGetProcAddress("eglQueryDmaBufModifiersEXT"); + if (_pfnQueryDmaBufFormats != NULL && _pfnQueryDmaBufModifiers != NULL) + { + _hasDmaBufImportModifiers = YES; + NSDebugMLLog(@"OpenGL", @"WaylandGL: EGL_EXT_image_dma_buf_import_modifiers available"); + } + } + + /* GL_OES_EGL_image + GL_OES_EGL_image_external — needs a current context */ + glExts = (const char *)glGetString(GL_EXTENSIONS); + if (glExts == NULL) + glExts = ""; + + if (strstr(glExts, "GL_OES_EGL_image") != NULL) + { + void *pfn = (void *)eglGetProcAddress("glEGLImageTargetTexture2DOES"); + if (pfn != NULL) + { + _pfnGLImageTargetTexture2D = pfn; + _hasExternalTexture = YES; + NSDebugMLLog(@"OpenGL", @"WaylandGL: GL_OES_EGL_image_external available"); + } + } +} + +- (BOOL)supportsDmaBufImport +{ + return _hasDmaBufImport; +} + +- (BOOL)supportsExternalTexture +{ + return _hasExternalTexture; +} + +- (EGLDisplay)eglDisplay +{ + return _eglDisplay; +} + +- (EGLImageKHR)createEGLImageFromDmaBufFd:(int)fd + width:(int)width + height:(int)height + stride:(int)stride + offset:(int)offset + fourcc:(uint32_t)fourcc +{ + EGLint attrs[] = { + EGL_WIDTH, (EGLint)width, + EGL_HEIGHT, (EGLint)height, + EGL_LINUX_DRM_FOURCC_EXT, (EGLint)fourcc, + EGL_DMA_BUF_PLANE0_FD_EXT, (EGLint)fd, + EGL_DMA_BUF_PLANE0_OFFSET_EXT, (EGLint)offset, + EGL_DMA_BUF_PLANE0_PITCH_EXT, (EGLint)stride, + EGL_NONE + }; + EGLImageKHR image; + + if (!_hasDmaBufImport || _pfnCreateImageKHR == NULL + || _eglDisplay == EGL_NO_DISPLAY) + return EGL_NO_IMAGE_KHR; + + image = _pfnCreateImageKHR(_eglDisplay, EGL_NO_CONTEXT, + EGL_LINUX_DMA_BUF_EXT, + (EGLClientBuffer)NULL, attrs); + if (image == EGL_NO_IMAGE_KHR) + NSDebugMLLog(@"OpenGL", @"WaylandGL: eglCreateImageKHR(dma-buf) failed: 0x%x", + eglGetError()); + return image; +} + +- (EGLImageKHR)createEGLImageFromDmaBufFd:(int)fd + width:(int)width + height:(int)height + stride:(int)stride + offset:(int)offset + fourcc:(uint32_t)fourcc + modifier:(uint64_t)modifier +{ + EGLint modLo = (EGLint)(modifier & 0xFFFFFFFFu); + EGLint modHi = (EGLint)(modifier >> 32); + EGLint attrs[] = { + EGL_WIDTH, (EGLint)width, + EGL_HEIGHT, (EGLint)height, + EGL_LINUX_DRM_FOURCC_EXT, (EGLint)fourcc, + EGL_DMA_BUF_PLANE0_FD_EXT, (EGLint)fd, + EGL_DMA_BUF_PLANE0_OFFSET_EXT, (EGLint)offset, + EGL_DMA_BUF_PLANE0_PITCH_EXT, (EGLint)stride, + EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, modLo, + EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, modHi, + EGL_NONE + }; + EGLImageKHR image; + + if (!_hasDmaBufImport || _pfnCreateImageKHR == NULL + || _eglDisplay == EGL_NO_DISPLAY) + return EGL_NO_IMAGE_KHR; + + if (!_hasDmaBufImportModifiers) + { + NSDebugMLLog(@"OpenGL", + @"WaylandGL: modifier requested but EGL_EXT_image_dma_buf_import_modifiers unavailable"); + return EGL_NO_IMAGE_KHR; + } + + image = _pfnCreateImageKHR(_eglDisplay, EGL_NO_CONTEXT, + EGL_LINUX_DMA_BUF_EXT, + (EGLClientBuffer)NULL, attrs); + if (image == EGL_NO_IMAGE_KHR) + NSDebugMLLog(@"OpenGL", + @"WaylandGL: eglCreateImageKHR(dma-buf+modifier) failed: 0x%x", + eglGetError()); + return image; +} + +- (void)destroyEGLImage:(EGLImageKHR)image +{ + if (image == EGL_NO_IMAGE_KHR || _pfnDestroyImageKHR == NULL + || _eglDisplay == EGL_NO_DISPLAY) + return; + _pfnDestroyImageKHR(_eglDisplay, image); +} + +- (void)bindEGLImage:(EGLImageKHR)image toExternalTexture:(unsigned int)texId +{ + typedef void (*TargetTex2DOES)(GLenum, GLeglImageOES); + TargetTex2DOES fn = (TargetTex2DOES)_pfnGLImageTargetTexture2D; + + if (image == EGL_NO_IMAGE_KHR || fn == NULL) + return; + glBindTexture(GL_TEXTURE_EXTERNAL_OES, texId); + fn(GL_TEXTURE_EXTERNAL_OES, (GLeglImageOES)image); +} + +- (id)initWithFormat:(NSOpenGLPixelFormat *)format + shareContext:(NSOpenGLContext *)share +{ + self = [super init]; + + if (!self) + { + return nil; + } + + if (format == nil || [format isKindOfClass:[WaylandGLPixelFormat class]] == NO) + { + NSDebugMLLog(@"OpenGL", @"Invalid pixel format %@", format); + [self release]; + return nil; + } + + _eglDisplay = EGL_NO_DISPLAY; + _eglContext = EGL_NO_CONTEXT; + _eglSurface = EGL_NO_SURFACE; + _eglWindow = NULL; + _glSurface = NULL; + _glSubsurface = NULL; + _window = NULL; + _extensionsLoaded = NO; + _hasDmaBufImport = NO; + _hasDmaBufImportModifiers = NO; + _hasExternalTexture = NO; + _pfnCreateImageKHR = NULL; + _pfnDestroyImageKHR = NULL; + _pfnQueryDmaBufFormats = NULL; + _pfnQueryDmaBufModifiers = NULL; + _pfnGLImageTargetTexture2D = NULL; + /* Default to unthrottled swaps. With vsync=1 eglSwapBuffers blocks on the + compositor frame callback, which starves the main run loop when called + from a timer. Apps that want vsync can set NSOpenGLCPSwapInterval = 1. */ + _swapInterval = 0; + + _pixelFormat = RETAIN(format); + _shareContext = RETAIN(share); + + if (share != nil && [share isKindOfClass:[WaylandGLContext class]]) + { + _eglDisplay = ((WaylandGLContext *)share)->_eglDisplay; + } + + return self; +} + +- (NSOpenGLPixelFormat *)pixelFormat +{ + return _pixelFormat; +} + +- (void)setView:(NSView *)view +{ + if (view == nil) + { + [NSException raise:NSInvalidArgumentException + format:@"setView called with nil"]; + } + + ASSIGN(_view, view); + + if ([self _attachToWindowIfNeeded] == NO) + { + return; + } + + if ([self _ensureDisplayAndContextWithShare:_shareContext] == NO) + { + return; + } + + [self _ensureSurface]; +} + +- (NSView *)view +{ + return _view; +} + +- (void)clearDrawable +{ + if (_window != NULL) + { + _window->usesOpenGL = NO; + } + [self _destroySurface]; +} + +- (void)makeCurrentContext +{ + if (_view == nil) + { + [NSException raise:NSGenericException + format:@"GL Context has no view attached, cannot be made current"]; + } + + if ([self _attachToWindowIfNeeded] == NO) + { + return; + } + + if ([self _ensureDisplayAndContextWithShare:_shareContext] == NO) + { + return; + } + + if ([self _ensureSurface] == NO) + { + return; + } + + if (eglMakeCurrent(_eglDisplay, _eglSurface, _eglSurface, _eglContext) == EGL_FALSE) + { + NSDebugMLLog(@"OpenGL", @"eglMakeCurrent failed"); + return; + } + + if (!_extensionsLoaded) + [self _loadExtensions]; + + currentGLContext = self; +} + +- (void)flushBuffer +{ + if (_eglDisplay == EGL_NO_DISPLAY || _eglSurface == EGL_NO_SURFACE) + { + NSLog(@"WaylandGL: flushBuffer: skipped — no EGL display/surface"); + return; + } + + static NSUInteger _swapCount = 0; + EGLBoolean ok = eglSwapBuffers(_eglDisplay, _eglSurface); + if (++_swapCount <= 5 || _swapCount % 90 == 0) + NSLog(@"WaylandGL: flushBuffer: eglSwapBuffers #%lu result=%d err=0x%x", + (unsigned long)_swapCount, (int)ok, ok ? 0 : eglGetError()); + + if (_window != NULL && _window->wlconfig != NULL) + { + wl_display_flush(_window->wlconfig->display); + } +} + +- (void)update +{ + NSRect viewFrame; + + [self _attachToWindowIfNeeded]; + + if (_eglWindow == NULL || _window == NULL) + { + return; + } + + [self _computeViewGeometry:&viewFrame]; + + wl_egl_window_resize(_eglWindow, + (int)NSWidth(viewFrame), + (int)NSHeight(viewFrame), + 0, + 0); + + if (_glSubsurface != NULL) + { + int newSubX = (int)NSMinX(viewFrame); + int newSubY = (int)NSMinY(viewFrame); + wl_subsurface_set_position(_glSubsurface, newSubX, newSubY); + if (_glSurfaceBinding != NULL) + { + _glSurfaceBinding->offset_x = (float)newSubX; + _glSurfaceBinding->offset_y = (float)newSubY; + } + wl_surface_commit(_window->surface); + wl_display_flush(_window->wlconfig->display); + } +} + +- (void)getValues:(long *)vals forParameter:(NSOpenGLContextParameter)param +{ + if (vals == NULL) + { + return; + } + + switch (param) + { + case NSOpenGLCPSwapInterval: + *vals = _swapInterval; + break; + default: + *vals = 0; + break; + } +} + +- (void)setValues:(const long *)vals forParameter:(NSOpenGLContextParameter)param +{ + if (vals == NULL) + { + return; + } + + if (param == NSOpenGLCPSwapInterval) + { + _swapInterval = (int)*vals; + if (_eglDisplay != EGL_NO_DISPLAY) + { + eglSwapInterval(_eglDisplay, _swapInterval); + } + } +} + +- (void)dealloc +{ + if (currentGLContext == self) + { + [WaylandGLContext clearCurrentContext]; + } + + if (_window != NULL) + { + _window->usesOpenGL = NO; + } + + [self _destroySurface]; + + if (_eglDisplay != EGL_NO_DISPLAY && _eglContext != EGL_NO_CONTEXT) + { + eglDestroyContext(_eglDisplay, _eglContext); + _eglContext = EGL_NO_CONTEXT; + } + + RELEASE(_view); + RELEASE(_shareContext); + RELEASE(_pixelFormat); + + [super dealloc]; +} + +@end diff --git a/Source/wayland/WaylandGLPixelFormat.m b/Source/wayland/WaylandGLPixelFormat.m new file mode 100644 index 00000000..5e59b89c --- /dev/null +++ b/Source/wayland/WaylandGLPixelFormat.m @@ -0,0 +1,250 @@ +/* -*- mode:ObjC -*- + WaylandGLPixelFormat - backend implementation of NSOpenGLPixelFormat + + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of the GNUstep Backend. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; see the file COPYING.LIB. + If not, see or write to the + Free Software Foundation, 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "wayland/WaylandOpenGL.h" + +@implementation WaylandGLPixelFormat + +static BOOL +_isAttributeWithValue(NSOpenGLPixelFormatAttribute attr) +{ + switch (attr) + { + case NSOpenGLPFAAuxBuffers: + case NSOpenGLPFAColorSize: + case NSOpenGLPFAAlphaSize: + case NSOpenGLPFADepthSize: + case NSOpenGLPFAStencilSize: + case NSOpenGLPFAAccumSize: + case NSOpenGLPFARendererID: + case NSOpenGLPFAScreenMask: + case NSOpenGLPFASamples: + case NSOpenGLPFAAuxDepthStencil: + case NSOpenGLPFASampleBuffers: + return YES; + default: + return NO; + } +} + +- (id)initWithAttributes:(NSOpenGLPixelFormatAttribute *)attribs +{ + NSOpenGLPixelFormatAttribute *ptr; + + self = [super init]; + if (self == nil) + { + return nil; + } + + if (attribs == NULL) + { + _attributeCount = 1; + _attributes = NSZoneMalloc(NSDefaultMallocZone(), + sizeof(NSOpenGLPixelFormatAttribute)); + _attributes[0] = (NSOpenGLPixelFormatAttribute)0; + return self; + } + + _attributeCount = 1; + for (ptr = attribs; *ptr != 0; ptr++) + { + _attributeCount++; + if (_isAttributeWithValue(*ptr)) + { + if (*(ptr + 1) != 0) + { + ptr++; + _attributeCount++; + } + } + } + + _attributes = NSZoneMalloc(NSDefaultMallocZone(), + _attributeCount * sizeof(NSOpenGLPixelFormatAttribute)); + memcpy(_attributes, attribs, + _attributeCount * sizeof(NSOpenGLPixelFormatAttribute)); + + return self; +} + +- (EGLConfig)eglConfigForDisplay:(EGLDisplay)eglDisplay +{ + EGLint redSize = 8; + EGLint greenSize = 8; + EGLint blueSize = 8; + EGLint alphaSize = 8; + EGLint depthSize = 24; + EGLint stencilSize = 8; + EGLint sampleBuffers = 0; + EGLint samples = 0; + /* Match the renderable type to the API that was bound with eglBindAPI() + before this call. Requesting both EGL_OPENGL_BIT|EGL_OPENGL_ES2_BIT + requires a single config to support both APIs simultaneously, which + most drivers do not provide, causing eglChooseConfig to return 0 + configs and the context creation to fail. */ + EGLint renderableType; + switch (eglQueryAPI()) + { + case EGL_OPENGL_ES_API: + renderableType = EGL_OPENGL_ES2_BIT; + break; + case EGL_OPENGL_API: + default: + renderableType = EGL_OPENGL_BIT; + break; + } + EGLConfig config = NULL; + EGLint configCount = 0; + NSUInteger i; + + if (_attributes != NULL) + { + for (i = 0; i < _attributeCount; i++) + { + NSOpenGLPixelFormatAttribute attr = _attributes[i]; + if (_isAttributeWithValue(attr) == NO) + { + continue; + } + + if (i + 1 >= _attributeCount) + { + break; + } + + switch (attr) + { + case NSOpenGLPFAColorSize: + redSize = greenSize = blueSize = ((EGLint)_attributes[i + 1] / 3); + if (redSize < 1) + { + redSize = greenSize = blueSize = 1; + } + break; + case NSOpenGLPFAAlphaSize: + alphaSize = _attributes[i + 1]; + break; + case NSOpenGLPFADepthSize: + depthSize = _attributes[i + 1]; + break; + case NSOpenGLPFAStencilSize: + stencilSize = _attributes[i + 1]; + break; + case NSOpenGLPFASampleBuffers: + sampleBuffers = _attributes[i + 1]; + break; + case NSOpenGLPFASamples: + samples = _attributes[i + 1]; + break; + default: + break; + } + + i++; + } + } + + { + EGLint attrs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, renderableType, + EGL_RED_SIZE, redSize, + EGL_GREEN_SIZE, greenSize, + EGL_BLUE_SIZE, blueSize, + EGL_ALPHA_SIZE, alphaSize, + EGL_DEPTH_SIZE, depthSize, + EGL_STENCIL_SIZE, stencilSize, + EGL_SAMPLE_BUFFERS, sampleBuffers, + EGL_SAMPLES, samples, + EGL_NONE + }; + + if (eglChooseConfig(eglDisplay, attrs, &config, 1, &configCount) == EGL_FALSE + || configCount == 0) + { + NSDebugMLLog(@"OpenGL", @"No EGL config matched requested NSOpenGL attributes"); + return NULL; + } + } + + return config; +} + +- (void)getValues:(int *)vals + forAttribute:(NSOpenGLPixelFormatAttribute)attrib + forVirtualScreen:(int)screen +{ + NSUInteger i; + + (void)screen; + + if (vals == NULL) + { + return; + } + + *vals = 0; + if (_attributes == NULL) + { + return; + } + + for (i = 0; i + 1 < _attributeCount; i++) + { + if (_attributes[i] == attrib) + { + if (_isAttributeWithValue(attrib)) + { + *vals = _attributes[i + 1]; + } + else + { + *vals = 1; + } + return; + } + } +} + +- (void)dealloc +{ + if (_attributes != NULL) + { + NSZoneFree(NSDefaultMallocZone(), _attributes); + _attributes = NULL; + } + + [super dealloc]; +} + +@end diff --git a/Source/wayland/WaylandInputServer.m b/Source/wayland/WaylandInputServer.m new file mode 100644 index 00000000..dc4e81de --- /dev/null +++ b/Source/wayland/WaylandInputServer.m @@ -0,0 +1,286 @@ +/* WaylandInputServer - Input method / preedit support for Wayland backend + + Copyright (C) 2024 Free Software Foundation, Inc. + + This file is part of the GNUstep Backend. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; see the file COPYING.LIB. + If not, see or write to the + Free Software Foundation, 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wayland/WaylandInputServer.h" + +/* Commit pending text_input state to the compositor. */ +static void +commit_text_input(WaylandConfig *wlconfig) +{ + if (wlconfig && wlconfig->text_input) + { + zwp_text_input_v3_commit(wlconfig->text_input); + wl_display_flush(wlconfig->display); + } +} + + +@implementation WaylandInputServer + +- (id) initWithDelegate: (id)aDelegate name: (NSString *)name +{ + delegate = aDelegate; + ASSIGN(server_name, name); + focused_window_id = 0; + wlconfig = NULL; + NSDebugMLLog(@"WaylandIME", @"WaylandInputServer: initialized"); + return self; +} + +- (void) dealloc +{ + DESTROY(server_name); + [super dealloc]; +} + +- (void) setWlconfig: (WaylandConfig *)config +{ + wlconfig = config; +} + +- (void) setFocusedWindowId: (int)windowId +{ + focused_window_id = windowId; + NSDebugMLLog(@"WaylandIME", @"WaylandInputServer: focused window id = %d", windowId); +} + +- (int) focusedWindowId +{ + return focused_window_id; +} + + +/* NSInputServiceProvider protocol */ + +- (void) activeConversationChanged: (id)sender + toNewConversation: (long)newConversation +{ + [super activeConversationChanged: sender toNewConversation: newConversation]; + + if ([sender respondsToSelector: @selector(window)] == NO) + return; + + NSWindow *window = [sender performSelector: @selector(window)]; + if (window != nil) + { + focused_window_id = [window windowNumber]; + NSDebugMLLog(@"WaylandIME", + @"WaylandInputServer: conversation changed, focused window = %d", + focused_window_id); + } +} + +- (void) activeConversationWillChange: (id)sender + fromOldConversation: (long)oldConversation +{ + [super activeConversationWillChange: sender + fromOldConversation: oldConversation]; +} + +@end + + +@implementation WaylandInputServer (InputMethod) + +- (NSString *) inputMethodStyle +{ + /* When text_input_v3 is available the preedit is delivered inline via + * setMarkedText:selectedRange: on the focused responder; no separate IM + * window is needed. Return nil so AppKit does not try to manage an IM + * panel on our behalf. */ + return nil; +} + +- (NSString *) fontSize: (int *)size +{ + NSString *str = [[NSUserDefaults standardUserDefaults] + stringForKey: @"NSFontSize"]; + if (!str) + str = @"12"; + if (size) + *size = (int) strtol([str cString], NULL, 10); + return str; +} + +- (BOOL) clientWindowRect: (NSRect *)rect +{ + if (!rect || focused_window_id == 0) + return NO; + NSWindow *window = GSWindowWithNumber(focused_window_id); + if (window == nil) + return NO; + *rect = [window frame]; + return YES; +} + +/* ── IME geometry: status area ───────────────────────────────────────────── + * + * The status area is where the IM draws its mode indicator (e.g. "あ" for + * hiragana mode). We report the bottom-left corner of the focused window + * as a reasonable default; a real status bar is not rendered in the backend. + */ +- (BOOL) statusArea: (NSRect *)rect +{ + if (!rect) + return NO; + + if (focused_window_id != 0) + { + NSWindow *window = GSWindowWithNumber(focused_window_id); + if (window) + { + NSRect frame = [window frame]; + *rect = NSMakeRect(frame.origin.x, + frame.origin.y, + frame.size.width, + 20.0); + return YES; + } + } + + /* Fallback: bottom-left of screen. */ + NSRect screen = [[NSScreen mainScreen] frame]; + *rect = NSMakeRect(screen.origin.x, screen.origin.y, screen.size.width, 20.0); + return YES; +} + +/* ── IME geometry: preedit area ───────────────────────────────────────────── + * + * The preedit area covers the region where candidate text is displayed. + * We return the stored rect if we have one, otherwise the client window rect. + */ +- (BOOL) preeditArea: (NSRect *)rect +{ + if (!rect) + return NO; + + if (wlconfig && !NSIsEmptyRect(wlconfig->ime_preedit_rect)) + { + *rect = wlconfig->ime_preedit_rect; + return YES; + } + + return [self clientWindowRect: rect]; +} + +/* ── IME geometry: preedit spot ───────────────────────────────────────────── + * + * The preedit spot is the screen coordinate of the text-insertion cursor. + * The compositor IM uses this to position its candidate window. + */ +- (BOOL) preeditSpot: (NSPoint *)p +{ + if (!p) + return NO; + + if (wlconfig && wlconfig->text_input_active) + { + *p = wlconfig->ime_preedit_spot; + return YES; + } + + /* Fallback: centre of the focused window. */ + if (focused_window_id != 0) + { + NSWindow *window = GSWindowWithNumber(focused_window_id); + if (window) + { + NSRect frame = [window frame]; + *p = NSMakePoint(NSMidX(frame), NSMidY(frame)); + return YES; + } + } + + return NO; +} + +/* ── IME geometry setters ─────────────────────────────────────────────────── + * + * AppKit calls these when the text cursor moves. We store the values and + * forward them to the compositor via set_cursor_rectangle so the IM can + * reposition its candidate window. + */ +- (BOOL) setStatusArea: (NSRect *)rect +{ + /* Status area is compositor-managed; acknowledge but don't act. */ + return YES; +} + +- (BOOL) setPreeditArea: (NSRect *)rect +{ + if (!rect || !wlconfig) + return NO; + + wlconfig->ime_preedit_rect = *rect; + + if (wlconfig->text_input && wlconfig->text_input_active) + { + zwp_text_input_v3_set_cursor_rectangle( + wlconfig->text_input, + (int32_t) rect->origin.x, + (int32_t) rect->origin.y, + (int32_t) rect->size.width, + (int32_t) rect->size.height); + commit_text_input(wlconfig); + } + + return YES; +} + +- (BOOL) setPreeditSpot: (NSPoint *)p +{ + if (!p || !wlconfig) + return NO; + + wlconfig->ime_preedit_spot = *p; + + /* Forward cursor position as a 1×(line-height) rectangle. */ + if (wlconfig->text_input && wlconfig->text_input_active) + { + int32_t lineHeight = 16; /* sensible default; AppKit can update via setPreeditArea: */ + zwp_text_input_v3_set_cursor_rectangle( + wlconfig->text_input, + (int32_t) p->x, + (int32_t) p->y, + 0, + lineHeight); + commit_text_input(wlconfig); + NSDebugMLLog(@"WaylandIME", + @"WaylandInputServer: preedit spot → (%g,%g)", p->x, p->y); + } + + return YES; +} + +@end diff --git a/Source/wayland/WaylandServer+Cursor.m b/Source/wayland/WaylandServer+Cursor.m index 7c9cf14a..4e8113be 100644 --- a/Source/wayland/WaylandServer+Cursor.m +++ b/Source/wayland/WaylandServer+Cursor.m @@ -27,6 +27,7 @@ #include "wayland/WaylandServer.h" #include "cairo/WaylandCairoShmSurface.h" +#include #import #import #import @@ -52,14 +53,53 @@ { WaylandConfig *wlconfig = data; - struct window *window = wl_surface_get_user_data(surface); + struct window *window = surface_get_window(surface); - if (window->ignoreMouse) + if (window == NULL || window->ignoreMouse) { return; } - wlconfig->pointer.focus = window; + float ox, oy; + surface_get_offset(surface, &ox, &oy); + wlconfig->pointer.focus = window; + wlconfig->pointer.focus_offset_x = ox; + wlconfig->pointer.focus_offset_y = oy; + + float sx = wl_fixed_to_double(sx_w) + ox; + float sy = wl_fixed_to_double(sy_w) + oy; + + /* Track cursor global (output-relative) position so we can infer where + * xdg_toplevel windows actually are on screen. + * + * Layer-shell surfaces have a known global position (we set the margins + * ourselves), so entering one gives us a ground-truth fix. When the + * cursor then enters an xdg_toplevel we subtract the surface-local entry + * point to infer that toplevel's global origin and store it in + * saved_pos_x/y. Both values are in Wayland output coordinates + * (Y increasing downward from the output's top-left corner). */ + if (window->layer_surface) + { + wlconfig->cursor_global_x = window->pos_x + sx; + wlconfig->cursor_global_y = window->pos_y + sy; + wlconfig->cursor_global_valid = YES; + } + else if (window->toplevel) + { + if (wlconfig->cursor_global_valid) + { + window->saved_pos_x = wlconfig->cursor_global_x - sx; + window->saved_pos_y = wlconfig->cursor_global_y - sy; + window->global_pos_known = YES; + } + /* Keep cursor_global current while traversing toplevels. */ + if (window->global_pos_known) + { + wlconfig->cursor_global_x = window->saved_pos_x + sx; + wlconfig->cursor_global_y = window->saved_pos_y + sy; + wlconfig->cursor_global_valid = YES; + } + } if (wlconfig->pointer.captured) { @@ -68,11 +108,8 @@ [(WaylandServer *)GSCurrentServer() initializeMouseIfRequired]; - - NSDebugLog(@"[%d] pointer_handle_enter",window->window_id); - - float sx = wl_fixed_to_double(sx_w); - float sy = wl_fixed_to_double(sy_w); + NSDebugFLLog(@"WaylandPointer", @"[%d] pointer_handle_enter sx=%g sy=%g", + window->window_id, sx, sy); if (window && wlconfig->pointer.serial) @@ -116,9 +153,9 @@ { WaylandConfig *wlconfig = data; - struct window *window = wl_surface_get_user_data(surface); + struct window *window = surface_get_window(surface); - if (window->ignoreMouse) + if (window == NULL || window->ignoreMouse) { return; } @@ -159,8 +196,10 @@ [GSCurrentServer() postEvent:event atStart:NO]; } - wlconfig->pointer.focus = NULL; - wlconfig->pointer.serial = serial; + wlconfig->pointer.focus = NULL; + wlconfig->pointer.focus_offset_x = 0.0f; + wlconfig->pointer.focus_offset_y = 0.0f; + wlconfig->pointer.serial = serial; wlconfig->event_serial = serial; } } @@ -181,11 +220,32 @@ { return; } - float sx = wl_fixed_to_double(sx_w); - float sy = wl_fixed_to_double(sy_w); + float sx = wl_fixed_to_double(sx_w) + wlconfig->pointer.focus_offset_x; + float sy = wl_fixed_to_double(sy_w) + wlconfig->pointer.focus_offset_y; wlconfig->pointer.last_timestamp = (NSTimeInterval) time / 1000.0; + /* Keep cursor_global current on every motion so that the position is + * fresh at the moment a context menu is about to be opened. Use + * pointer.focus (the actual Wayland surface receiving events) not the + * potentially-captured focused_window. */ + { + struct window *tw = wlconfig->pointer.focus; + if (tw) + { + if (tw->layer_surface) + { + wlconfig->cursor_global_x = tw->pos_x + sx; + wlconfig->cursor_global_y = tw->pos_y + sy; + wlconfig->cursor_global_valid = YES; + } + else if (tw->global_pos_known) + { + wlconfig->cursor_global_x = tw->saved_pos_x + sx; + wlconfig->cursor_global_y = tw->saved_pos_y + sy; + } + } + } [(WaylandServer *)GSCurrentServer() initializeMouseIfRequired]; @@ -209,7 +269,6 @@ if (wlconfig->pointer.button_state == WL_POINTER_BUTTON_STATE_PRESSED) { - switch (wlconfig->pointer.button) { case BTN_LEFT: @@ -218,7 +277,7 @@ case BTN_RIGHT: eventType = NSRightMouseDragged; break; - case BTN_MIDDLE: + default: /* BTN_MIDDLE, BTN_SIDE, BTN_EXTRA, BTN_FORWARD, BTN_BACK, … */ eventType = NSOtherMouseDragged; break; } @@ -285,49 +344,41 @@ { wlconfig->pointer.button = button; if (window->toplevel) - { - // if the window is a toplevel we check if the event is for resizing or - // moving the window these actions are delegated to the compositor and - // therefore we skip forwarding the events to the NSWindow / NSView - - NSWindow *nswindow = GSWindowWithNumber(window->window_id); - if (nswindow != nil) - { - GSStandardWindowDecorationView * wd = [nswindow _windowView]; - - if ([wd pointInTitleBarRect:eventLocation]) - { - xdg_toplevel_move(window->toplevel, wlconfig->seat, serial); - window->moving = YES; - return; - } - if ([wd pointInResizeBarRect:eventLocation]) - { - GSResizeEdgeMode mode = [wd resizeModeForPoint:eventLocation]; - - uint32_t edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; - - if (mode == GSResizeEdgeBottomLeftMode) - { - edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; - } - else if (mode == GSResizeEdgeBottomRightMode) - { - edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; - } - else if (mode == GSResizeEdgeBottomMode) - { - edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; - } - - xdg_toplevel_resize(window->toplevel, wlconfig->seat, serial, - edges); - window->resizing = YES; - return; - } - } // endif nswindow != nil - } // endif window->toplevel - + { + NSWindow *nswindow = GSWindowWithNumber(window->window_id); + + if (nswindow != nil + && ![(WaylandServer *)GSCurrentServer() handlesWindowDecorations]) + { + GSStandardWindowDecorationView *wd = [nswindow _windowView]; + + if ([wd pointInTitleBarRect:eventLocation]) + { + xdg_toplevel_move(window->toplevel, wlconfig->seat, serial); + window->moving = YES; + return; + } + + if ([wd pointInResizeBarRect:eventLocation]) + { + GSResizeEdgeMode mode = [wd resizeModeForPoint:eventLocation]; + + uint32_t edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; + + if (mode == GSResizeEdgeBottomLeftMode) + edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; + else if (mode == GSResizeEdgeBottomRightMode) + edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; + else if (mode == GSResizeEdgeBottomMode) + edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; + + xdg_toplevel_resize(window->toplevel, wlconfig->seat, serial, + edges); + window->resizing = YES; + return; + } + } + } if (button == wlconfig->pointer.last_click_button && time - wlconfig->pointer.last_click_time < DOUBLECLICK_DELAY && fabsf(wlconfig->pointer.x - wlconfig->pointer.last_click_x) @@ -354,13 +405,14 @@ case BTN_RIGHT: eventType = NSRightMouseDown; break; - case BTN_MIDDLE: + default: + /* BTN_MIDDLE (2), BTN_SIDE (3), BTN_EXTRA (4), + BTN_FORWARD (5), BTN_BACK (6), BTN_TASK (7), … */ eventType = NSOtherMouseDown; + NSDebugFLLog(@"WaylandPointer", + @"pointer_handle_button: button=0x%x (btn%u) pressed", + button, button - BTN_LEFT); break; - // TODO: handle BTN_SIDE, BTN_EXTRA, BTN_FORWARD, BTN_BACK and other - // constants in libinput. - // We may just want to send NSOtherMouseDown and populate buttonNumber - // with the libinput constant? } } else if (state == WL_POINTER_BUTTON_STATE_RELEASED) @@ -385,8 +437,11 @@ case BTN_RIGHT: eventType = NSRightMouseUp; break; - case BTN_MIDDLE: + default: eventType = NSOtherMouseUp; + NSDebugFLLog(@"WaylandPointer", + @"pointer_handle_button: button=0x%x (btn%u) released", + button, button - BTN_LEFT); break; } } @@ -394,20 +449,22 @@ { return; } - /* FIXME: unlike in _motion and _axis handlers, the argument used in _button - is the "serial" of the event, not passed and unavailable in _motion and - _axis handlers. Is it allowed to pass "serial" as the eventNumber: in - _button handler, but "time" as the eventNumber: in the _motion and _axis - handlers? */ + /* eventNumber: use the Wayland serial (a monotonically increasing counter + from the compositor) — it is consistent with what the button handlers + receive and matches what the pointer-enter handler uses for serial. */ tick = serial; - /* FIXME: X11 backend uses the XGetPointerMapping()-returned values from - its map_return argument as constants for buttonNumber. As the variant - with buttonNumber: seems to be a GNUstep extension, and the value - internal, it might be ok to just provide libinput constant as we're doing - here. If this is truly correct, please update this comment to document - the correctness of doing so. */ - buttonNumber = button; + /* buttonNumber: map evdev button codes to a 0-based AppKit button index. + * BTN_LEFT (0x110) → 0 (NSLeftMouse*) + * BTN_RIGHT (0x111) → 1 (NSRightMouse*) + * BTN_MIDDLE (0x112) → 2 (NSOtherMouse*) + * BTN_SIDE (0x113) → 3 (NSOtherMouse* — browser back) + * BTN_EXTRA (0x114) → 4 (NSOtherMouse* — browser forward) + * BTN_FORWARD(0x115) → 5 + * BTN_BACK (0x116) → 6 + * This is equivalent to the X11 approach of subtracting the base button + * code, and produces stable values across different mouse hardware. */ + buttonNumber = (int)(button - BTN_LEFT); event = [NSEvent mouseEventWithType:eventType location:eventLocation @@ -433,117 +490,200 @@ } -// Discrete step information for scroll and other axes. +/* Accumulate axis delta for this logical frame. + * The event is dispatched as a single NSScrollWheel in pointer_handle_frame. + * This avoids two separate events when a compositor sends both vertical and + * horizontal components in the same frame (common for diagonal touchpad swipes). */ static void -pointer_handle_frame(void *data, struct wl_pointer *pointer) -{} +pointer_handle_axis(void *data, struct wl_pointer *pointer, uint32_t time, + uint32_t axis, wl_fixed_t value) +{ + WaylandConfig *wlconfig = data; + struct window *window = wlconfig->pointer.focus; + + if (window == NULL || window->ignoreMouse) + return; + + float delta = wl_fixed_to_double(value) * wlconfig->mouse_scroll_multiplier; + + switch (axis) + { + case WL_POINTER_AXIS_VERTICAL_SCROLL: + wlconfig->pointer.frame_deltaY += delta; + break; + case WL_POINTER_AXIS_HORIZONTAL_SCROLL: + wlconfig->pointer.frame_deltaX += delta; + break; + default: + return; + } -// Source information for scroll and other axes. + wlconfig->pointer.frame_has_axis = YES; + wlconfig->pointer.frame_time = time; + + NSDebugFLLog(@"WaylandScroll", + @"pointer_handle_axis: axis=%u delta=%g (source=%u)", + axis, delta, wlconfig->pointer.axis_source); +} + +/* Store the axis source for the current frame (wheel, finger, continuous…). */ static void pointer_handle_axis_source(void *data, struct wl_pointer *pointer, uint32_t axis_source) { WaylandConfig *wlconfig = data; wlconfig->pointer.axis_source = axis_source; + NSDebugFLLog(@"WaylandScroll", @"pointer_handle_axis_source: source=%u", axis_source); } -// Stop notification for scroll and other axes. +/* Emit a zero-delta NSScrollWheel to signal that a touchpad gesture ended. + * AppKit uses this to stop inertial scrolling. */ static void pointer_handle_axis_stop(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis) -{} +{ + WaylandConfig *wlconfig = data; + NSDebugFLLog(@"WaylandScroll", @"pointer_handle_axis_stop: axis=%u", axis); + + struct window *window = wlconfig->pointer.focus; + if (window == NULL || window->ignoreMouse) + return; + + [(WaylandServer *)GSCurrentServer() initializeMouseIfRequired]; + + NSPoint loc = NSMakePoint(wlconfig->pointer.x, window->height - wlconfig->pointer.y); + NSEvent *ev = [NSEvent mouseEventWithType:NSScrollWheel + location:loc + modifierFlags:wlconfig->modifiers + timestamp:(NSTimeInterval)time / 1000.0 + windowNumber:(int)window->window_id + context:GSCurrentContext() + eventNumber:time + clickCount:0 + pressure:0.0 + buttonNumber:0 + deltaX:0.0 + deltaY:0.0 + deltaZ:0.0]; + [GSCurrentServer() postEvent:ev atStart:NO]; +} -// Discrete step information for scroll and other axes. +/* Accumulate the integer scroll-wheel step count for the current frame. + * Discrete steps are reported alongside the smooth axis value and allow + * applications to snap to whole-line increments. */ static void pointer_handle_axis_discrete(void *data, struct wl_pointer *pointer, uint32_t axis, int discrete) -{} - -// Scroll and other axis notifications. -static void -pointer_handle_axis(void *data, struct wl_pointer *pointer, uint32_t time, - uint32_t axis, wl_fixed_t value) { - WaylandConfig *wlconfig = data; - NSEvent *event; - NSEventType eventType; - NSPoint eventLocation; - NSGraphicsContext *gcontext; - unsigned int eventFlags; - float deltaX = 0.0; - float deltaY = 0.0; - int clickCount = 1; - int buttonNumber; - - struct window *window = wlconfig->pointer.focus; - if (window->ignoreMouse) + WaylandConfig *wlconfig = data; + NSDebugFLLog(@"WaylandScroll", + @"pointer_handle_axis_discrete: axis=%u discrete=%d", axis, discrete); + switch (axis) { - return; + case WL_POINTER_AXIS_VERTICAL_SCROLL: + wlconfig->pointer.frame_discrete_y += discrete; + break; + case WL_POINTER_AXIS_HORIZONTAL_SCROLL: + wlconfig->pointer.frame_discrete_x += discrete; + break; } +} - [(WaylandServer *)GSCurrentServer() initializeMouseIfRequired]; +/* Dispatch the accumulated scroll deltas as a single NSScrollWheel event, + * then reset the per-frame state. Grouping axis events per frame produces + * smoother diagonal scrolling and avoids redundant event dispatch. */ +static void +pointer_handle_frame(void *data, struct wl_pointer *pointer) +{ + WaylandConfig *wlconfig = data; - gcontext = GSCurrentContext(); - eventLocation - = NSMakePoint(wlconfig->pointer.x, window->height - wlconfig->pointer.y); - eventFlags = wlconfig->modifiers; + if (!wlconfig->pointer.frame_has_axis) + return; + + struct window *window = wlconfig->pointer.focus; + if (window == NULL || window->ignoreMouse) + goto reset; - if (wlconfig->pointer.axis_source != WL_POINTER_AXIS_SOURCE_WHEEL) { - //axis_source == WL POINTER AXIS SOURCE FINGER - //axis_source == WL POINTER AXIS SOURCE CONTINUOUS - // XXX the scroll is from trackpad we should calculate - // the momentumPhase - NSDebugLog(@"touch scroll"); + [(WaylandServer *)GSCurrentServer() initializeMouseIfRequired]; + + NSPoint loc = NSMakePoint(wlconfig->pointer.x, + window->height - wlconfig->pointer.y); + NSTimeInterval ts = (NSTimeInterval)wlconfig->pointer.frame_time / 1000.0; + + NSDebugFLLog(@"WaylandScroll", + @"pointer_handle_frame: dx=%g dy=%g disc_x=%d disc_y=%d src=%u", + wlconfig->pointer.frame_deltaX, wlconfig->pointer.frame_deltaY, + wlconfig->pointer.frame_discrete_x, wlconfig->pointer.frame_discrete_y, + wlconfig->pointer.axis_source); + + NSEvent *ev = [NSEvent mouseEventWithType:NSScrollWheel + location:loc + modifierFlags:wlconfig->modifiers + timestamp:ts + windowNumber:(int)window->window_id + context:GSCurrentContext() + eventNumber:wlconfig->pointer.frame_time + clickCount:0 + pressure:0.0 + buttonNumber:0 + deltaX:wlconfig->pointer.frame_deltaX + deltaY:wlconfig->pointer.frame_deltaY + deltaZ:0.0]; + [GSCurrentServer() postEvent:ev atStart:NO]; } - //float mouse_scroll_multiplier = wlconfig->mouse_scroll_multiplier; - /* For smooth-scroll events, we're not doing any cross-event or delta - calculations, as is done in button event handling. */ +reset: + wlconfig->pointer.frame_has_axis = NO; + wlconfig->pointer.frame_deltaX = 0.0f; + wlconfig->pointer.frame_deltaY = 0.0f; + wlconfig->pointer.frame_discrete_x = 0; + wlconfig->pointer.frame_discrete_y = 0; + wlconfig->pointer.frame_time = 0; +} + +/* wl_pointer.axis_value120 (protocol v8) — high-resolution wheel step. + * Accumulate into the per-frame scroll state using the standard 1/120 scale. */ +static void +pointer_handle_axis_value120(void *data, struct wl_pointer *pointer, + uint32_t axis, int32_t value120) +{ + WaylandConfig *wlconfig = data; + NSDebugFLLog(@"WaylandScroll", + @"pointer_handle_axis_value120: axis=%u value120=%d", axis, value120); + float delta = ((float)value120 / 120.0f) * wlconfig->mouse_scroll_multiplier; switch (axis) { - case WL_POINTER_AXIS_VERTICAL_SCROLL: - eventType = NSScrollWheel; - deltaY = wl_fixed_to_double(value) * wlconfig->mouse_scroll_multiplier; - break; - case WL_POINTER_AXIS_HORIZONTAL_SCROLL: - eventType = NSScrollWheel; - deltaX = wl_fixed_to_double(value) * wlconfig->mouse_scroll_multiplier; - break; + case WL_POINTER_AXIS_VERTICAL_SCROLL: + wlconfig->pointer.frame_deltaY += delta; + wlconfig->pointer.frame_discrete_y += (value120 / 120); + break; + case WL_POINTER_AXIS_HORIZONTAL_SCROLL: + wlconfig->pointer.frame_deltaX += delta; + wlconfig->pointer.frame_discrete_x += (value120 / 120); + break; } + wlconfig->pointer.frame_has_axis = YES; +} - /* FIXME: X11 backend uses the XGetPointerMapping()-returned values from - its map_return argument as constants for buttonNumber. As the variant - with buttonNumber: seems to be a GNUstep extension, and the value - internal, it might be ok to just not provide any value here. - If this is truly correct, please update this comment to document - the correctness of doing so. */ - buttonNumber = 0; - - event = [NSEvent mouseEventWithType:eventType - location:eventLocation - modifierFlags:eventFlags - timestamp:(NSTimeInterval) time / 1000.0 - windowNumber:(int) window->window_id - context:gcontext - eventNumber:time - clickCount:clickCount - pressure:1.0 - buttonNumber:buttonNumber - deltaX:deltaX - deltaY:deltaY - deltaZ:0.]; - - [GSCurrentServer() postEvent:event atStart:NO]; +/* wl_pointer.axis_relative_direction (protocol v9) — natural-scroll hint. + * Logged for traceability; direction inversion can be applied here later. */ +static void +pointer_handle_axis_relative_direction(void *data, struct wl_pointer *pointer, + uint32_t axis, uint32_t direction) +{ + NSDebugFLLog(@"WaylandScroll", + @"pointer_handle_axis_relative_direction: axis=%u direction=%u", + axis, direction); } -// the Seat category uses this listener const struct wl_pointer_listener pointer_listener - = {pointer_handle_enter, pointer_handle_leave, - pointer_handle_motion, pointer_handle_button, - pointer_handle_axis, pointer_handle_frame, - pointer_handle_axis_source, pointer_handle_axis_stop, - pointer_handle_axis_discrete}; + = {pointer_handle_enter, pointer_handle_leave, + pointer_handle_motion, pointer_handle_button, + pointer_handle_axis, pointer_handle_frame, + pointer_handle_axis_source, pointer_handle_axis_stop, + pointer_handle_axis_discrete, pointer_handle_axis_value120, + pointer_handle_axis_relative_direction}; @implementation WaylandServer (Cursor) @@ -613,7 +753,7 @@ - (void)releasemouse - (void)setMouseLocation:(NSPoint)mouseLocation onScreen:(int)aScreen { - NSDebugLog(@"setMouseLocation: not supported"); + NSDebugMLLog(@"WaylandPointer", @"setMouseLocation: not supported on Wayland"); } - (void)hidecursor @@ -694,8 +834,8 @@ - (void)standardcursor:(int)style :(void **)cid } if (strlen(cursor_name) != 0) { - NSDebugLog(@"load cursor from theme for style %d: %s", style, - cursor_name); + NSDebugMLLog(@"WaylandPointer", @"load cursor from theme for style %d: %s", style, + cursor_name); struct cursor *wayland_cursor = calloc(1, sizeof(struct cursor)); wayland_cursor->cursor @@ -709,7 +849,7 @@ - (void)standardcursor:(int)style :(void **)cid } else { - NSDebugLog(@"unable to load cursor from theme for style %d", style); + NSDebugMLLog(@"WaylandPointer", @"unable to load cursor from theme for style %d", style); } } @@ -727,9 +867,9 @@ - (void)imagecursor:(NSPoint)hotp :(NSImage *)image :(void **)cid // TODO should check if the bitmaprep format is compatible memcpy(pbuffer->data, data, [raw_img bytesPerPlane]); - struct cursor * wayland_cursor = malloc(sizeof(struct cursor)); + struct cursor *wayland_cursor = calloc(1, sizeof(struct cursor)); - struct wl_cursor * cursor = malloc(sizeof(struct wl_cursor)); + struct wl_cursor *cursor = calloc(1, sizeof(struct wl_cursor)); cursor->image_count = 1; cursor->name = "custom"; struct wl_cursor_image * cursor_image = malloc(sizeof(struct wl_cursor_image)); @@ -755,8 +895,8 @@ - (void)setcursorcolor:(NSColor *)fg :(NSColor *)bg :(void *)cid - (void) recolorcursor:(NSColor *)fg :(NSColor *)bg :(void*) cid { - // TODO recolorcursor - NSDebugLog(@"recolorcursor"); + /* TODO: implement cursor recolouring on Wayland */ + NSDebugMLLog(@"WaylandPointer", @"recolorcursor: not yet implemented"); } - (void)setcursor:(void *)cid @@ -779,10 +919,6 @@ - (void)setcursor:(void *)cid return; } - if (wayland_cursor->surface) - { - wl_surface_destroy(wayland_cursor->surface); - } wl_pointer_set_cursor(wlconfig->pointer.wlpointer, wlconfig->event_serial, wlconfig->cursor_surface, wayland_cursor->image->hotspot_x, diff --git a/Source/wayland/WaylandServer+Keyboard.m b/Source/wayland/WaylandServer+Keyboard.m index c6132a67..5e36197c 100644 --- a/Source/wayland/WaylandServer+Keyboard.m +++ b/Source/wayland/WaylandServer+Keyboard.m @@ -1,5 +1,5 @@ -/* - WaylandServer - Keyboard Handling +/* + WaylandServer - Keyboard Handling + zwp_text_input_v3 (IME/preedit) Copyright (C) 2020 Free Software Foundation, Inc. @@ -26,18 +26,34 @@ */ #include "wayland/WaylandServer.h" +#include + +/* Informal protocol for marked (preedit) text — implemented by NSTextView. */ +@interface NSObject (WaylandMarkedText) +- (void) setMarkedText: (id)aString selectedRange: (NSRange)selRange; +- (void) unmarkText; +- (void) insertText: (id)aString; +@end +#include +#include #include #include -#include +#include #include +#include +#include #include +#include +#include #include + +/* ── wl_keyboard listener ─────────────────────────────────────────────────── */ + static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size) { - NSDebugLog(@"keyboard_handle_keymap"); WaylandConfig *wlconfig = data; struct xkb_keymap *keymap; struct xkb_state *state; @@ -93,6 +109,8 @@ wlconfig->xkb.keymap = keymap; wlconfig->xkb.state = state; + NSDebugFLLog(@"WaylandIME", @"keyboard_handle_keymap: XKB keymap loaded (size=%u)", size); + wlconfig->xkb.control_mask = 1 << xkb_keymap_mod_get_index(wlconfig->xkb.keymap, "Control"); wlconfig->xkb.alt_mask @@ -105,18 +123,90 @@ keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { - // NSDebugLog(@"keyboard_handle_enter"); - WaylandConfig *wlconfig = data; + WaylandConfig *wlconfig = data; wlconfig->event_serial = serial; + NSDebugFLLog(@"WaylandIME", @"keyboard_handle_enter: serial=%u", serial); + + if (!surface) + return; + struct window *window = surface_get_window(surface); + if (!window) + return; + + wlconfig->keyboard_focus = window; + + NSWindow *nswindow = GSWindowWithNumber(window->window_id); + if (!nswindow) + return; + + NSEvent *ev = [NSEvent otherEventWithType:NSAppKitDefined + location:NSZeroPoint + modifierFlags:0 + timestamp:0 + windowNumber:window->window_id + context:GSCurrentContext() + subtype:GSAppKitWindowFocusIn + data1:0 + data2:0]; + [nswindow sendEvent:ev]; + + /* Enable text input so the compositor IM can send preedit/commit events. */ + if (wlconfig->text_input) + { + zwp_text_input_v3_enable(wlconfig->text_input); + zwp_text_input_v3_commit(wlconfig->text_input); + } } static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface) { - WaylandConfig *wlconfig = data; + WaylandConfig *wlconfig = data; wlconfig->event_serial = serial; - // NSDebugLog(@"keyboard_handle_leave"); + NSDebugFLLog(@"WaylandIME", @"keyboard_handle_leave: serial=%u", serial); + + if (!wlconfig->keyboard_focus) + { + /* Disable text input even if we lost track of focus window. */ + if (wlconfig->text_input) + { + zwp_text_input_v3_disable(wlconfig->text_input); + zwp_text_input_v3_commit(wlconfig->text_input); + } + return; + } + + /* Clear any pending preedit before disabling. */ + if (wlconfig->ime_pending_preedit) + { + free(wlconfig->ime_pending_preedit); + wlconfig->ime_pending_preedit = NULL; + } + + NSWindow *nswindow = GSWindowWithNumber(wlconfig->keyboard_focus->window_id); + if (nswindow) + { + NSEvent *ev = [NSEvent otherEventWithType:NSAppKitDefined + location:NSZeroPoint + modifierFlags:0 + timestamp:0 + windowNumber:wlconfig->keyboard_focus->window_id + context:GSCurrentContext() + subtype:GSAppKitWindowFocusOut + data1:0 + data2:0]; + [nswindow sendEvent:ev]; + } + + wlconfig->keyboard_focus = NULL; + + if (wlconfig->text_input) + { + zwp_text_input_v3_disable(wlconfig->text_input); + zwp_text_input_v3_commit(wlconfig->text_input); + wlconfig->text_input_active = NO; + } } static void @@ -125,12 +215,10 @@ uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { - // NSDebugLog(@"keyboard_handle_modifiers"); WaylandConfig *wlconfig = data; wlconfig->event_serial = serial; xkb_mod_mask_t mask; - /* If we're not using a keymap, then we don't handle PC-style modifiers */ if (!wlconfig->xkb.keymap) return; @@ -152,50 +240,50 @@ keyboard_handle_key(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state_w) { - // NSDebugLog(@"keyboard_handle_key: %d", key); - WaylandConfig *wlconfig = data; + WaylandConfig *wlconfig = data; wlconfig->event_serial = serial; - uint32_t code, num_syms; enum wl_keyboard_key_state state = state_w; - const xkb_keysym_t *syms; - xkb_keysym_t sym; - struct window *window = wlconfig->pointer.focus; + uint32_t code; + struct window *window = wlconfig->keyboard_focus + ? wlconfig->keyboard_focus + : wlconfig->pointer.focus; if (!window) return; - code = 0; - if (key == 28) - { - sym = NSCarriageReturnCharacter; - } - else if (key == 14) - { - sym = NSDeleteCharacter; - } - else - { - code = key + 8; + /* Resolve the XKB keycode (evdev + 8 offset). */ + code = key + 8; - num_syms = xkb_state_key_get_syms(wlconfig->xkb.state, code, &syms); + /* Build the character string for this keypress. + * + * xkb_state_key_get_utf8 handles the full XKB composition pipeline, + * including dead-key sequences (e.g. dead_acute + 'e' → 'é'). + * It returns 0 for non-printable keysyms (arrows, function keys, etc.). + */ + char utf8buf[16] = {0}; + NSString *s = @""; - sym = XKB_KEY_NoSymbol; - if (num_syms == 1) - sym = syms[0]; + if (key == 28) /* Enter / Return */ + { + unichar cr = NSCarriageReturnCharacter; + s = [NSString stringWithCharacters:&cr length:1]; } - - NSString *s = [NSString stringWithUTF8String:&sym]; - NSEventType eventType; - - if (state == WL_KEYBOARD_KEY_STATE_PRESSED) + else if (key == 14) /* Backspace */ { - eventType = NSKeyDown; + unichar del = NSDeleteCharacter; + s = [NSString stringWithCharacters:&del length:1]; } - else + else if (wlconfig->xkb.state) { - eventType = NSKeyUp; + int len = xkb_state_key_get_utf8(wlconfig->xkb.state, code, + utf8buf, sizeof(utf8buf) - 1); + if (len > 0) + s = [NSString stringWithUTF8String:utf8buf]; } + NSEventType eventType = (state == WL_KEYBOARD_KEY_STATE_PRESSED) + ? NSKeyDown : NSKeyUp; + NSEvent *ev = [NSEvent keyEventWithType:eventType location:NSZeroPoint modifierFlags:wlconfig->modifiers @@ -208,15 +296,13 @@ keyCode:code]; [GSCurrentServer() postEvent:ev atStart:NO]; - - // NSDebugLog(@"keyboard_handle_key: %@", s); } static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *keyboard, int32_t rate, int32_t delay) { - // NSDebugLog(@"keyboard_handle_repeat_info"); + /* Key repeat is handled by AppKit; nothing to do here. */ } const struct wl_keyboard_listener keyboard_listener @@ -224,7 +310,244 @@ keyboard_handle_leave, keyboard_handle_key, keyboard_handle_modifiers, keyboard_handle_repeat_info}; -@implementation -WaylandServer (KeyboardOps) + +/* ── zwp_text_input_v3 listener (IME/preedit) ────────────────────────────── */ + +/* Apply pending preedit to the focused text view via setMarkedText:. */ +static void +apply_pending_preedit(WaylandConfig *wlconfig) +{ + if (!wlconfig->keyboard_focus || !wlconfig->ime_pending_preedit) + return; + + NSWindow *win = GSWindowWithNumber(wlconfig->keyboard_focus->window_id); + if (!win) + return; + + id responder = [win firstResponder]; + if (![responder respondsToSelector:@selector(setMarkedText:selectedRange:)]) + return; + + NSString *preeditStr = + [NSString stringWithUTF8String:wlconfig->ime_pending_preedit]; + if (!preeditStr) + return; + + /* Underline the preedit text — standard IM convention. */ + NSAttributedString *marked = + [[NSAttributedString alloc] + initWithString:preeditStr + attributes:@{NSUnderlineStyleAttributeName: + @(NSUnderlineStyleSingle)}]; + + /* Convert byte cursor offsets to character offsets (UTF-8 → UTF-16). + * For simplicity we use the midpoint of the preedit range as selection. */ + NSUInteger len = [preeditStr length]; + NSRange sel = (len > 0) ? NSMakeRange(len / 2, 0) : NSMakeRange(0, 0); + + [responder setMarkedText:marked selectedRange:sel]; + [marked release]; +} + +/* Clear any marked text that was set by the IME. */ +static void +clear_marked_text(WaylandConfig *wlconfig) +{ + if (!wlconfig->keyboard_focus) + return; + NSWindow *win = GSWindowWithNumber(wlconfig->keyboard_focus->window_id); + if (!win) + return; + id responder = [win firstResponder]; + if ([responder respondsToSelector:@selector(unmarkText)]) + [responder unmarkText]; +} + +static void +text_input_enter(void *data, struct zwp_text_input_v3 *ti, + struct wl_surface *surface) +{ + WaylandConfig *wlconfig = data; + wlconfig->text_input_active = YES; + NSDebugFLLog(@"WaylandIME", @"text_input_enter"); + + zwp_text_input_v3_enable(ti); + zwp_text_input_v3_commit(ti); +} + +static void +text_input_leave(void *data, struct zwp_text_input_v3 *ti, + struct wl_surface *surface) +{ + WaylandConfig *wlconfig = data; + NSDebugFLLog(@"WaylandIME", @"text_input_leave"); + + /* Clear any preedit text the IM left behind. */ + if (wlconfig->ime_pending_preedit) + { + clear_marked_text(wlconfig); + free(wlconfig->ime_pending_preedit); + wlconfig->ime_pending_preedit = NULL; + } + if (wlconfig->ime_pending_commit) + { + free(wlconfig->ime_pending_commit); + wlconfig->ime_pending_commit = NULL; + } + + wlconfig->text_input_active = NO; + zwp_text_input_v3_disable(ti); + zwp_text_input_v3_commit(ti); +} + +static void +text_input_preedit_string(void *data, struct zwp_text_input_v3 *ti, + const char *text, int32_t cursor_begin, + int32_t cursor_end) +{ + WaylandConfig *wlconfig = data; + NSDebugFLLog(@"WaylandIME", @"text_input_preedit: '%s' [%d,%d]", + text ? text : "", cursor_begin, cursor_end); + + free(wlconfig->ime_pending_preedit); + wlconfig->ime_pending_preedit = text ? strdup(text) : NULL; + wlconfig->ime_preedit_cursor_begin = cursor_begin; + wlconfig->ime_preedit_cursor_end = cursor_end; +} + +static void +text_input_commit_string(void *data, struct zwp_text_input_v3 *ti, + const char *text) +{ + WaylandConfig *wlconfig = data; + NSDebugFLLog(@"WaylandIME", @"text_input_commit: '%s'", text ? text : ""); + + free(wlconfig->ime_pending_commit); + wlconfig->ime_pending_commit = text ? strdup(text) : NULL; +} + +static void +text_input_delete_surrounding_text(void *data, struct zwp_text_input_v3 *ti, + uint32_t before_length, + uint32_t after_length) +{ + NSDebugFLLog(@"WaylandIME", + @"text_input_delete_surrounding: before=%u after=%u", + before_length, after_length); + /* Full surrounding text deletion is deferred to a later milestone. */ +} + +static void +text_input_done(void *data, struct zwp_text_input_v3 *ti, uint32_t serial) +{ + WaylandConfig *wlconfig = data; + wlconfig->ime_serial = serial; + NSDebugFLLog(@"WaylandIME", @"text_input_done: serial=%u", serial); + + struct window *window = wlconfig->keyboard_focus; + if (!window) + goto cleanup; + + NSWindow *nswindow = GSWindowWithNumber(window->window_id); + if (!nswindow) + goto cleanup; + + id responder = [nswindow firstResponder]; + + /* ── Commit string: insert text into the focused control ── */ + if (wlconfig->ime_pending_commit) + { + NSString *commitStr = + [NSString stringWithUTF8String:wlconfig->ime_pending_commit]; + if (commitStr && [commitStr length] > 0) + { + /* Clear any preedit before committing. */ + if ([responder respondsToSelector:@selector(unmarkText)]) + [responder unmarkText]; + + if ([responder respondsToSelector:@selector(insertText:)]) + { + [responder insertText:commitStr]; + } + else + { + /* Fallback: deliver each character as a key event. */ + for (NSUInteger i = 0; i < [commitStr length]; i++) + { + unichar c = [commitStr characterAtIndex:i]; + NSString *cs = [NSString stringWithCharacters:&c length:1]; + NSEvent *ev = [NSEvent keyEventWithType:NSKeyDown + location:NSZeroPoint + modifierFlags:0 + timestamp:[[NSDate date] + timeIntervalSinceReferenceDate] + windowNumber:window->window_id + context:nil + characters:cs + charactersIgnoringModifiers:cs + isARepeat:NO + keyCode:0]; + [nswindow sendEvent:ev]; + } + } + } + free(wlconfig->ime_pending_commit); + wlconfig->ime_pending_commit = NULL; + } + + /* ── Preedit string: show/update marked text ── */ + if (wlconfig->ime_pending_preedit) + { + apply_pending_preedit(wlconfig); + /* Keep ime_pending_preedit alive for area/spot queries. */ + } + else + { + /* NULL preedit = clear any marked text the IM set previously. */ + clear_marked_text(wlconfig); + } + +cleanup:; +} + +/* v2 events — logged but not acted on in this milestone. */ +static void +text_input_action(void *data, struct zwp_text_input_v3 *ti, + uint32_t index, uint32_t direction) +{ + NSDebugFLLog(@"WaylandIME", @"text_input_action: idx=%u dir=%u", + index, direction); +} + +static void +text_input_language(void *data, struct zwp_text_input_v3 *ti, + const char *language) +{ + NSDebugFLLog(@"WaylandIME", @"text_input_language: %s", + language ? language : ""); +} + +static void +text_input_preedit_hint(void *data, struct zwp_text_input_v3 *ti, + uint32_t start, uint32_t end, uint32_t hint) +{ + NSDebugFLLog(@"WaylandIME", @"text_input_preedit_hint: [%u,%u] hint=%u", + start, end, hint); +} + +const struct zwp_text_input_v3_listener text_input_v3_listener = { + text_input_enter, + text_input_leave, + text_input_preedit_string, + text_input_commit_string, + text_input_delete_surrounding_text, + text_input_done, + text_input_action, + text_input_language, + text_input_preedit_hint, +}; + + +@implementation WaylandServer (KeyboardOps) @end diff --git a/Source/wayland/WaylandServer+Output.m b/Source/wayland/WaylandServer+Output.m index 650ddd83..7797b7e7 100644 --- a/Source/wayland/WaylandServer+Output.m +++ b/Source/wayland/WaylandServer+Output.m @@ -1,4 +1,4 @@ -/* +/* WaylandServer - Output Handling Copyright (C) 2020 Free Software Foundation, Inc. @@ -26,63 +26,224 @@ */ #include "wayland/WaylandServer.h" +#include +#include +#include +#include +#include +#include + + +/* ── Helpers ─────────────────────────────────────────────────────────────── */ + +/* Compute the logical (AppKit) dimensions of an output, accounting for its + * pixel scale factor and any rotation transform reported by the compositor. + * + * Physical mode width/height are in hardware pixels. After dividing by the + * scale factor we get logical pixels (points). A 90° or 270° rotation also + * swaps the two axes. */ +static void +output_compute_effective_size(struct output *output) +{ + int ew = (output->scale > 0) ? output->width / output->scale : output->width; + int eh = (output->scale > 0) ? output->height / output->scale : output->height; + + switch (output->transform) + { + case WL_OUTPUT_TRANSFORM_90: + case WL_OUTPUT_TRANSFORM_270: + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + /* Swap: the output is rotated 90° or 270° relative to normal. */ + output->effective_width = eh; + output->effective_height = ew; + break; + default: + output->effective_width = ew; + output->effective_height = eh; + break; + } +} + +/* Post NSApplicationDidChangeScreenParametersNotification on the main thread + * so NSScreen reloads its geometry. Guard against early-init calls that + * arrive before NSApp is running. */ +static void +notify_screen_parameters_changed(void) +{ + if (NSApp == nil) + return; + [[NSNotificationCenter defaultCenter] + postNotificationName: NSApplicationDidChangeScreenParametersNotification + object: NSApp]; +} + +/* Clamp all regular (non-layer-shell) windows assigned to an output so that + * they remain within the output's effective (logical) bounds after a + * reconfigure. We update our internal pos_x/y and ask AppKit to move the + * window; the compositor will honour or adjust the request as it sees fit. */ +static void +reposition_windows_for_output(struct output *output) +{ + WaylandConfig *wlconfig = output->wlconfig; + struct window *window; + + wl_list_for_each(window, &wlconfig->window_list, link) + { + if (window->output != output) + continue; + if (window->terminated || window->layer_surface) + continue; + + int ew = output->effective_width; + int eh = output->effective_height; + BOOL moved = NO; + + /* Clamp position so at least the top-left corner is on-screen. */ + if (window->pos_x < 0) { window->pos_x = 0; moved = YES; } + if (window->pos_y < 0) { window->pos_y = 0; moved = YES; } + if (window->pos_x >= (float)ew) { window->pos_x = MAX(0, ew - (int)window->width); moved = YES; } + if (window->pos_y >= (float)eh) { window->pos_y = MAX(0, eh - (int)window->height); moved = YES; } + + if (moved) + { + NSDebugFLLog(@"WaylandOutput", + @"reposition_windows_for_output: clamped window %d " + @"to (%g,%g) within %dx%d output", + window->window_id, window->pos_x, window->pos_y, ew, eh); + + /* Notify AppKit of the new position in GNUstep screen coordinates + * (Y-flipped: bottom-left origin, as in AppKit). */ + NSWindow *nswindow = GSWindowWithNumber(window->window_id); + if (nswindow) + { + float ns_y = eh - window->pos_y - window->height; + [nswindow setFrameOrigin: NSMakePoint(window->pos_x, ns_y)]; + } + } + } +} + + +/* ── wl_output event handlers ─────────────────────────────────────────────── */ static void handle_geometry(void *data, struct wl_output *wl_output, int x, int y, int physical_width, int physical_height, int subpixel, const char *make, const char *model, int transform) { - NSDebugLog(@"handle_geometry"); struct output *output = data; - output->alloc_x = x; - output->alloc_y = y; + output->alloc_x = x; + output->alloc_y = y; output->transform = transform; - if (output->make) - free(output->make); - output->make = strdup(make); + if (output->make) free(output->make); + if (output->model) free(output->model); + output->make = strdup(make ? make : ""); + output->model = strdup(model ? model : ""); - if (output->model) - free(output->model); - output->model = strdup(model); + NSDebugFLLog(@"WaylandOutput", + @"handle_geometry: output %u @(%d,%d) physical=%dx%dmm " + @"transform=%d make=%s model=%s", + output->server_output_id, x, y, + physical_width, physical_height, transform, make, model); } static void -handle_done(void *data, struct wl_output *wl_output) +handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, + int width, int height, int refresh) { - NSDebugLog(@"handle_done"); + struct output *output = data; + + NSDebugFLLog(@"WaylandOutput", + @"handle_mode: output %u flags=0x%x size=%dx%d refresh=%dHz", + output->server_output_id, flags, width, height, refresh / 1000); + + if (flags & WL_OUTPUT_MODE_CURRENT) + { + output->width = width; + output->height = height; + NSDebugFLLog(@"WaylandOutput", + @"handle_mode: output %u current mode → %dx%d physical", + output->server_output_id, width, height); + } } static void handle_scale(void *data, struct wl_output *wl_output, int32_t scale) { - NSDebugLog(@"handle_scale"); struct output *output = data; - output->scale = scale; + NSDebugFLLog(@"WaylandOutput", @"handle_scale: output %u scale=%d", + output->server_output_id, scale); } +/* handle_done is called once all output properties for a configuration batch + * have been sent. It is the right place to: + * 1. Compute the effective (logical) output size. + * 2. Reposition any windows that fell outside the new bounds. + * 3. Notify AppKit so NSScreen reloads its geometry. */ static void -handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int width, - int height, int refresh) +handle_done(void *data, struct wl_output *wl_output) { - NSDebugLog(@"handle_mode"); struct output *output = data; - if (flags & WL_OUTPUT_MODE_CURRENT) - { - output->width = width; - output->height = height /*- 30*/; - NSDebugLog(@"handle_mode output=%dx%d", width, height); - - // XXX - Should we implement this? - // if (display->output_configure_handler) - // (*display->output_configure_handler) - // (output, display->user_data); - // - } + int old_ew = output->effective_width; + int old_eh = output->effective_height; + + output_compute_effective_size(output); + + NSDebugFLLog(@"WaylandOutput", + @"handle_done: output %u '%s' logical=%dx%d (phys=%dx%d scale=%d " + @"transform=%d) alloc=(%d,%d)", + output->server_output_id, + output->name ? output->name : "unknown", + output->effective_width, output->effective_height, + output->width, output->height, output->scale, output->transform, + output->alloc_x, output->alloc_y); + + BOOL first_configure = !output->configured; + output->configured = YES; + + /* Only reposition and notify if something actually changed (or first time). */ + if (!first_configure + && output->effective_width == old_ew + && output->effective_height == old_eh) + return; + + reposition_windows_for_output(output); + notify_screen_parameters_changed(); +} + +/* wl_output v4: human-readable connector name (e.g. "HDMI-A-1", "eDP-1"). */ +static void +handle_name(void *data, struct wl_output *wl_output, const char *name) +{ + struct output *output = data; + if (output->name) free(output->name); + output->name = strdup(name ? name : ""); + NSDebugFLLog(@"WaylandOutput", @"handle_name: output %u name='%s'", + output->server_output_id, output->name); +} + +/* wl_output v4: human-readable description (e.g. "Samsung 27\" monitor"). */ +static void +handle_description(void *data, struct wl_output *wl_output, + const char *description) +{ + struct output *output = data; + if (output->description) free(output->description); + output->description = strdup(description ? description : ""); + NSDebugFLLog(@"WaylandOutput", @"handle_description: output %u desc='%s'", + output->server_output_id, output->description); } -const struct wl_output_listener output_listener - = {handle_geometry, handle_mode, handle_done, handle_scale}; +const struct wl_output_listener output_listener = { + handle_geometry, + handle_mode, + handle_done, + handle_scale, + handle_name, + handle_description, +}; diff --git a/Source/wayland/WaylandServer+Seat.m b/Source/wayland/WaylandServer+Seat.m index d4cf73c2..892f645d 100644 --- a/Source/wayland/WaylandServer+Seat.m +++ b/Source/wayland/WaylandServer+Seat.m @@ -26,6 +26,7 @@ */ #include "wayland/WaylandServer.h" +#include extern const struct wl_keyboard_listener keyboard_listener; @@ -87,6 +88,17 @@ #endif } +/* wl_seat.name — sent by the compositor since wl_seat v2. We bound the seat + * at version 5 for frame/axis events, so this event now arrives at startup. + * An absent handler slot causes libwayland-client to abort with + * "listener function for opcode 1 of wl_seat is NULL". */ +static void +seat_handle_name(void *data, struct wl_seat *seat, const char *name) +{ + NSDebugFLLog(@"WaylandIME", @"seat_handle_name: '%s'", name); +} + const struct wl_seat_listener seat_listener = { seat_handle_capabilities, + seat_handle_name, }; diff --git a/Source/wayland/WaylandServer+Xdgshell.m b/Source/wayland/WaylandServer+Xdgshell.m index 88314618..f55c2d88 100644 --- a/Source/wayland/WaylandServer+Xdgshell.m +++ b/Source/wayland/WaylandServer+Xdgshell.m @@ -29,6 +29,8 @@ #include #include #include +#include +#include static void xdg_surface_on_configure(void *data, struct xdg_surface *xdg_surface, @@ -40,30 +42,16 @@ if (window->terminated == YES) { - // 'struct window' is defined in Headers/wayland/WaylandServer. - // - // window->terminated should only be true after - // -[WaylandServer(WindowOps) termwindow:(int)win] sets this to true - // after invoking -[WaylandServer destroyWindowShell:window] and - // using wl_list_remove(&window->link);. - // - // We do not expect 'window' to be referenced again, since - // -destroyWindowShell: invokes wl_display_dispatch_pending(...) and - // wl_display_flush(...);. On the off chance it does, first, we may want - // to patch every single invocation of get_window_with_id() that may - // currently be ignoring the case where NULL may be returned, and - // possibly crashing for that reason. - // - // But, a free here should on its own be fine as long as everyone - // passes around the window ID and does not store a ptr to window itself. - NSDebugLog(@"deleting window win=%d", window->window_id); + /* termwindow: already removed this window from wlconfig->window_list + * and set terminated = YES. We must NOT call wl_list_remove again — + * the node's prev/next pointers point to themselves after the first + * remove, and a second remove would corrupt the list. + * This configure arrived in-flight after termwindow; free the struct. */ + NSDebugLog(@"deleting window win=%d (deferred free)", window->window_id); free(window); return; } - NSEvent *ev = nil; - NSWindow *nswindow = GSWindowWithNumber(window->window_id); - // NSDebugLog(@"Acknowledging surface configure %p %d (window_id=%d)", // xdg_surface, serial, window->window_id); @@ -76,22 +64,11 @@ window->width, window->height) :window->window_id]; } - - if (window->wlconfig->pointer.focus - && window->wlconfig->pointer.focus->window_id == window->window_id) - { - ev = [NSEvent otherEventWithType:NSAppKitDefined - location:NSZeroPoint - modifierFlags:0 - timestamp:0 - windowNumber:(int) window->window_id - context:GSCurrentContext() - subtype:GSAppKitWindowFocusIn - data1:0 - data2:0]; - - [nswindow sendEvent:ev]; - } + /* Keyboard focus is now handled exclusively by keyboard_handle_enter/leave, + which correctly tracks what the compositor has granted. Sending + GSAppKitWindowFocusIn here based on pointer position was wrong: it could + steal key-window status from a modal dialog simply because the mouse + cursor happened to be over the reconfigured window. */ } static void @@ -145,15 +122,73 @@ NSDebugLog(@"[%d] xdg_popup_configure [%d,%d %dx%d]", window->window_id, x, y, width, height); + + /* The compositor reports the popup's actual position (in parent surface + * coords) and size. Sync window->pos_x/pos_y so GNUstep's NSWindow frame + * matches where the compositor actually placed the popup. Without this, + * locationForSubmenu: uses a stale frame and submenus appear offset. */ + if (window->parent_id) + { + struct window *parent + = get_window_with_id(window->wlconfig, window->parent_id); + if (parent && window->output) + { + window->pos_x = parent->pos_x + x; + window->pos_y = parent->pos_y + y; + + if (width > 0) + window->width = width; + if (height > 0) + window->height = height; + + NSWindow *nswin = GSWindowWithNumber(window->window_id); + if (nswin) + { + NSEvent *ev = [NSEvent + otherEventWithType:NSAppKitDefined + location:NSMakePoint(0, 0) + modifierFlags:0 + timestamp:0 + windowNumber:window->window_id + context:GSCurrentContext() + subtype:GSAppKitWindowMoved + data1:window->pos_x + data2:WaylandToNS(window, window->pos_y)]; + [nswin sendEvent:ev]; + } + } + } } static void xdg_popup_done(void *data, struct xdg_popup *xdg_popup) { struct window *window = data; - window->terminated = YES; + + /* Destroy the popup role (required by the protocol after popup_done), + * then destroy the xdg_surface and wl_surface in the correct order. + * NULL all pointers immediately so that destroySurfaceRole: (called + * when GNUstep eventually orders the window out) does not attempt a + * second destroy on already-freed Wayland proxy objects. A double + * destroy causes a Wayland protocol error, which disconnects the + * compositor session and closes every window including modal dialogs. */ xdg_popup_destroy(xdg_popup); - wl_surface_destroy(window->surface); + window->popup = NULL; + + if (window->xdg_surface) + { + xdg_surface_destroy(window->xdg_surface); + window->xdg_surface = NULL; + } + + if (window->surface) + { + wl_surface_destroy(window->surface); + window->surface = NULL; + } + + window->configured = NO; + window->terminated = YES; } static void diff --git a/Source/wayland/WaylandServer.m b/Source/wayland/WaylandServer.m index d3fd1b8e..7b92d6f9 100644 --- a/Source/wayland/WaylandServer.m +++ b/Source/wayland/WaylandServer.m @@ -46,10 +46,14 @@ #include #include "wayland/WaylandServer.h" +#include "wayland/WaylandOpenGL.h" +#include "wayland/WaylandInputServer.h" +#include "cairo/WaylandCairoShmSurface.h" extern const struct wl_output_listener output_listener; - extern const struct wl_seat_listener seat_listener; +extern const struct wl_data_device_listener data_device_listener; +extern const struct zwp_text_input_v3_listener text_input_v3_listener; static void shm_format(void *data, struct wl_shm *wl_shm, uint32_t format) @@ -67,6 +71,10 @@ extern const struct xdg_popup_listener xdg_popup_listener; +extern const struct zxdg_toplevel_decoration_v1_listener toplevel_decoration_listener; + +static BOOL handlesWindowDecorations = NO; + static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) @@ -111,10 +119,14 @@ memset(output, 0, sizeof(struct output)); output->wlconfig = wlconfig; output->scale = 1; + /* Bind at version 4 to receive name/description events in addition to + * the v2 done/scale events. Fall back to whatever the compositor + * offers if it is older. */ + uint32_t out_v = (version < 4) ? version : 4; output->output - = wl_registry_bind(registry, name, &wl_output_interface, 2); + = wl_registry_bind(registry, name, &wl_output_interface, out_v); output->server_output_id = name; - NSDebugLog(@"wayland: found output interface"); + NSDebugLog(@"wayland: found output interface (version %u)", out_v); wl_list_insert(wlconfig->output_list.prev, &output->link); wlconfig->output_count++; wl_output_add_listener(output->output, &output_listener, output); @@ -122,16 +134,103 @@ else if (strcmp(interface, wl_seat_interface.name) == 0) { wlconfig->pointer.wlpointer = NULL; - wlconfig->seat_version = version; + /* Bind at v5+ to receive wl_pointer.frame and axis-source/stop/discrete + * events needed for correct per-frame scroll accumulation. */ + uint32_t seat_v = (version < 5) ? version : 5; + wlconfig->seat_version = seat_v; wlconfig->seat - = wl_registry_bind(wlconfig->registry, name, &wl_seat_interface, 1); + = wl_registry_bind(wlconfig->registry, name, &wl_seat_interface, seat_v); NSDebugLog(@"wayland: found seat interface"); wl_seat_add_listener(wlconfig->seat, &seat_listener, wlconfig); } + else if (strcmp(interface, wl_subcompositor_interface.name) == 0) + { + wlconfig->subcompositor + = wl_registry_bind(registry, name, &wl_subcompositor_interface, 1); + NSDebugLog(@"wayland: found subcompositor interface"); + } + else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) + { + wlconfig->decoration_manager + = wl_registry_bind(registry, name, &zxdg_decoration_manager_v1_interface, 1); + NSDebugLog(@"wayland: found xdg-decoration-manager interface"); + } + else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) + { + uint32_t v = (version < 3) ? version : 3; + wlconfig->data_device_manager_version = v; + wlconfig->data_device_manager + = wl_registry_bind(registry, name, &wl_data_device_manager_interface, v); + NSDebugLog(@"wayland: found wl_data_device_manager (version %u)", v); + } + else if (strcmp(interface, zwp_text_input_manager_v3_interface.name) == 0) + { + wlconfig->text_input_manager + = wl_registry_bind(registry, name, &zwp_text_input_manager_v3_interface, 1); + NSDebugLog(@"wayland: found zwp_text_input_manager_v3"); + } } -static void handle_global_remove(void *data, struct wl_registry *registry, - uint32_t name) {} +static void +handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) +{ + WaylandConfig *wlconfig = data; + + /* Find the output with this global name and remove it. */ + struct output *output; + struct output *found = NULL; + wl_list_for_each(output, &wlconfig->output_list, link) + { + if (output->server_output_id == name) + { + found = output; + break; + } + } + + if (found == NULL) + return; + + NSDebugLog(@"wayland: global removed: output %u '%s'", + found->server_output_id, found->name ? found->name : "unknown"); + + /* Reassign any windows on the removed output to the first remaining output + * so that coordinate conversions (which dereference window->output) do not + * crash. */ + struct output *fallback = NULL; + wl_list_for_each(output, &wlconfig->output_list, link) + { + if (output != found) { fallback = output; break; } + } + + struct window *window; + wl_list_for_each(window, &wlconfig->window_list, link) + { + if (window->output == found) + { + window->output = fallback; /* may be NULL if last output removed */ + NSDebugLog(@"wayland: window %d reassigned from removed output %u", + window->window_id, name); + } + } + + /* Remove from the list and free resources. */ + wl_list_remove(&found->link); + wlconfig->output_count--; + + wl_output_destroy(found->output); + if (found->make) free(found->make); + if (found->model) free(found->model); + if (found->name) free(found->name); + if (found->description) free(found->description); + free(found); + + /* Notify AppKit that the screen list changed. */ + if (NSApp != nil) + [[NSNotificationCenter defaultCenter] + postNotificationName: NSApplicationDidChangeScreenParametersNotification + object: NSApp]; +} static const struct wl_registry_listener registry_listener = { handle_global, handle_global_remove}; @@ -200,6 +299,32 @@ - (id)_initWaylandContext wl_display_dispatch(wlconfig->display); wl_display_roundtrip(wlconfig->display); + /* Create text_input once both text_input_manager and seat are available. */ + if (wlconfig->text_input_manager && wlconfig->seat) + { + wlconfig->text_input = zwp_text_input_manager_v3_get_text_input( + wlconfig->text_input_manager, wlconfig->seat); + if (wlconfig->text_input) + { + zwp_text_input_v3_add_listener(wlconfig->text_input, + &text_input_v3_listener, wlconfig); + NSDebugLog(@"wayland: zwp_text_input_v3 created"); + } + } + + /* Get a data device now that both seat and data_device_manager are bound. */ + if (wlconfig->data_device_manager && wlconfig->seat) + { + wlconfig->data_device = wl_data_device_manager_get_data_device( + wlconfig->data_device_manager, wlconfig->seat); + if (wlconfig->data_device) + { + wl_data_device_add_listener(wlconfig->data_device, + &data_device_listener, wlconfig); + NSDebugLog(@"wayland: wl_data_device created"); + } + } + if (!wlconfig->compositor) { [NSException raise:NSWindowServerCommunicationException @@ -217,6 +342,27 @@ - (id)_initWaylandContext @"compositor must support the stable XDG Shell protocol"]; } + /* Determine decoration mode. Default: use SSD if the compositor supports it. + The user can override with GSBackHandlesWindowDecorations in defaults. */ + NSUserDefaults *defs = [NSUserDefaults standardUserDefaults]; + if ([defs objectForKey: @"GSBackHandlesWindowDecorations"]) + { + handlesWindowDecorations + = [defs boolForKey: @"GSBackHandlesWindowDecorations"]; + } + else + { + handlesWindowDecorations = (wlconfig->decoration_manager != NULL); + } + NSDebugLog(@"wayland: handlesWindowDecorations=%s (decoration_manager=%s)", + handlesWindowDecorations ? "YES" : "NO", + wlconfig->decoration_manager ? "available" : "not available"); + + + inputServer = [[WaylandInputServer allocWithZone: [self zone]] + initWithDelegate: nil name: @"WaylandInput"]; + [(WaylandInputServer *)inputServer setWlconfig: wlconfig]; + return self; } @@ -264,12 +410,18 @@ - (id)initWithAttributes:(NSDictionary *)info - (void)dealloc { NSDebugLog(@"Destroying Wayland Server"); + if (wlconfig->decoration_manager) + { + zxdg_decoration_manager_v1_destroy(wlconfig->decoration_manager); + wlconfig->decoration_manager = NULL; + } + DESTROY(inputServer); [super dealloc]; } - (BOOL)handlesWindowDecorations { - return NO; + return handlesWindowDecorations; } - (void)restrictWindow:(int)win toImage:(NSImage *)image @@ -279,16 +431,32 @@ - (void)restrictWindow:(int)win toImage:(NSImage *)image - (NSRect)boundsForScreen:(int)screen { - NSDebugLog(@"boundsForScreen: %d", screen); struct output *output; + /* Find the output whose server_output_id matches the requested screen. */ wl_list_for_each(output, &wlconfig->output_list, link) { - NSDebugLog(@"screen found: %dx%d", output->width, output->height); - return NSMakeRect(0, 0, output->width, output->height); + if ((int)output->server_output_id == screen) + { + int ew = output->configured ? output->effective_width : output->width; + int eh = output->configured ? output->effective_height : output->height; + NSDebugLog(@"boundsForScreen: %d → logical=%dx%d (phys=%dx%d scale=%d)", + screen, ew, eh, output->width, output->height, output->scale); + return NSMakeRect(output->alloc_x, output->alloc_y, ew, eh); + } + } + + /* Fallback: return the first output's bounds. */ + wl_list_for_each(output, &wlconfig->output_list, link) + { + int ew = output->configured ? output->effective_width : output->width; + int eh = output->configured ? output->effective_height : output->height; + NSDebugLog(@"boundsForScreen: %d not found, using first output %dx%d", + screen, ew, eh); + return NSMakeRect(0, 0, ew, eh); } - NSDebugLog(@"can't find screen"); + NSDebugLog(@"boundsForScreen: no outputs available"); return NSZeroRect; } @@ -330,8 +498,8 @@ - (void *)serverDevice - (void *)windowDevice:(int)win { - NSDebugLog(@"windowDevice"); - return NULL; + NSDebugLog(@"windowDevice: %d", win); + return get_window_with_id(wlconfig, win); } - (void)beep @@ -339,6 +507,74 @@ - (void)beep NSDebugLog(@"beep"); } +- glContextClass +{ + return [WaylandGLContext class]; +} + +- glPixelFormatClass +{ + return [WaylandGLPixelFormat class]; +} + +@end + +@implementation WaylandServer (InputMethod) + +- (NSString *) inputMethodStyle +{ + return inputServer + ? [(WaylandInputServer *) inputServer inputMethodStyle] : nil; +} + +- (NSString *) fontSize: (int *)size +{ + return inputServer + ? [(WaylandInputServer *) inputServer fontSize: size] : nil; +} + +- (BOOL) clientWindowRect: (NSRect *)rect +{ + return inputServer + ? [(WaylandInputServer *) inputServer clientWindowRect: rect] : NO; +} + +- (BOOL) statusArea: (NSRect *)rect +{ + return inputServer + ? [(WaylandInputServer *) inputServer statusArea: rect] : NO; +} + +- (BOOL) preeditArea: (NSRect *)rect +{ + return inputServer + ? [(WaylandInputServer *) inputServer preeditArea: rect] : NO; +} + +- (BOOL) preeditSpot: (NSPoint *)p +{ + return inputServer + ? [(WaylandInputServer *) inputServer preeditSpot: p] : NO; +} + +- (BOOL) setStatusArea: (NSRect *)rect +{ + return inputServer + ? [(WaylandInputServer *) inputServer setStatusArea: rect] : NO; +} + +- (BOOL) setPreeditArea: (NSRect *)rect +{ + return inputServer + ? [(WaylandInputServer *) inputServer setPreeditArea: rect] : NO; +} + +- (BOOL) setPreeditSpot: (NSPoint *)p +{ + return inputServer + ? [(WaylandInputServer *) inputServer setPreeditSpot: p] : NO; +} + @end @implementation @@ -404,6 +640,8 @@ - (int)window:(NSRect) window->moving = NO; window->resizing = NO; window->ignoreMouse = NO; + window->usesOpenGL = NO; + window->global_pos_known = NO; // FIXME is this needed? if (window->pos_x < 0) @@ -448,6 +686,14 @@ - (void)termwindow:(int)win NSDebugLog(@"termwindow: win=%d", win); struct window *window = get_window_with_id(wlconfig, win); + /* Clear any stale focus references to avoid dangling pointers. */ + if (wlconfig->keyboard_focus == window) + wlconfig->keyboard_focus = NULL; + if (wlconfig->pointer.focus == window) + wlconfig->pointer.focus = NULL; + if (wlconfig->pointer.captured == window) + wlconfig->pointer.captured = NULL; + [self destroyWindowShell:window]; // FIXME should wait for buffer release before detroying it // @@ -556,7 +802,27 @@ - (void)orderwindow:(int)op:(int)otherWin:(int)win - (void)movewindow:(NSPoint)loc:(int)win { - NSDebugLog(@"movewindow"); + NSDebugLog(@"[%d] movewindow: %f,%f", win, loc.x, loc.y); + struct window *window = get_window_with_id(wlconfig, win); + if (!window) + return; + + window->pos_x = loc.x; + window->pos_y = NSToWayland(window, (int) loc.y); + + /* Layer-shell surfaces are positioned via top/left margins from their + anchor point (ANCHOR_TOP | ANCHOR_LEFT), so we can reposition them + by updating the margins and committing. XDG toplevels are positioned + by the compositor and cannot be moved from the client side. */ + if (window->layer_surface) + { + zwlr_layer_surface_v1_set_margin(window->layer_surface, + (int32_t) window->pos_y, + 0, 0, + (int32_t) window->pos_x); + wl_surface_commit(window->surface); + wl_display_flush(wlconfig->display); + } } - (NSRect) _OSFrameToWFrame: (NSRect)o for: (void*)win @@ -669,8 +935,35 @@ - (void)setParentWindow:(int)parentWin forChildWindow:(int)childWin NSDebugLog(@"setParentWindow: parent=%d child=%d", parentWin, childWin); struct window *parent = get_window_with_id(wlconfig, parentWin); struct window *child = get_window_with_id(wlconfig, childWin); + if (!parent || !child) + { + return; + } + + if (child->level == NSPopUpMenuWindowLevel) + { + /* NSPopUpMenuWindowLevel is a NOOP in createSurfaceShell; create the + * xdg_popup here so the compositor handles grab and dismiss. */ + [self createPopupShell:child withParentShell:parent]; + return; + } + + if (child->level == NSSubmenuWindowLevel) + { + /* Submenus are created by createSubMenuShell (layer shell preferred). + * Only fall back to xdg_popup if no role has been assigned yet. */ + if (![self windowSurfaceHasRole:child]) + [self createPopupShell:child withParentShell:parent]; + return; + } - [self createPopupShell:child withParentShell:parent]; + /* Panels, dialogs, and other transient toplevels: record the parent and + * call xdg_toplevel_set_parent. Never use xdg_popup here — xdg_popup + * auto-dismisses when pointer focus leaves the surface, which closes + * dialogs as soon as the mouse moves to another window. */ + child->parent_id = parentWin; + if (child->toplevel && parent->toplevel) + xdg_toplevel_set_parent(child->toplevel, parent->toplevel); } - (void)setwindowlevel:(int)level:(int)win @@ -714,6 +1007,16 @@ - (void)flushwindowrect:(NSRect)rect:(int)win NSDebugLog(@"[%d] flushwindowrect: %f,%f %fx%f", win, NSMinX(rect), NSMinY(rect), NSWidth(rect), NSHeight(rect)); struct window *window = get_window_with_id(wlconfig, win); + if (window == NULL) + { + return; + } + + if (window->usesOpenGL) + { + NSDebugLog(@"[%d] skipping cairo flush for OpenGL-backed window", win); + return; + } [[GSCurrentContext() class] handleExposeRect:rect forDriver:window->wcs]; } @@ -783,7 +1086,59 @@ - (void)createSurfaceShell:(struct window *)window break; case NSPopUpMenuWindowLevel: NSDebugLog(@"[%d] NSPopUpMenuWindowLevel", win); - [self createPopup:window]; + /* Use layer shell so the menu can extend beyond the parent window's + * surface without losing pointer events. xdg_popup clips event + * delivery to the parent surface bounds on many compositors, which + * breaks tracking when the menu extends outside the parent window. + * NSPopUpButton popups go through setParentWindow:forChildWindow: + * before orderwindow and already have a surface role by the time + * createSurfaceShell is reached, so this path is only taken for + * right-click context menus (displayTransient). + * + * Before creating the surface, translate window->pos_x/y from + * GNUstep's assumed coordinates into accurate output-relative + * coordinates by adding the delta between the key window's inferred + * global origin (saved_pos_x/y, tracked via cursor enter events) and + * GNUstep's assumed origin (pos_x/y). Then notify GNUstep of the + * corrected frame so that locationForSubmenu: and other screen- + * coordinate queries work correctly for any submenus. */ + if (wlconfig->layer_shell) + { + NSWindow *keyWin = [NSApp keyWindow]; + if (keyWin && (int)[keyWin windowNumber] != win) + { + struct window *kwin = + get_window_with_id(wlconfig, (int)[keyWin windowNumber]); + if (kwin && kwin->global_pos_known) + { + float dx = kwin->saved_pos_x - kwin->pos_x; + float dy = kwin->saved_pos_y - kwin->pos_y; + window->pos_x += dx; + window->pos_y += dy; + NSWindow *nswin = GSWindowWithNumber(win); + if (nswin) + { + NSEvent *ev = + [NSEvent otherEventWithType:NSAppKitDefined + location:NSZeroPoint + modifierFlags:0 + timestamp:0 + windowNumber:win + context:GSCurrentContext() + subtype:GSAppKitWindowMoved + data1:window->pos_x + data2:WaylandToNS(window, + window->pos_y)]; + [nswin sendEvent:ev]; + } + } + } + [self createLayerShell:window + withLayerType:ZWLR_LAYER_SHELL_V1_LAYER_TOP + withNamespace:@"gnustep-popup"]; + } + else + [self createTopLevel:window]; break; case NSScreenSaverWindowLevel: NSDebugLog(@"[%d] NSScreenSaverWindowLevel", win); @@ -836,7 +1191,10 @@ - (void)createTopLevel:(struct window *)window return; } - wl_surface_set_user_data(window->surface, window); + window->surface_binding.window = window; + window->surface_binding.offset_x = 0.0f; + window->surface_binding.offset_y = 0.0f; + wl_surface_set_user_data(window->surface, &window->surface_binding); if (window->xdg_surface == NULL) { window->xdg_surface @@ -848,6 +1206,26 @@ - (void)createTopLevel:(struct window *)window xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener, window); + /* Apply transient-parent relationship. + * + * Priority 1: explicit parent from setParentWindow: (sheets, child panels). + * Priority 2: implicit parent for modal panels — set to the keyboard-focused + * window so the Ambrosia compositor can protect the dialog from focus + * steals even when no explicit addChildWindow: call was made (e.g. the + * standalone [NSOpenPanel runModal] case). */ + { + struct window *parent = NULL; + if (window->parent_id) + parent = get_window_with_id(wlconfig, window->parent_id); + else if (window->level == NSModalPanelWindowLevel + && wlconfig->keyboard_focus != NULL + && wlconfig->keyboard_focus != window) + parent = wlconfig->keyboard_focus; + + if (parent && parent->toplevel) + xdg_toplevel_set_parent(window->toplevel, parent->toplevel); + } + xdg_surface_set_window_geometry(window->xdg_surface, 0, 0, window->width, window->height); } @@ -883,7 +1261,10 @@ - (void)createLayerShell:(struct window *)window } window->surface = wl_compositor_create_surface(wlconfig->compositor); - wl_surface_set_user_data(window->surface, window); + window->surface_binding.window = window; + window->surface_binding.offset_x = 0.0f; + window->surface_binding.offset_y = 0.0f; + wl_surface_set_user_data(window->surface, &window->surface_binding); const char *cString = [namespace UTF8String]; window->layer_surface @@ -952,40 +1333,33 @@ - (void)createSubMenuShell:(struct window *)window if ([self windowSurfaceHasRole:window]) { - // if the role is already assigned skip return; } + /* Use layer shell so the submenu can extend beyond any parent surface + * without losing pointer events. xdg_popup clips event delivery to + * the parent surface bounds, which breaks tracking when the submenu + * extends to the right or below the parent menu's surface. */ if (wlconfig->layer_shell) { - // the preferred way to create a submenu is to use an overlay [self createLayerShell:window - withLayerType:ZWLR_LAYER_SHELL_V1_LAYER_TOP - withNamespace:@"gnustep-submenu"]; + withLayerType:ZWLR_LAYER_SHELL_V1_LAYER_TOP + withNamespace:@"gnustep-submenu"]; return; } - else - { - NSDebugLog(@"layer shell not supported, fallback to xdg popup"); - } - // if the layer shell is not available then we use the xdg popup - // for that we need a parent toplevel window + /* No layer shell: fall back to xdg_popup under the nearest parent + * menu surface. */ + NSDebugLog(@"layer shell not supported, fallback to xdg popup"); struct window *rootwindow = window; struct window *parentwindow = rootwindow; - while (rootwindow = [self getSuperMenuWindow:parentwindow]) + while ((rootwindow = [self getSuperMenuWindow:parentwindow])) { parentwindow = rootwindow; } if (!parentwindow) - { - return; - } + return; NSDebugLog(@"new popup: %d parent id: %d", win, parentwindow->window_id); - NSDebugLog(@"parent: %d [%f,%f %fx%f]", parentwindow->window_id, - parentwindow->pos_x, parentwindow->pos_y, parentwindow->width, - parentwindow->height); - [self createPopupShell:window withParentShell:parentwindow]; } @@ -999,9 +1373,10 @@ - (void)createPopupShell:(struct window *)child { NSDebugLog(@"createPopupShell"); - if (parent->toplevel == NULL && parent->layer_surface == NULL) + if (parent->toplevel == NULL && parent->layer_surface == NULL + && parent->popup == NULL) { - NSDebugLog(@"parent surface %d is not toplevel", parent->window_id); + NSDebugLog(@"parent surface %d has no surface role", parent->window_id); return; } if ([self windowSurfaceHasRole:child]) @@ -1009,8 +1384,13 @@ - (void)createPopupShell:(struct window *)child [self destroySurfaceRole:child]; } + child->parent_id = parent->window_id; + child->surface = wl_compositor_create_surface(wlconfig->compositor); - wl_surface_set_user_data(child->surface, child); + child->surface_binding.window = child; + child->surface_binding.offset_x = 0.0f; + child->surface_binding.offset_y = 0.0f; + wl_surface_set_user_data(child->surface, &child->surface_binding); NSWindow *nswin = (GSWindowWithNumber(child->window_id)); CGFloat x = nswin.frame.origin.x; @@ -1036,6 +1416,12 @@ - (void)createPopupShell:(struct window *)child child->popup = xdg_surface_get_popup(child->xdg_surface, parent->xdg_surface, positioner); + /* Grab pointer/keyboard so the compositor auto-dismisses the popup on + * outside clicks and delivers events to it. Only grab when we have a + * valid event serial (i.e. the popup was triggered by user input). */ + if (wlconfig->event_serial) + xdg_popup_grab(child->popup, wlconfig->seat, wlconfig->event_serial); + if (parent->layer_surface) { zwlr_layer_surface_v1_get_popup(parent->layer_surface, child->popup); @@ -1082,11 +1468,22 @@ - (void)destroySurfaceRole:(struct window *)window xdg_surface_destroy(window->xdg_surface); window->xdg_surface = NULL; } + /* Destroy the base wl_surface last. Role objects (xdg_surface, + * layer_surface) must be destroyed first per the Wayland protocol. + * The cairo SHM surface (window->wcs) is an ObjC object that holds a + * reference to the wl_surface via pbuffer->owner_surface; clear that + * pointer so the release callback does not write to a freed proxy. */ if (window->wcs) { - // [window->wcs destroySurface]; + [(WaylandCairoShmSurface *)window->wcs clearOwnerSurface]; } - window->configured = NO; + if (window->surface) + { + wl_surface_destroy(window->surface); + window->surface = NULL; + } + window->configured = NO; + window->buffer_needs_attach = YES; } - (void)destroyWindowShell:(struct window *)window diff --git a/Source/wayland/protocols/xdg-decoration-unstable-v1.xml b/Source/wayland/protocols/xdg-decoration-unstable-v1.xml new file mode 100644 index 00000000..023589ad --- /dev/null +++ b/Source/wayland/protocols/xdg-decoration-unstable-v1.xml @@ -0,0 +1,176 @@ + + + + Copyright © 2018 Simon Ser + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + + This interface allows a compositor to announce support for server-side + decorations. + + A window decoration is a set of window controls as deemed appropriate by + the party managing them, such as user interface components used to move, + resize and change a window's state. + + A client can use this protocol to request being decorated by a supporting + compositor. + + If compositor and client do not negotiate the use of a server-side + decoration using this protocol, clients continue to self-decorate as they + see fit. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + + + Destroy the decoration manager. This doesn't destroy objects created + with the manager. + + + + + + Create a new decoration object associated with the given toplevel. + + For objects of version 1, creating an xdg_toplevel_decoration from an + xdg_toplevel which has a buffer attached or committed is a client + error, and any attempts by a client to attach or manipulate a buffer + prior to the first xdg_toplevel_decoration.configure event must also be + treated as errors. + + For objects of version 2 or newer, creating an xdg_toplevel_decoration + from an xdg_toplevel which has a buffer attached or committed is + allowed. The initial decoration mode of the surface if a buffer is + already attached depends on whether a xdg_toplevel_decoration object + has been associated with the surface or not prior to this request. + + If an xdg_toplevel_decoration was associated with the surface, then + destroyed without a surface commit, the previous decoration mode is + retained. + + If no xdg_toplevel_decoration was associated with the surface prior to + this request, or if a surface commit has been performed after a previous + xdg_toplevel_decoration object associated with the surface was + destroyed, the decoration mode is assumed to be client-side. + + + + + + + + + The decoration object allows the compositor to toggle server-side window + decorations for a toplevel surface. The client can request to switch to + another mode. + + The xdg_toplevel_decoration object must be destroyed before its + xdg_toplevel. + + + + + + + + + + + + Switch back to a mode without any server-side decorations at the next + commit, unless a new xdg_toplevel_decoration is created for the surface + first. + + + + + + These values describe window decoration modes. + + + + + + + + Set the toplevel surface decoration mode. This informs the compositor + that the client prefers the provided decoration mode. + + After requesting a decoration mode, the compositor will respond by + emitting an xdg_surface.configure event. The client should then update + its content, drawing it without decorations if the received mode is + server-side decorations. The client must also acknowledge the configure + when committing the new content (see xdg_surface.ack_configure). + + The compositor can decide not to use the client's mode and enforce a + different mode instead. + + Clients whose decoration mode depend on the xdg_toplevel state may send + a set_mode request in response to an xdg_surface.configure event and wait + for the next xdg_surface.configure event to prevent unwanted state. + Such clients are responsible for preventing configure loops and must + make sure not to send multiple successive set_mode requests with the + same decoration mode. + + If an invalid mode is supplied by the client, the invalid_mode protocol + error is raised by the compositor. + + + + + + + Unset the toplevel surface decoration mode. This informs the compositor + that the client doesn't prefer a particular decoration mode. + + This request has the same semantics as set_mode. + + + + + + The configure event configures the effective decoration mode. The + configured state should not be applied immediately. Clients must send an + ack_configure in response to this event. See xdg_surface.configure and + xdg_surface.ack_configure for details. + + A configure event can be sent at any time. The specified mode must be + obeyed by the client. + + + + + diff --git a/Source/wayland/text-input-unstable-v3-protocol.c b/Source/wayland/text-input-unstable-v3-protocol.c new file mode 100644 index 00000000..c723e407 --- /dev/null +++ b/Source/wayland/text-input-unstable-v3-protocol.c @@ -0,0 +1,93 @@ +/* Generated by wayland-scanner 1.24.0 */ + +/* + * Copyright © 2012, 2013 Intel Corporation + * Copyright © 2015, 2016 Jan Arne Petersen + * Copyright © 2017, 2018 Red Hat, Inc. + * Copyright © 2018 Purism SPC + * + * Permission to use, copy, modify, distribute, and sell this + * software and its documentation for any purpose is hereby granted + * without fee, provided that the above copyright notice appear in + * all copies and that both that copyright notice and this permission + * notice appear in supporting documentation, and that the name of + * the copyright holders not be used in advertising or publicity + * pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + * THIS SOFTWARE. + */ + +#include +#include +#include +#include "wayland-util.h" + +extern const struct wl_interface wl_seat_interface; +extern const struct wl_interface wl_surface_interface; +extern const struct wl_interface zwp_text_input_v3_interface; + +static const struct wl_interface *text_input_unstable_v3_types[] = { + NULL, + NULL, + NULL, + NULL, + &wl_surface_interface, + &wl_surface_interface, + &zwp_text_input_v3_interface, + &wl_seat_interface, +}; + +static const struct wl_message zwp_text_input_v3_requests[] = { + { "destroy", "", text_input_unstable_v3_types + 0 }, + { "enable", "", text_input_unstable_v3_types + 0 }, + { "disable", "", text_input_unstable_v3_types + 0 }, + { "set_surrounding_text", "sii", text_input_unstable_v3_types + 0 }, + { "set_text_change_cause", "u", text_input_unstable_v3_types + 0 }, + { "set_content_type", "uu", text_input_unstable_v3_types + 0 }, + { "set_cursor_rectangle", "iiii", text_input_unstable_v3_types + 0 }, + { "commit", "", text_input_unstable_v3_types + 0 }, + { "set_available_actions", "2a", text_input_unstable_v3_types + 0 }, + { "show_input_panel", "2", text_input_unstable_v3_types + 0 }, + { "hide_input_panel", "2", text_input_unstable_v3_types + 0 }, +}; + +static const struct wl_message zwp_text_input_v3_events[] = { + { "enter", "o", text_input_unstable_v3_types + 4 }, + { "leave", "o", text_input_unstable_v3_types + 5 }, + { "preedit_string", "?sii", text_input_unstable_v3_types + 0 }, + { "commit_string", "?s", text_input_unstable_v3_types + 0 }, + { "delete_surrounding_text", "uu", text_input_unstable_v3_types + 0 }, + { "done", "u", text_input_unstable_v3_types + 0 }, + { "action", "2uu", text_input_unstable_v3_types + 0 }, + { "language", "2s", text_input_unstable_v3_types + 0 }, + { "preedit_hint", "2uuu", text_input_unstable_v3_types + 0 }, +}; + +WL_EXPORT const struct wl_interface zwp_text_input_v3_interface = { + "zwp_text_input_v3", 2, + 11, zwp_text_input_v3_requests, + 9, zwp_text_input_v3_events, +}; + +static const struct wl_message zwp_text_input_manager_v3_requests[] = { + { "destroy", "", text_input_unstable_v3_types + 0 }, + { "get_text_input", "no", text_input_unstable_v3_types + 6 }, +}; + +WL_EXPORT const struct wl_interface zwp_text_input_manager_v3_interface = { + "zwp_text_input_manager_v3", 2, + 2, zwp_text_input_manager_v3_requests, + 0, NULL, +}; + diff --git a/Source/wayland/xdg-decoration-unstable-v1-protocol.c b/Source/wayland/xdg-decoration-unstable-v1-protocol.c new file mode 100644 index 00000000..cfac2d1b --- /dev/null +++ b/Source/wayland/xdg-decoration-unstable-v1-protocol.c @@ -0,0 +1,73 @@ +/* + * Copyright © 2018 Simon Ser + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface xdg_toplevel_interface; +extern const struct wl_interface zxdg_toplevel_decoration_v1_interface; + +static const struct wl_interface *xdg_decoration_unstable_v1_types[] = { + NULL, + &zxdg_toplevel_decoration_v1_interface, + &xdg_toplevel_interface, +}; + +static const struct wl_message zxdg_decoration_manager_v1_requests[] = { + { "destroy", "", xdg_decoration_unstable_v1_types + 0 }, + { "get_toplevel_decoration", "no", xdg_decoration_unstable_v1_types + 1 }, +}; + +WL_PRIVATE const struct wl_interface zxdg_decoration_manager_v1_interface = { + "zxdg_decoration_manager_v1", 2, + 2, zxdg_decoration_manager_v1_requests, + 0, NULL, +}; + +static const struct wl_message zxdg_toplevel_decoration_v1_requests[] = { + { "destroy", "", xdg_decoration_unstable_v1_types + 0 }, + { "set_mode", "u", xdg_decoration_unstable_v1_types + 0 }, + { "unset_mode", "", xdg_decoration_unstable_v1_types + 0 }, +}; + +static const struct wl_message zxdg_toplevel_decoration_v1_events[] = { + { "configure", "u", xdg_decoration_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zxdg_toplevel_decoration_v1_interface = { + "zxdg_toplevel_decoration_v1", 2, + 3, zxdg_toplevel_decoration_v1_requests, + 1, zxdg_toplevel_decoration_v1_events, +}; diff --git a/config.h.in b/config.h.in index 18ed752c..56f572b2 100644 --- a/config.h.in +++ b/config.h.in @@ -44,25 +44,25 @@ /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H -/* Define to 1 if you have the `wayland-client' library (-lwayland-client). */ +/* Define to 1 if you have the 'wayland-client' library (-lwayland-client). */ #undef HAVE_LIBWAYLAND_CLIENT -/* Define to 1 if you have the `Xext' library (-lXext). */ +/* Define to 1 if you have the 'Xext' library (-lXext). */ #undef HAVE_LIBXEXT -/* Define to 1 if you have the `Xft' library (-lXft). */ +/* Define to 1 if you have the 'Xft' library (-lXft). */ #undef HAVE_LIBXFT -/* Define to 1 if you have the `xkbcommon' library (-lxkbcommon). */ +/* Define to 1 if you have the 'xkbcommon' library (-lxkbcommon). */ #undef HAVE_LIBXKBCOMMON -/* Define to 1 if you have the `Xmu' library (-lXmu). */ +/* Define to 1 if you have the 'Xmu' library (-lXmu). */ #undef HAVE_LIBXMU -/* Define to 1 if you have the `Xt' library (-lXt). */ +/* Define to 1 if you have the 'Xt' library (-lXt). */ #undef HAVE_LIBXT -/* Define to 1 if you have the `shmctl' function. */ +/* Define to 1 if you have the 'shmctl' function. */ #undef HAVE_SHMCTL /* Define to 1 if you have the header file. */ @@ -80,7 +80,7 @@ /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H -/* Define to 1 if you have the `syslog' function. */ +/* Define to 1 if you have the 'syslog' function. */ #undef HAVE_SYSLOG /* Define to 1 if you have the header file. */ @@ -95,7 +95,7 @@ /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H -/* Define to 1 if you have the `usleep' function. */ +/* Define to 1 if you have the 'usleep' function. */ #undef HAVE_USLEEP /* Define if you have XftDrawStringUtf8 */ @@ -141,7 +141,7 @@ /* Define to enable Xshape support */ #undef HAVE_XSHAPE -/* Define to 1 if you have the `Xutf8LookupString' function. */ +/* Define to 1 if you have the 'Xutf8LookupString' function. */ #undef HAVE_XUTF8LOOKUPSTRING /* Define to the address where bug reports for this package should be sent. */ @@ -162,7 +162,7 @@ /* Define to the version of this package. */ #undef PACKAGE_VERSION -/* Define to 1 if all of the C90 standard headers exist (not just the ones +/* Define to 1 if all of the C89 standard headers exist (not just the ones required in a freestanding environment). This macro is provided for backward compatibility; new code need not use it. */ #undef STDC_HEADERS diff --git a/configure b/configure index 555301dd..09052453 100755 --- a/configure +++ b/configure @@ -1,9 +1,9 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.73. +# Generated by GNU Autoconf 2.72. # # -# Copyright (C) 1992-1996, 1998-2017, 2020-2026 Free Software Foundation, +# Copyright (C) 1992-1996, 1998-2017, 2020-2023 Free Software Foundation, # Inc. # # @@ -135,7 +135,7 @@ case $# in # (( esac # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed 'exec'. -printf '%s\n' "$0: could not re-execute with $CONFIG_SHELL" >&2 +printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi # We don't want this to propagate to other subprocesses. @@ -262,7 +262,7 @@ case $# in # (( esac # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed 'exec'. -printf '%s\n' "$0: could not re-execute with $CONFIG_SHELL" >&2 +printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi @@ -1554,9 +1554,9 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF configure -generated by GNU Autoconf 2.73 +generated by GNU Autoconf 2.72 -Copyright (C) 2026 Free Software Foundation, Inc. +Copyright (C) 2023 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF @@ -1596,7 +1596,7 @@ printf '%s\n' "$ac_try_echo"; } >&5 then : ac_retval=0 else case e in #( - e) printf '%s\n' "$as_me: failed program was:" >&5 + e) printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 ;; @@ -1635,7 +1635,7 @@ printf '%s\n' "$ac_try_echo"; } >&5 then : ac_retval=0 else case e in #( - e) printf '%s\n' "$as_me: failed program was:" >&5 + e) printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 ;; @@ -1713,7 +1713,7 @@ printf '%s\n' "$ac_try_echo"; } >&5 then : ac_retval=0 else case e in #( - e) printf '%s\n' "$as_me: failed program was:" >&5 + e) printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 ;; @@ -1817,7 +1817,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by $as_me, which was -generated by GNU Autoconf 2.73. Invocation command line was +generated by GNU Autoconf 2.72. Invocation command line was $ $0$ac_configure_args_raw @@ -2082,8 +2082,8 @@ esac printf '%s\n' "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ - || { { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 -printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;} + || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See 'config.log' for more details" "$LINENO" 5; } fi @@ -2642,12 +2642,12 @@ for ac_var in $ac_precious_vars; do eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) - { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&5 -printf '%s\n' "$as_me: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&5 +printf "%s\n" "$as_me: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) - { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was not set in the previous run" >&5 -printf '%s\n' "$as_me: error: '$ac_var' was not set in the previous run" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was not set in the previous run" >&5 +printf "%s\n" "$as_me: error: '$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) @@ -2662,18 +2662,18 @@ printf '%s\n' "$as_me: error: '$ac_var' was not set in the previous run" >&2;} ac_new_val_w="$ac_new_val_w $ac_val" done if test "$ac_old_val_w" != "$ac_new_val_w"; then - { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: '$ac_var' has changed since the previous run:" >&5 -printf '%s\n' "$as_me: error: '$ac_var' has changed since the previous run:" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' has changed since the previous run:" >&5 +printf "%s\n" "$as_me: error: '$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else - { printf '%s\n' "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&5 -printf '%s\n' "$as_me: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&5 +printf "%s\n" "$as_me: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi - { printf '%s\n' "$as_me:${as_lineno-$LINENO}: former value: '$ac_old_val'" >&5 -printf '%s\n' "$as_me: former value: '$ac_old_val'" >&2;} - { printf '%s\n' "$as_me:${as_lineno-$LINENO}: current value: '$ac_new_val'" >&5 -printf '%s\n' "$as_me: current value: '$ac_new_val'" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: '$ac_old_val'" >&5 +printf "%s\n" "$as_me: former value: '$ac_old_val'" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: '$ac_new_val'" >&5 +printf "%s\n" "$as_me: current value: '$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. @@ -2689,10 +2689,10 @@ printf '%s\n' "$as_me: current value: '$ac_new_val'" >&2;} fi done if $ac_cache_corrupted; then - { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 -printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;} - { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 -printf '%s\n' "$as_me: error: changes in the environment can compromise the build" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run '${MAKE-make} distclean' and/or 'rm $cache_file' and start over" "$LINENO" 5 fi @@ -3360,8 +3360,8 @@ fi fi -test -z "$CC" && { { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 -printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;} +test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See 'config.log' for more details" "$LINENO" 5; } @@ -3478,13 +3478,13 @@ printf '%s\n' "no" >&6; } printf '%s\n' "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 -{ { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 -printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;} +{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See 'config.log' for more details" "$LINENO" 5; } else case e in #( - e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -printf '%s\n' "yes" >&6; } ;; + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } ;; esac fi { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 @@ -3523,8 +3523,8 @@ for ac_file in conftest.exe conftest conftest.*; do esac done else case e in #( - e) { { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 -printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;} + e) { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See 'config.log' for more details" "$LINENO" 5; } ;; esac @@ -3584,8 +3584,8 @@ printf '%s\n' "$ac_try_echo"; } >&5 if test "$cross_compiling" = maybe; then cross_compiling=yes else - { { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 -printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;} + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot run C compiled programs. If you meant to cross compile, use '--host'. See 'config.log' for more details" "$LINENO" 5; } @@ -3637,11 +3637,11 @@ then : esac done else case e in #( - e) printf '%s\n' "$as_me: failed program was:" >&5 + e) printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 -{ { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 -printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;} +{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See 'config.log' for more details" "$LINENO" 5; } ;; esac @@ -3862,16 +3862,16 @@ fi if test "x$ac_cv_prog_cc_c11" = xno then : - { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -printf '%s\n' "unsupported" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } else case e in #( e) if test "x$ac_cv_prog_cc_c11" = x then : - { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -printf '%s\n' "none needed" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } else case e in #( - e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 -printf '%s\n' "$ac_cv_prog_cc_c11" >&6; } + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 +printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } CC="$CC $ac_cv_prog_cc_c11" ;; esac fi @@ -3911,16 +3911,16 @@ fi if test "x$ac_cv_prog_cc_c99" = xno then : - { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -printf '%s\n' "unsupported" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } else case e in #( e) if test "x$ac_cv_prog_cc_c99" = x then : - { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -printf '%s\n' "none needed" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } else case e in #( - e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 -printf '%s\n' "$ac_cv_prog_cc_c99" >&6; } + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 +printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } CC="$CC $ac_cv_prog_cc_c99" ;; esac fi @@ -3960,16 +3960,16 @@ fi if test "x$ac_cv_prog_cc_c89" = xno then : - { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -printf '%s\n' "unsupported" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } else case e in #( e) if test "x$ac_cv_prog_cc_c89" = x then : - { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -printf '%s\n' "none needed" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } else case e in #( - e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 -printf '%s\n' "$ac_cv_prog_cc_c89" >&6; } + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } CC="$CC $ac_cv_prog_cc_c89" ;; esac fi @@ -4110,8 +4110,8 @@ if $ac_preproc_ok then : else case e in #( - e) { { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 -printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;} + e) { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See 'config.log' for more details" "$LINENO" 5; } ;; esac @@ -4426,7 +4426,7 @@ then : ac_x_libraries= else case e in #( e) LIBS=$ac_save_LIBS -for ac_dir in `printf '%s\n' "$ac_x_includes $ac_x_header_dirs" | sed s/include/lib/g` +for ac_dir in `printf "%s\n" "$ac_x_includes $ac_x_header_dirs" | sed s/include/lib/g` do # Don't even attempt the hair of trying to link an X program! for ac_extension in a so sl dylib la dll; do @@ -4534,8 +4534,8 @@ then : printf '%s\n' "yes" >&6; } X_LIBS="$X_LIBS -R $x_libraries" else case e in #( - e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: neither works" >&5 -printf '%s\n' "neither works" >&6; } ;; + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: neither works" >&5 +printf "%s\n" "neither works" >&6; } ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ @@ -4584,7 +4584,7 @@ if ac_fn_c_try_link "$LINENO" then : else case e in #( - e) { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for dnet_ntoa in -ldnet" >&5 + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dnet_ntoa in -ldnet" >&5 printf %s "checking for dnet_ntoa in -ldnet... " >&6; } if test ${ac_cv_lib_dnet_dnet_ntoa+y} then : @@ -5305,10 +5305,10 @@ Alternatively, you may set the environment variables XEXT_CFLAGS and XEXT_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then - { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf '%s\n' "no" >&6; } - { { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 -printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. @@ -5441,10 +5441,10 @@ Alternatively, you may set the environment variables XT_CFLAGS and XT_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then - { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf '%s\n' "no" >&6; } - { { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 -printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. @@ -5577,10 +5577,10 @@ Alternatively, you may set the environment variables XMU_CFLAGS and XMU_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then - { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf '%s\n' "no" >&6; } - { { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 -printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. @@ -5864,7 +5864,7 @@ fi # Old X11 support { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for X11 function prototypes" >&5 printf %s "checking for X11 function prototypes... " >&6; } -{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for egrep -e" >&5 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for egrep -e" >&5 printf %s "checking for egrep -e... " >&6; } if test ${ac_cv_path_EGREP_TRADITIONAL+y} then : @@ -5901,7 +5901,7 @@ case `"$ac_path_EGREP_TRADITIONAL" --version 2>&1` in #( cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" - printf '%s\n' 'EGREP_TRADITIONAL' >> "conftest.nl" + printf "%s\n" 'EGREP_TRADITIONAL' >> "conftest.nl" "$ac_path_EGREP_TRADITIONAL" -E 'EGR(EP|AC)_TRADITIONAL$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val @@ -5963,7 +5963,7 @@ case `"$ac_path_EGREP_TRADITIONAL" --version 2>&1` in #( cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" - printf '%s\n' 'EGREP_TRADITIONAL' >> "conftest.nl" + printf "%s\n" 'EGREP_TRADITIONAL' >> "conftest.nl" "$ac_path_EGREP_TRADITIONAL" 'EGR(EP|AC)_TRADITIONAL$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val @@ -5994,8 +5994,8 @@ esac fi ;; esac fi -{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP_TRADITIONAL" >&5 -printf '%s\n' "$ac_cv_path_EGREP_TRADITIONAL" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP_TRADITIONAL" >&5 +printf "%s\n" "$ac_cv_path_EGREP_TRADITIONAL" >&6; } EGREP_TRADITIONAL=$ac_cv_path_EGREP_TRADITIONAL cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -6136,10 +6136,10 @@ Alternatively, you may set the environment variables FREETYPE_CFLAGS and FREETYPE_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then - { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf '%s\n' "no" >&6; } - { { printf '%s\n' "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 -printf '%s\n' "$as_me: error: in '$ac_pwd':" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. @@ -8089,7 +8089,280 @@ printf '%s\n' "$as_me: WARNING: can't find freetype, required for graphics=cairo if test $BUILD_SERVER = win32; then BUILD_GRAPHICS=winlib elif test $BUILD_SERVER = wayland; then - as_fn_error $? "wayland backend requires cairo" "$LINENO" 5 + for ac_header in wayland-util.h +do : + ac_fn_c_check_header_compile "$LINENO" "wayland-util.h" "ac_cv_header_wayland_util_h" "$ac_includes_default" +if test "x$ac_cv_header_wayland_util_h" = xyes +then : + printf "%s\n" "#define HAVE_WAYLAND_UTIL_H 1" >>confdefs.h + +else case e in #( + e) as_fn_error $? "**** No wayland-util.h. Install libwayland-dev or equivalent." "$LINENO" 5 ;; +esac +fi + +done + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for wl_display_flush in -lwayland-client" >&5 +printf %s "checking for wl_display_flush in -lwayland-client... " >&6; } +if test ${ac_cv_lib_wayland_client_wl_display_flush+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS +LIBS="-lwayland-client $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char wl_display_flush (void); +int +main (void) +{ +return wl_display_flush (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_lib_wayland_client_wl_display_flush=yes +else case e in #( + e) ac_cv_lib_wayland_client_wl_display_flush=no ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_wayland_client_wl_display_flush" >&5 +printf "%s\n" "$ac_cv_lib_wayland_client_wl_display_flush" >&6; } +if test "x$ac_cv_lib_wayland_client_wl_display_flush" = xyes +then : + printf "%s\n" "#define HAVE_LIBWAYLAND_CLIENT 1" >>confdefs.h + + LIBS="-lwayland-client $LIBS" + +else case e in #( + e) as_fn_error $? "**** No wl_display_flush in libwayland-client. Install correct version of libwayland-dev or equivalent." "$LINENO" 5 ;; +esac +fi + + for ac_header in xkbcommon/xkbcommon.h +do : + ac_fn_c_check_header_compile "$LINENO" "xkbcommon/xkbcommon.h" "ac_cv_header_xkbcommon_xkbcommon_h" "$ac_includes_default" +if test "x$ac_cv_header_xkbcommon_xkbcommon_h" = xyes +then : + printf "%s\n" "#define HAVE_XKBCOMMON_XKBCOMMON_H 1" >>confdefs.h + +else case e in #( + e) as_fn_error $? "**** No xkbcommon/xkbcommon.h. Required for wayland. Install libxkbcommon-dev or equivalent." "$LINENO" 5 ;; +esac +fi + +done + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for xkb_context_new in -lxkbcommon" >&5 +printf %s "checking for xkb_context_new in -lxkbcommon... " >&6; } +if test ${ac_cv_lib_xkbcommon_xkb_context_new+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS +LIBS="-lxkbcommon $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char xkb_context_new (void); +int +main (void) +{ +return xkb_context_new (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_lib_xkbcommon_xkb_context_new=yes +else case e in #( + e) ac_cv_lib_xkbcommon_xkb_context_new=no ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_xkbcommon_xkb_context_new" >&5 +printf "%s\n" "$ac_cv_lib_xkbcommon_xkb_context_new" >&6; } +if test "x$ac_cv_lib_xkbcommon_xkb_context_new" = xyes +then : + printf "%s\n" "#define HAVE_LIBXKBCOMMON 1" >>confdefs.h + + LIBS="-lxkbcommon $LIBS" + +else case e in #( + e) as_fn_error $? "**** No xkb_context_new in libxkbcommon. Install correct version of libxkbcommon-dev or equivalent." "$LINENO" 5 ;; +esac +fi + + + for ac_header in EGL/egl.h +do : + ac_fn_c_check_header_compile "$LINENO" "EGL/egl.h" "ac_cv_header_EGL_egl_h" "$ac_includes_default" +if test "x$ac_cv_header_EGL_egl_h" = xyes +then : + printf "%s\n" "#define HAVE_EGL_EGL_H 1" >>confdefs.h + +else case e in #( + e) as_fn_error $? "**** No EGL/egl.h. Required for wayland. Install libegl-dev or equivalent." "$LINENO" 5 ;; +esac +fi + +done + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for eglGetDisplay in -lEGL" >&5 +printf %s "checking for eglGetDisplay in -lEGL... " >&6; } +if test ${ac_cv_lib_EGL_eglGetDisplay+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS +LIBS="-lEGL $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char eglGetDisplay (void); +int +main (void) +{ +return eglGetDisplay (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_lib_EGL_eglGetDisplay=yes +else case e in #( + e) ac_cv_lib_EGL_eglGetDisplay=no ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_EGL_eglGetDisplay" >&5 +printf "%s\n" "$ac_cv_lib_EGL_eglGetDisplay" >&6; } +if test "x$ac_cv_lib_EGL_eglGetDisplay" = xyes +then : + printf "%s\n" "#define HAVE_LIBEGL 1" >>confdefs.h + + LIBS="-lEGL $LIBS" + +else case e in #( + e) as_fn_error $? "**** No eglGetDisplay in libEGL. Install correct version of libegl-dev or equivalent." "$LINENO" 5 ;; +esac +fi + + + for ac_header in GLES2/gl2.h +do : + ac_fn_c_check_header_compile "$LINENO" "GLES2/gl2.h" "ac_cv_header_GLES2_gl2_h" "$ac_includes_default" +if test "x$ac_cv_header_GLES2_gl2_h" = xyes +then : + printf "%s\n" "#define HAVE_GLES2_GL2_H 1" >>confdefs.h + +else case e in #( + e) as_fn_error $? "**** No GLES2/gl2.h. Required for wayland. Install libgles2-mesa-dev or equivalent." "$LINENO" 5 ;; +esac +fi + +done + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for glClear in -lGLESv2" >&5 +printf %s "checking for glClear in -lGLESv2... " >&6; } +if test ${ac_cv_lib_GLESv2_glClear+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS +LIBS="-lGLESv2 $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char glClear (void); +int +main (void) +{ +return glClear (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_lib_GLESv2_glClear=yes +else case e in #( + e) ac_cv_lib_GLESv2_glClear=no ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_GLESv2_glClear" >&5 +printf "%s\n" "$ac_cv_lib_GLESv2_glClear" >&6; } +if test "x$ac_cv_lib_GLESv2_glClear" = xyes +then : + printf "%s\n" "#define HAVE_LIBGLESV2 1" >>confdefs.h + + LIBS="-lGLESv2 $LIBS" + +else case e in #( + e) as_fn_error $? "**** No glClear in libGLESv2. Install correct version of libgles2-mesa-dev or equivalent." "$LINENO" 5 ;; +esac +fi + + + CAIRO_LIBS="$CAIRO_LIBS $XFT_LIBS -lEGL -lGLESv2" + CAIRO_CFLAGS="$CAIRO_CFLAGS" + LIBS="-lwayland-client -lwayland-cursor -lwayland-egl -lxkbcommon $LIBS" else BUILD_GRAPHICS=xlib fi @@ -8506,7 +8779,44 @@ cat >confcache <<\_ACEOF _ACEOF -ac_cache_dump | +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # 'set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # 'set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | sed ' /^ac_cv_env_/b end t clear @@ -8941,7 +9251,7 @@ cat >>"$CONFIG_STATUS" <<\_ACEOF || ac_write_fail=1 # values after options handling. ac_log=" This file was extended by $as_me, which was -generated by GNU Autoconf 2.73. Invocation command line was +generated by GNU Autoconf 2.72. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS @@ -9005,10 +9315,10 @@ cat >>"$CONFIG_STATUS" <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ config.status -configured by $0, generated by GNU Autoconf 2.73, +configured by $0, generated by GNU Autoconf 2.72, with options \\"\$ac_cs_config\\" -Copyright (C) 2026 Free Software Foundation, Inc. +Copyright (C) 2023 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." @@ -9641,9 +9951,9 @@ test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && - { printf '%s\n' "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable 'datarootdir' + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable 'datarootdir' which seems to be undefined. Please make sure it is defined" >&5 -printf '%s\n' "$as_me: WARNING: $ac_file contains a reference to the variable 'datarootdir' +printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable 'datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" diff --git a/configure.ac b/configure.ac index 7742d8df..f76898eb 100644 --- a/configure.ac +++ b/configure.ac @@ -26,7 +26,7 @@ builtin(include, pkg.m4)dnl AC_INIT -AC_PREREQ([2.73]) +AC_PREREQ([2.72]) AC_CONFIG_SRCDIR([back.make.in]) AC_CONFIG_HEADERS([config.h])