add basic plugin capability
This commit is contained in:
parent
8c21412b54
commit
f17efc3da8
79
m4/build-to-host.m4
Normal file
79
m4/build-to-host.m4
Normal file
@ -0,0 +1,79 @@
|
||||
# build-to-host.m4 serial 3
|
||||
dnl Copyright (C) 2023-2024 Free Software Foundation, Inc.
|
||||
dnl This file is free software; the Free Software Foundation
|
||||
dnl gives unlimited permission to copy and/or distribute it,
|
||||
dnl with or without modifications, as long as this notice is preserved.
|
||||
|
||||
dnl Written by Bruno Haible.
|
||||
|
||||
dnl When the build environment ($build_os) is different from the target runtime
|
||||
dnl environment ($host_os), file names may need to be converted from the build
|
||||
dnl environment syntax to the target runtime environment syntax. This is
|
||||
dnl because the Makefiles are executed (mostly) by build environment tools and
|
||||
dnl therefore expect file names in build environment syntax, whereas the runtime
|
||||
dnl expects file names in target runtime environment syntax.
|
||||
dnl
|
||||
dnl For example, if $build_os = cygwin and $host_os = mingw32, filenames need
|
||||
dnl be converted from Cygwin syntax to native Windows syntax:
|
||||
dnl /cygdrive/c/foo/bar -> C:\foo\bar
|
||||
dnl /usr/local/share -> C:\cygwin64\usr\local\share
|
||||
dnl
|
||||
dnl gl_BUILD_TO_HOST([somedir])
|
||||
dnl This macro takes as input an AC_SUBSTed variable 'somedir', which must
|
||||
dnl already have its final value assigned, and produces two additional
|
||||
dnl AC_SUBSTed variables 'somedir_c' and 'somedir_c_make', that designate the
|
||||
dnl same file name value, just in different syntax:
|
||||
dnl - somedir_c is the file name in target runtime environment syntax,
|
||||
dnl as a C string (starting and ending with a double-quote,
|
||||
dnl and with escaped backslashes and double-quotes in
|
||||
dnl between).
|
||||
dnl - somedir_c_make is the same thing, escaped for use in a Makefile.
|
||||
|
||||
AC_DEFUN([gl_BUILD_TO_HOST],
|
||||
[
|
||||
AC_REQUIRE([AC_CANONICAL_BUILD])
|
||||
AC_REQUIRE([AC_CANONICAL_HOST])
|
||||
AC_REQUIRE([gl_BUILD_TO_HOST_INIT])
|
||||
|
||||
dnl Define somedir_c.
|
||||
gl_final_[$1]="$[$1]"
|
||||
dnl Translate it from build syntax to host syntax.
|
||||
case "$build_os" in
|
||||
cygwin*)
|
||||
case "$host_os" in
|
||||
mingw* | windows*)
|
||||
gl_final_[$1]=`cygpath -w "$gl_final_[$1]"` ;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
dnl Convert it to C string syntax.
|
||||
[$1]_c=`printf '%s\n' "$gl_final_[$1]" | sed -e "$gl_sed_double_backslashes" -e "$gl_sed_escape_doublequotes" | tr -d "$gl_tr_cr"`
|
||||
[$1]_c='"'"$[$1]_c"'"'
|
||||
AC_SUBST([$1_c])
|
||||
|
||||
dnl Define somedir_c_make.
|
||||
[$1]_c_make=`printf '%s\n' "$[$1]_c" | sed -e "$gl_sed_escape_for_make_1" -e "$gl_sed_escape_for_make_2" | tr -d "$gl_tr_cr"`
|
||||
dnl Use the substituted somedir variable, when possible, so that the user
|
||||
dnl may adjust somedir a posteriori when there are no special characters.
|
||||
if test "$[$1]_c_make" = '\"'"${gl_final_[$1]}"'\"'; then
|
||||
[$1]_c_make='\"$([$1])\"'
|
||||
fi
|
||||
AC_SUBST([$1_c_make])
|
||||
])
|
||||
|
||||
dnl Some initializations for gl_BUILD_TO_HOST.
|
||||
AC_DEFUN([gl_BUILD_TO_HOST_INIT],
|
||||
[
|
||||
gl_sed_double_backslashes='s/\\/\\\\/g'
|
||||
gl_sed_escape_doublequotes='s/"/\\"/g'
|
||||
changequote(,)dnl
|
||||
gl_sed_escape_for_make_1="s,\\([ \"&'();<>\\\\\`|]\\),\\\\\\1,g"
|
||||
changequote([,])dnl
|
||||
gl_sed_escape_for_make_2='s,\$,\\$$,g'
|
||||
dnl Find out how to remove carriage returns from output. Solaris /usr/ucb/tr
|
||||
dnl does not understand '\r'.
|
||||
case `echo r | tr -d '\r'` in
|
||||
'') gl_tr_cr='\015' ;;
|
||||
*) gl_tr_cr='\r' ;;
|
||||
esac
|
||||
])
|
527
m4/host-cpu-c-abi.m4
Normal file
527
m4/host-cpu-c-abi.m4
Normal file
@ -0,0 +1,527 @@
|
||||
# host-cpu-c-abi.m4 serial 17
|
||||
dnl Copyright (C) 2002-2024 Free Software Foundation, Inc.
|
||||
dnl This file is free software; the Free Software Foundation
|
||||
dnl gives unlimited permission to copy and/or distribute it,
|
||||
dnl with or without modifications, as long as this notice is preserved.
|
||||
|
||||
dnl From Bruno Haible and Sam Steingold.
|
||||
|
||||
dnl Sets the HOST_CPU variable to the canonical name of the CPU.
|
||||
dnl Sets the HOST_CPU_C_ABI variable to the canonical name of the CPU with its
|
||||
dnl C language ABI (application binary interface).
|
||||
dnl Also defines __${HOST_CPU}__ and __${HOST_CPU_C_ABI}__ as C macros in
|
||||
dnl config.h.
|
||||
dnl
|
||||
dnl This canonical name can be used to select a particular assembly language
|
||||
dnl source file that will interoperate with C code on the given host.
|
||||
dnl
|
||||
dnl For example:
|
||||
dnl * 'i386' and 'sparc' are different canonical names, because code for i386
|
||||
dnl will not run on SPARC CPUs and vice versa. They have different
|
||||
dnl instruction sets.
|
||||
dnl * 'sparc' and 'sparc64' are different canonical names, because code for
|
||||
dnl 'sparc' and code for 'sparc64' cannot be linked together: 'sparc' code
|
||||
dnl contains 32-bit instructions, whereas 'sparc64' code contains 64-bit
|
||||
dnl instructions. A process on a SPARC CPU can be in 32-bit mode or in 64-bit
|
||||
dnl mode, but not both.
|
||||
dnl * 'mips' and 'mipsn32' are different canonical names, because they use
|
||||
dnl different argument passing and return conventions for C functions, and
|
||||
dnl although the instruction set of 'mips' is a large subset of the
|
||||
dnl instruction set of 'mipsn32'.
|
||||
dnl * 'mipsn32' and 'mips64' are different canonical names, because they use
|
||||
dnl different sizes for the C types like 'int' and 'void *', and although
|
||||
dnl the instruction sets of 'mipsn32' and 'mips64' are the same.
|
||||
dnl * The same canonical name is used for different endiannesses. You can
|
||||
dnl determine the endianness through preprocessor symbols:
|
||||
dnl - 'arm': test __ARMEL__.
|
||||
dnl - 'mips', 'mipsn32', 'mips64': test _MIPSEB vs. _MIPSEL.
|
||||
dnl - 'powerpc64': test _BIG_ENDIAN vs. _LITTLE_ENDIAN.
|
||||
dnl * The same name 'i386' is used for CPUs of type i386, i486, i586
|
||||
dnl (Pentium), AMD K7, Pentium II, Pentium IV, etc., because
|
||||
dnl - Instructions that do not exist on all of these CPUs (cmpxchg,
|
||||
dnl MMX, SSE, SSE2, 3DNow! etc.) are not frequently used. If your
|
||||
dnl assembly language source files use such instructions, you will
|
||||
dnl need to make the distinction.
|
||||
dnl - Speed of execution of the common instruction set is reasonable across
|
||||
dnl the entire family of CPUs. If you have assembly language source files
|
||||
dnl that are optimized for particular CPU types (like GNU gmp has), you
|
||||
dnl will need to make the distinction.
|
||||
dnl See <https://en.wikipedia.org/wiki/X86_instruction_listings>.
|
||||
AC_DEFUN([gl_HOST_CPU_C_ABI],
|
||||
[
|
||||
AC_REQUIRE([AC_CANONICAL_HOST])
|
||||
AC_REQUIRE([gl_C_ASM])
|
||||
AC_CACHE_CHECK([host CPU and C ABI], [gl_cv_host_cpu_c_abi],
|
||||
[case "$host_cpu" in
|
||||
|
||||
changequote(,)dnl
|
||||
i[34567]86 )
|
||||
changequote([,])dnl
|
||||
gl_cv_host_cpu_c_abi=i386
|
||||
;;
|
||||
|
||||
x86_64 )
|
||||
# On x86_64 systems, the C compiler may be generating code in one of
|
||||
# these ABIs:
|
||||
# - 64-bit instruction set, 64-bit pointers, 64-bit 'long': x86_64.
|
||||
# - 64-bit instruction set, 64-bit pointers, 32-bit 'long': x86_64
|
||||
# with native Windows (mingw, MSVC).
|
||||
# - 64-bit instruction set, 32-bit pointers, 32-bit 'long': x86_64-x32.
|
||||
# - 32-bit instruction set, 32-bit pointers, 32-bit 'long': i386.
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_SOURCE(
|
||||
[[#if (defined __x86_64__ || defined __amd64__ \
|
||||
|| defined _M_X64 || defined _M_AMD64)
|
||||
int ok;
|
||||
#else
|
||||
error fail
|
||||
#endif
|
||||
]])],
|
||||
[AC_COMPILE_IFELSE(
|
||||
[AC_LANG_SOURCE(
|
||||
[[#if defined __ILP32__ || defined _ILP32
|
||||
int ok;
|
||||
#else
|
||||
error fail
|
||||
#endif
|
||||
]])],
|
||||
[gl_cv_host_cpu_c_abi=x86_64-x32],
|
||||
[gl_cv_host_cpu_c_abi=x86_64])],
|
||||
[gl_cv_host_cpu_c_abi=i386])
|
||||
;;
|
||||
|
||||
changequote(,)dnl
|
||||
alphaev[4-8] | alphaev56 | alphapca5[67] | alphaev6[78] )
|
||||
changequote([,])dnl
|
||||
gl_cv_host_cpu_c_abi=alpha
|
||||
;;
|
||||
|
||||
arm* | aarch64 )
|
||||
# Assume arm with EABI.
|
||||
# On arm64 systems, the C compiler may be generating code in one of
|
||||
# these ABIs:
|
||||
# - aarch64 instruction set, 64-bit pointers, 64-bit 'long': arm64.
|
||||
# - aarch64 instruction set, 32-bit pointers, 32-bit 'long': arm64-ilp32.
|
||||
# - 32-bit instruction set, 32-bit pointers, 32-bit 'long': arm or armhf.
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_SOURCE(
|
||||
[[#ifdef __aarch64__
|
||||
int ok;
|
||||
#else
|
||||
error fail
|
||||
#endif
|
||||
]])],
|
||||
[AC_COMPILE_IFELSE(
|
||||
[AC_LANG_SOURCE(
|
||||
[[#if defined __ILP32__ || defined _ILP32
|
||||
int ok;
|
||||
#else
|
||||
error fail
|
||||
#endif
|
||||
]])],
|
||||
[gl_cv_host_cpu_c_abi=arm64-ilp32],
|
||||
[gl_cv_host_cpu_c_abi=arm64])],
|
||||
[# Don't distinguish little-endian and big-endian arm, since they
|
||||
# don't require different machine code for simple operations and
|
||||
# since the user can distinguish them through the preprocessor
|
||||
# defines __ARMEL__ vs. __ARMEB__.
|
||||
# But distinguish arm which passes floating-point arguments and
|
||||
# return values in integer registers (r0, r1, ...) - this is
|
||||
# gcc -mfloat-abi=soft or gcc -mfloat-abi=softfp - from arm which
|
||||
# passes them in float registers (s0, s1, ...) and double registers
|
||||
# (d0, d1, ...) - this is gcc -mfloat-abi=hard. GCC 4.6 or newer
|
||||
# sets the preprocessor defines __ARM_PCS (for the first case) and
|
||||
# __ARM_PCS_VFP (for the second case), but older GCC does not.
|
||||
echo 'double ddd; void func (double dd) { ddd = dd; }' > conftest.c
|
||||
# Look for a reference to the register d0 in the .s file.
|
||||
AC_TRY_COMMAND(${CC-cc} $CFLAGS $CPPFLAGS $gl_c_asm_opt conftest.c) >/dev/null 2>&1
|
||||
if LC_ALL=C grep 'd0,' conftest.$gl_asmext >/dev/null; then
|
||||
gl_cv_host_cpu_c_abi=armhf
|
||||
else
|
||||
gl_cv_host_cpu_c_abi=arm
|
||||
fi
|
||||
rm -f conftest*
|
||||
])
|
||||
;;
|
||||
|
||||
hppa1.0 | hppa1.1 | hppa2.0* | hppa64 )
|
||||
# On hppa, the C compiler may be generating 32-bit code or 64-bit
|
||||
# code. In the latter case, it defines _LP64 and __LP64__.
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_SOURCE(
|
||||
[[#ifdef __LP64__
|
||||
int ok;
|
||||
#else
|
||||
error fail
|
||||
#endif
|
||||
]])],
|
||||
[gl_cv_host_cpu_c_abi=hppa64],
|
||||
[gl_cv_host_cpu_c_abi=hppa])
|
||||
;;
|
||||
|
||||
ia64* )
|
||||
# On ia64 on HP-UX, the C compiler may be generating 64-bit code or
|
||||
# 32-bit code. In the latter case, it defines _ILP32.
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_SOURCE(
|
||||
[[#ifdef _ILP32
|
||||
int ok;
|
||||
#else
|
||||
error fail
|
||||
#endif
|
||||
]])],
|
||||
[gl_cv_host_cpu_c_abi=ia64-ilp32],
|
||||
[gl_cv_host_cpu_c_abi=ia64])
|
||||
;;
|
||||
|
||||
mips* )
|
||||
# We should also check for (_MIPS_SZPTR == 64), but gcc keeps this
|
||||
# at 32.
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_SOURCE(
|
||||
[[#if defined _MIPS_SZLONG && (_MIPS_SZLONG == 64)
|
||||
int ok;
|
||||
#else
|
||||
error fail
|
||||
#endif
|
||||
]])],
|
||||
[gl_cv_host_cpu_c_abi=mips64],
|
||||
[# In the n32 ABI, _ABIN32 is defined, _ABIO32 is not defined (but
|
||||
# may later get defined by <sgidefs.h>), and _MIPS_SIM == _ABIN32.
|
||||
# In the 32 ABI, _ABIO32 is defined, _ABIN32 is not defined (but
|
||||
# may later get defined by <sgidefs.h>), and _MIPS_SIM == _ABIO32.
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_SOURCE(
|
||||
[[#if (_MIPS_SIM == _ABIN32)
|
||||
int ok;
|
||||
#else
|
||||
error fail
|
||||
#endif
|
||||
]])],
|
||||
[gl_cv_host_cpu_c_abi=mipsn32],
|
||||
[gl_cv_host_cpu_c_abi=mips])])
|
||||
;;
|
||||
|
||||
powerpc* )
|
||||
# Different ABIs are in use on AIX vs. Mac OS X vs. Linux,*BSD.
|
||||
# No need to distinguish them here; the caller may distinguish
|
||||
# them based on the OS.
|
||||
# On powerpc64 systems, the C compiler may still be generating
|
||||
# 32-bit code. And on powerpc-ibm-aix systems, the C compiler may
|
||||
# be generating 64-bit code.
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_SOURCE(
|
||||
[[#if defined __powerpc64__ || defined __LP64__
|
||||
int ok;
|
||||
#else
|
||||
error fail
|
||||
#endif
|
||||
]])],
|
||||
[# On powerpc64, there are two ABIs on Linux: The AIX compatible
|
||||
# one and the ELFv2 one. The latter defines _CALL_ELF=2.
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_SOURCE(
|
||||
[[#if defined _CALL_ELF && _CALL_ELF == 2
|
||||
int ok;
|
||||
#else
|
||||
error fail
|
||||
#endif
|
||||
]])],
|
||||
[gl_cv_host_cpu_c_abi=powerpc64-elfv2],
|
||||
[gl_cv_host_cpu_c_abi=powerpc64])
|
||||
],
|
||||
[gl_cv_host_cpu_c_abi=powerpc])
|
||||
;;
|
||||
|
||||
rs6000 )
|
||||
gl_cv_host_cpu_c_abi=powerpc
|
||||
;;
|
||||
|
||||
riscv32 | riscv64 )
|
||||
# There are 2 architectures (with variants): rv32* and rv64*.
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_SOURCE(
|
||||
[[#if __riscv_xlen == 64
|
||||
int ok;
|
||||
#else
|
||||
error fail
|
||||
#endif
|
||||
]])],
|
||||
[cpu=riscv64],
|
||||
[cpu=riscv32])
|
||||
# There are 6 ABIs: ilp32, ilp32f, ilp32d, lp64, lp64f, lp64d.
|
||||
# Size of 'long' and 'void *':
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_SOURCE(
|
||||
[[#if defined __LP64__
|
||||
int ok;
|
||||
#else
|
||||
error fail
|
||||
#endif
|
||||
]])],
|
||||
[main_abi=lp64],
|
||||
[main_abi=ilp32])
|
||||
# Float ABIs:
|
||||
# __riscv_float_abi_double:
|
||||
# 'float' and 'double' are passed in floating-point registers.
|
||||
# __riscv_float_abi_single:
|
||||
# 'float' are passed in floating-point registers.
|
||||
# __riscv_float_abi_soft:
|
||||
# No values are passed in floating-point registers.
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_SOURCE(
|
||||
[[#if defined __riscv_float_abi_double
|
||||
int ok;
|
||||
#else
|
||||
error fail
|
||||
#endif
|
||||
]])],
|
||||
[float_abi=d],
|
||||
[AC_COMPILE_IFELSE(
|
||||
[AC_LANG_SOURCE(
|
||||
[[#if defined __riscv_float_abi_single
|
||||
int ok;
|
||||
#else
|
||||
error fail
|
||||
#endif
|
||||
]])],
|
||||
[float_abi=f],
|
||||
[float_abi=''])
|
||||
])
|
||||
gl_cv_host_cpu_c_abi="${cpu}-${main_abi}${float_abi}"
|
||||
;;
|
||||
|
||||
s390* )
|
||||
# On s390x, the C compiler may be generating 64-bit (= s390x) code
|
||||
# or 31-bit (= s390) code.
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_SOURCE(
|
||||
[[#if defined __LP64__ || defined __s390x__
|
||||
int ok;
|
||||
#else
|
||||
error fail
|
||||
#endif
|
||||
]])],
|
||||
[gl_cv_host_cpu_c_abi=s390x],
|
||||
[gl_cv_host_cpu_c_abi=s390])
|
||||
;;
|
||||
|
||||
sparc | sparc64 )
|
||||
# UltraSPARCs running Linux have `uname -m` = "sparc64", but the
|
||||
# C compiler still generates 32-bit code.
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_SOURCE(
|
||||
[[#if defined __sparcv9 || defined __arch64__
|
||||
int ok;
|
||||
#else
|
||||
error fail
|
||||
#endif
|
||||
]])],
|
||||
[gl_cv_host_cpu_c_abi=sparc64],
|
||||
[gl_cv_host_cpu_c_abi=sparc])
|
||||
;;
|
||||
|
||||
*)
|
||||
gl_cv_host_cpu_c_abi="$host_cpu"
|
||||
;;
|
||||
esac
|
||||
])
|
||||
|
||||
dnl In most cases, $HOST_CPU and $HOST_CPU_C_ABI are the same.
|
||||
HOST_CPU=`echo "$gl_cv_host_cpu_c_abi" | sed -e 's/-.*//'`
|
||||
HOST_CPU_C_ABI="$gl_cv_host_cpu_c_abi"
|
||||
AC_SUBST([HOST_CPU])
|
||||
AC_SUBST([HOST_CPU_C_ABI])
|
||||
|
||||
# This was
|
||||
# AC_DEFINE_UNQUOTED([__${HOST_CPU}__])
|
||||
# AC_DEFINE_UNQUOTED([__${HOST_CPU_C_ABI}__])
|
||||
# earlier, but KAI C++ 3.2d doesn't like this.
|
||||
sed -e 's/-/_/g' >> confdefs.h <<EOF
|
||||
#ifndef __${HOST_CPU}__
|
||||
#define __${HOST_CPU}__ 1
|
||||
#endif
|
||||
#ifndef __${HOST_CPU_C_ABI}__
|
||||
#define __${HOST_CPU_C_ABI}__ 1
|
||||
#endif
|
||||
EOF
|
||||
AH_TOP([/* CPU and C ABI indicator */
|
||||
#ifndef __i386__
|
||||
#undef __i386__
|
||||
#endif
|
||||
#ifndef __x86_64_x32__
|
||||
#undef __x86_64_x32__
|
||||
#endif
|
||||
#ifndef __x86_64__
|
||||
#undef __x86_64__
|
||||
#endif
|
||||
#ifndef __alpha__
|
||||
#undef __alpha__
|
||||
#endif
|
||||
#ifndef __arm__
|
||||
#undef __arm__
|
||||
#endif
|
||||
#ifndef __armhf__
|
||||
#undef __armhf__
|
||||
#endif
|
||||
#ifndef __arm64_ilp32__
|
||||
#undef __arm64_ilp32__
|
||||
#endif
|
||||
#ifndef __arm64__
|
||||
#undef __arm64__
|
||||
#endif
|
||||
#ifndef __hppa__
|
||||
#undef __hppa__
|
||||
#endif
|
||||
#ifndef __hppa64__
|
||||
#undef __hppa64__
|
||||
#endif
|
||||
#ifndef __ia64_ilp32__
|
||||
#undef __ia64_ilp32__
|
||||
#endif
|
||||
#ifndef __ia64__
|
||||
#undef __ia64__
|
||||
#endif
|
||||
#ifndef __loongarch64__
|
||||
#undef __loongarch64__
|
||||
#endif
|
||||
#ifndef __m68k__
|
||||
#undef __m68k__
|
||||
#endif
|
||||
#ifndef __mips__
|
||||
#undef __mips__
|
||||
#endif
|
||||
#ifndef __mipsn32__
|
||||
#undef __mipsn32__
|
||||
#endif
|
||||
#ifndef __mips64__
|
||||
#undef __mips64__
|
||||
#endif
|
||||
#ifndef __powerpc__
|
||||
#undef __powerpc__
|
||||
#endif
|
||||
#ifndef __powerpc64__
|
||||
#undef __powerpc64__
|
||||
#endif
|
||||
#ifndef __powerpc64_elfv2__
|
||||
#undef __powerpc64_elfv2__
|
||||
#endif
|
||||
#ifndef __riscv32__
|
||||
#undef __riscv32__
|
||||
#endif
|
||||
#ifndef __riscv64__
|
||||
#undef __riscv64__
|
||||
#endif
|
||||
#ifndef __riscv32_ilp32__
|
||||
#undef __riscv32_ilp32__
|
||||
#endif
|
||||
#ifndef __riscv32_ilp32f__
|
||||
#undef __riscv32_ilp32f__
|
||||
#endif
|
||||
#ifndef __riscv32_ilp32d__
|
||||
#undef __riscv32_ilp32d__
|
||||
#endif
|
||||
#ifndef __riscv64_ilp32__
|
||||
#undef __riscv64_ilp32__
|
||||
#endif
|
||||
#ifndef __riscv64_ilp32f__
|
||||
#undef __riscv64_ilp32f__
|
||||
#endif
|
||||
#ifndef __riscv64_ilp32d__
|
||||
#undef __riscv64_ilp32d__
|
||||
#endif
|
||||
#ifndef __riscv64_lp64__
|
||||
#undef __riscv64_lp64__
|
||||
#endif
|
||||
#ifndef __riscv64_lp64f__
|
||||
#undef __riscv64_lp64f__
|
||||
#endif
|
||||
#ifndef __riscv64_lp64d__
|
||||
#undef __riscv64_lp64d__
|
||||
#endif
|
||||
#ifndef __s390__
|
||||
#undef __s390__
|
||||
#endif
|
||||
#ifndef __s390x__
|
||||
#undef __s390x__
|
||||
#endif
|
||||
#ifndef __sh__
|
||||
#undef __sh__
|
||||
#endif
|
||||
#ifndef __sparc__
|
||||
#undef __sparc__
|
||||
#endif
|
||||
#ifndef __sparc64__
|
||||
#undef __sparc64__
|
||||
#endif
|
||||
])
|
||||
|
||||
])
|
||||
|
||||
|
||||
dnl Sets the HOST_CPU_C_ABI_32BIT variable to 'yes' if the C language ABI
|
||||
dnl (application binary interface) is a 32-bit one, to 'no' if it is a 64-bit
|
||||
dnl one.
|
||||
dnl This is a simplified variant of gl_HOST_CPU_C_ABI.
|
||||
AC_DEFUN([gl_HOST_CPU_C_ABI_32BIT],
|
||||
[
|
||||
AC_REQUIRE([AC_CANONICAL_HOST])
|
||||
AC_CACHE_CHECK([32-bit host C ABI], [gl_cv_host_cpu_c_abi_32bit],
|
||||
[case "$host_cpu" in
|
||||
|
||||
# CPUs that only support a 32-bit ABI.
|
||||
arc \
|
||||
| bfin \
|
||||
| cris* \
|
||||
| csky \
|
||||
| epiphany \
|
||||
| ft32 \
|
||||
| h8300 \
|
||||
| m68k \
|
||||
| microblaze | microblazeel \
|
||||
| nds32 | nds32le | nds32be \
|
||||
| nios2 | nios2eb | nios2el \
|
||||
| or1k* \
|
||||
| or32 \
|
||||
| sh | sh[1234] | sh[1234]e[lb] \
|
||||
| tic6x \
|
||||
| xtensa* )
|
||||
gl_cv_host_cpu_c_abi_32bit=yes
|
||||
;;
|
||||
|
||||
# CPUs that only support a 64-bit ABI.
|
||||
changequote(,)dnl
|
||||
alpha | alphaev[4-8] | alphaev56 | alphapca5[67] | alphaev6[78] \
|
||||
| mmix )
|
||||
changequote([,])dnl
|
||||
gl_cv_host_cpu_c_abi_32bit=no
|
||||
;;
|
||||
|
||||
*)
|
||||
if test -n "$gl_cv_host_cpu_c_abi"; then
|
||||
dnl gl_HOST_CPU_C_ABI has already been run. Use its result.
|
||||
case "$gl_cv_host_cpu_c_abi" in
|
||||
i386 | x86_64-x32 | arm | armhf | arm64-ilp32 | hppa | ia64-ilp32 | mips | mipsn32 | powerpc | riscv*-ilp32* | s390 | sparc)
|
||||
gl_cv_host_cpu_c_abi_32bit=yes ;;
|
||||
x86_64 | alpha | arm64 | aarch64c | hppa64 | ia64 | mips64 | powerpc64 | powerpc64-elfv2 | riscv*-lp64* | s390x | sparc64 )
|
||||
gl_cv_host_cpu_c_abi_32bit=no ;;
|
||||
*)
|
||||
gl_cv_host_cpu_c_abi_32bit=unknown ;;
|
||||
esac
|
||||
else
|
||||
gl_cv_host_cpu_c_abi_32bit=unknown
|
||||
fi
|
||||
if test $gl_cv_host_cpu_c_abi_32bit = unknown; then
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_SOURCE(
|
||||
[[int test_pointer_size[sizeof (void *) - 5];
|
||||
]])],
|
||||
[gl_cv_host_cpu_c_abi_32bit=no],
|
||||
[gl_cv_host_cpu_c_abi_32bit=yes])
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
])
|
||||
|
||||
HOST_CPU_C_ABI_32BIT="$gl_cv_host_cpu_c_abi_32bit"
|
||||
])
|
62
src/cthulhu/dynamic_api_manager.py
Normal file
62
src/cthulhu/dynamic_api_manager.py
Normal file
@ -0,0 +1,62 @@
|
||||
import gi
|
||||
from gi.repository import GObject
|
||||
|
||||
from cthulhu import resource_manager
|
||||
|
||||
class DynamicApiManager():
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
self.resourceManager = self.app.getResourceManager()
|
||||
self.api = {'Cthulhu': self.app}
|
||||
def registerAPI(self, key, value, application = '', contextName = None, overwrite = False):
|
||||
if not overwrite:
|
||||
try:
|
||||
d = self.api[application][key]
|
||||
return
|
||||
except KeyError:
|
||||
pass
|
||||
# add profile
|
||||
try:
|
||||
d = self.api[application]
|
||||
except KeyError:
|
||||
self.api[application]= {}
|
||||
# add dynamic API
|
||||
self.api[application][key] = value
|
||||
resourceContext = self.resourceManager.getResourceContext(contextName)
|
||||
if resourceContext:
|
||||
resourceEntry = resource_manager.ResourceEntry('api', key, value, value, key)
|
||||
resourceContext.addAPI(application, key, resourceEntry)
|
||||
|
||||
def unregisterAPI(self, key, application = '', contextName = None):
|
||||
ok = False
|
||||
try:
|
||||
del self.api[application][key]
|
||||
ok = True
|
||||
except:
|
||||
print('API Key: "{}/{}" not found,'.format(application, key))
|
||||
resourceContext = self.resourceManager.getResourceContext(contextName)
|
||||
if resourceContext:
|
||||
resourceContext.removeAPI(application, key)
|
||||
return ok
|
||||
def getAPI(self, key, application = '', fallback = True):
|
||||
# get dynamic API
|
||||
api = None
|
||||
|
||||
try:
|
||||
api = self.api[application][key]
|
||||
return api
|
||||
except:
|
||||
if not fallback:
|
||||
print('API Key: "{}/{}" not found,'.format(application, key))
|
||||
return None
|
||||
|
||||
# we already tried this
|
||||
if application == '':
|
||||
return api
|
||||
|
||||
try:
|
||||
api = self.api[application]['']
|
||||
except:
|
||||
print('API Key: "{}/{}" not found,'.format(application, key))
|
||||
|
||||
return api
|
160
src/cthulhu/plugin.py
Normal file
160
src/cthulhu/plugin.py
Normal file
@ -0,0 +1,160 @@
|
||||
import os, inspect
|
||||
import gi
|
||||
from gi.repository import GObject
|
||||
|
||||
class Plugin():
|
||||
#__gtype_name__ = 'BasePlugin'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.API = None
|
||||
self.pluginInfo = None
|
||||
self.moduleDir = ''
|
||||
self.hidden = False
|
||||
self.moduleName = ''
|
||||
self.name = ''
|
||||
self.version = ''
|
||||
self.website = ''
|
||||
self.authors = []
|
||||
self.buildIn = False
|
||||
self.description = ''
|
||||
self.iconName = ''
|
||||
self.copyright = ''
|
||||
self.dependencies = False
|
||||
self.helpUri = ''
|
||||
self.dataDir = ''
|
||||
self.translationContext = None
|
||||
def setApp(self, app):
|
||||
self.app = app
|
||||
self.dynamicApiManager = app.getDynamicApiManager()
|
||||
self.signalManager = app.getSignalManager()
|
||||
|
||||
def getApp(self):
|
||||
return self.app
|
||||
def setPluginInfo(self, pluginInfo):
|
||||
self.pluginInfo = pluginInfo
|
||||
self.updatePluginInfoAttributes()
|
||||
def getPluginInfo(self):
|
||||
return self.pluginInfo
|
||||
def updatePluginInfoAttributes(self):
|
||||
self.moduleDir = ''
|
||||
self.hidden = False
|
||||
self.moduleName = ''
|
||||
self.name = ''
|
||||
self.version = ''
|
||||
self.website = ''
|
||||
self.authors = []
|
||||
self.buildIn = False
|
||||
self.description = ''
|
||||
self.iconName = ''
|
||||
self.copyright = ''
|
||||
self.dependencies = False
|
||||
self.helpUri = ''
|
||||
self.dataDir = ''
|
||||
pluginInfo = self.getPluginInfo()
|
||||
if pluginInfo == None:
|
||||
return
|
||||
self.moduleName = self.getApp().getPluginSystemManager().getPluginModuleName(pluginInfo)
|
||||
self.name = self.getApp().getPluginSystemManager().getPluginName(pluginInfo)
|
||||
self.version = self.getApp().getPluginSystemManager().getPluginVersion(pluginInfo)
|
||||
self.moduleDir = self.getApp().getPluginSystemManager().getPluginModuleDir(pluginInfo)
|
||||
self.buildIn = self.getApp().getPluginSystemManager().isPluginBuildIn(pluginInfo)
|
||||
self.description = self.getApp().getPluginSystemManager().getPluginDescription(pluginInfo)
|
||||
self.hidden = self.getApp().getPluginSystemManager().isPluginHidden(pluginInfo)
|
||||
self.website = self.getApp().getPluginSystemManager().getPluginWebsite(pluginInfo)
|
||||
self.authors = self.getApp().getPluginSystemManager().getPluginAuthors(pluginInfo)
|
||||
self.iconName = self.getApp().getPluginSystemManager().getPluginIconName(pluginInfo)
|
||||
self.copyright = self.getApp().getPluginSystemManager().getPluginCopyright(pluginInfo)
|
||||
self.dependencies = self.getApp().getPluginSystemManager().getPluginDependencies(pluginInfo)
|
||||
|
||||
#settings = self.getApp().getPluginSystemManager().getPluginSettings(pluginInfo)
|
||||
#hasDependencies = self.getApp().getPluginSystemManager().hasPluginDependency(pluginInfo)
|
||||
|
||||
#externalData = self.getApp().getPluginSystemManager().getPluginExternalData(pluginInfo)
|
||||
self.helpUri = self.getApp().getPluginSystemManager().getPlugingetHelpUri(pluginInfo)
|
||||
self.dataDir = self.getApp().getPluginSystemManager().getPluginDataDir(pluginInfo)
|
||||
self.updateTranslationContext()
|
||||
|
||||
def updateTranslationContext(self, domain = None, localeDir = None, language = None, fallbackToCthulhuTranslation = True):
|
||||
self.translationContext = None
|
||||
useLocaleDir = '{}/locale/'.format(self.getModuleDir())
|
||||
if localeDir:
|
||||
if os.path.isdir(localeDir):
|
||||
useLocaleDir = localeDir
|
||||
useName = self.getModuleName()
|
||||
useDomain = useName
|
||||
if domain:
|
||||
useDomain = domain
|
||||
useLanguage = None
|
||||
if language:
|
||||
useLanguage = language
|
||||
self.translationContext = self.getApp().getTranslationManager().initTranslation(useName, domain=useDomain, localeDir=useLocaleDir, language=useLanguage, fallbackToCthulhuTranslation=fallbackToCthulhuTranslation)
|
||||
# Point _ to the translation object in the globals namespace of the caller frame
|
||||
try:
|
||||
callerFrame = inspect.currentframe().f_back
|
||||
# Install our gettext and ngettext function to the upper frame
|
||||
callerFrame.f_globals['_'] = self.translationContext.gettext
|
||||
callerFrame.f_globals['ngettext'] = self.translationContext.ngettext
|
||||
finally:
|
||||
del callerFrame # Avoid reference problems with frames (per python docs)
|
||||
def getTranslationContext(self):
|
||||
return self.translationContext
|
||||
def isPluginBuildIn(self):
|
||||
return self.buildIn
|
||||
def isPluginHidden(self):
|
||||
return self.hidden
|
||||
def getAuthors(self):
|
||||
return self.authors
|
||||
def getCopyright(self):
|
||||
return self.copyright
|
||||
def getDataDir(self):
|
||||
return self.dataDir
|
||||
def getDependencies(self):
|
||||
return self.dependencies
|
||||
def getDescription(self):
|
||||
return self.description
|
||||
def getgetHelpUri(self):
|
||||
return self.helpUri
|
||||
def getIconName(self):
|
||||
return self.iconName
|
||||
def getModuleDir(self):
|
||||
return self.moduleDir
|
||||
def getModuleName(self):
|
||||
return self.moduleName
|
||||
def getName(self):
|
||||
return self.name
|
||||
def getVersion(self):
|
||||
return self.version
|
||||
def getWebsite(self):
|
||||
return self.website
|
||||
def getSetting(key):
|
||||
#self.getModuleName())
|
||||
return None
|
||||
def setSetting(key, value):
|
||||
#self.getModuleName())
|
||||
pass
|
||||
def registerGestureByString(self, function, name, gestureString, learnModeEnabled = True):
|
||||
keybinding = self.getApp().getAPIHelper().registerGestureByString(function, name, gestureString, 'default', 'cthulhu', learnModeEnabled, contextName = self.getModuleName())
|
||||
return keybinding
|
||||
def unregisterShortcut(self, function, name, gestureString, learnModeEnabled = True):
|
||||
ok = self.getApp().getAPIHelper().unregisterShortcut(keybinding, contextName = self.getModuleName())
|
||||
return ok
|
||||
def registerSignal(self, signalName, signalFlag = GObject.SignalFlags.RUN_LAST, closure = GObject.TYPE_NONE, accumulator=()):
|
||||
ok = self.signalManager.registerSignal(signalName, signalFlag, closure, accumulator, contextName = self.getModuleName())
|
||||
return ok
|
||||
def unregisterSignal(self, signalName):
|
||||
# how to unregister?
|
||||
pass
|
||||
|
||||
def connectSignal(self, signalName, function, param = None):
|
||||
signalID = self.signalManager.connectSignal(signalName, function, param, contextName = self.getModuleName())
|
||||
return signalID
|
||||
def disconnectSignalByFunction(self, function):
|
||||
# need get mapped function
|
||||
mappedFunction = function
|
||||
self.signalManager.disconnectSignalByFunction(mappedFunction, contextName = self.getModuleName())
|
||||
|
||||
def registerAPI(self, key, value, application = ''):
|
||||
ok = self.dynamicApiManager.registerAPI(key, value, application = application, contextName = self.getModuleName())
|
||||
return ok
|
||||
def unregisterAPI(self, key, application = ''):
|
||||
self.dynamicApiManager.unregisterAPI(key, application = application, contextName = self.getModuleName())
|
520
src/cthulhu/plugin_system_manager.py
Normal file
520
src/cthulhu/plugin_system_manager.py
Normal file
@ -0,0 +1,520 @@
|
||||
#!/bin/python
|
||||
"""PluginManager for loading cthulhu plugins."""
|
||||
import os, inspect, sys, tarfile, shutil
|
||||
|
||||
from enum import IntEnum
|
||||
|
||||
version = sys.version[:3] # we only need major.minor version.
|
||||
if version in ["3.3","3.4"]:
|
||||
from importlib.machinery import SourceFileLoader
|
||||
else: # Python 3.5+, no support for python < 3.3.
|
||||
import importlib.util
|
||||
|
||||
import gi
|
||||
gi.require_version('Peas', '1.0')
|
||||
from gi.repository import GObject
|
||||
from gi.repository import Peas
|
||||
|
||||
gi.require_version('Atspi', '2.0')
|
||||
from gi.repository import Atspi
|
||||
|
||||
from cthulhu import resource_manager
|
||||
|
||||
class API(GObject.GObject):
|
||||
"""Interface that gives access to all the objects of Cthulhu."""
|
||||
def __init__(self, app):
|
||||
GObject.GObject.__init__(self)
|
||||
self.app = app
|
||||
|
||||
class PluginType(IntEnum):
|
||||
"""Types of plugins we support, depending on their directory location."""
|
||||
# pylint: disable=comparison-with-callable,inconsistent-return-statements,no-else-return
|
||||
# SYSTEM: provides system wide plugins
|
||||
SYSTEM = 1
|
||||
# USER: provides per user plugin
|
||||
USER = 2
|
||||
|
||||
def __str__(self):
|
||||
if self.value == PluginType.SYSTEM:
|
||||
return _("System plugin")
|
||||
elif self.value == PluginType.USER:
|
||||
return _("User plugin")
|
||||
|
||||
def get_root_dir(self):
|
||||
"""Returns the directory where this type of plugins can be found."""
|
||||
if self.value == PluginType.SYSTEM:
|
||||
return os.path.dirname(os.path.realpath(os.path.abspath(inspect.getfile(inspect.currentframe())))) + '/plugins'
|
||||
elif self.value == PluginType.USER:
|
||||
return os.path.expanduser('~') + '/.local/share/cthulhu/plugins'
|
||||
|
||||
|
||||
class PluginSystemManager():
|
||||
"""Orca Plugin Manager to handle a set of plugins.
|
||||
Attributes:
|
||||
DEFAULT_LOADERS (tuple): Default loaders used by the plugin manager. For
|
||||
possible values see
|
||||
https://developer.gnome.org/libpeas/stable/PeasEngine.html#peas-engine-enable-loader
|
||||
"""
|
||||
DEFAULT_LOADERS = ("python3", )
|
||||
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
self.engine = Peas.Engine.get_default()
|
||||
|
||||
for loader in self.DEFAULT_LOADERS:
|
||||
self.engine.enable_loader(loader)
|
||||
|
||||
self._setupPluginsDir()
|
||||
self._setupExtensionSet()
|
||||
|
||||
if self.app:
|
||||
self.gsettingsManager = self.app.getSettingsManager()
|
||||
# settings else:
|
||||
# settings self.gsettingsManager = gsettings_manager.getSettingsManager(self.app)
|
||||
self._activePlugins = []
|
||||
self._ignorePluginModulePath = []
|
||||
@property
|
||||
def plugins(self):
|
||||
"""Gets the engine's plugin list."""
|
||||
return self.engine.get_plugin_list()
|
||||
@classmethod
|
||||
def getPluginType(cls, pluginInfo):
|
||||
"""Gets the PluginType for the specified Peas.PluginInfo."""
|
||||
paths = [pluginInfo.get_data_dir(), PluginType.SYSTEM.get_root_dir()]
|
||||
if os.path.commonprefix(paths) == PluginType.SYSTEM.get_root_dir():
|
||||
return PluginType.SYSTEM
|
||||
return PluginType.USER
|
||||
|
||||
def getExtension(self, pluginInfo):
|
||||
if not pluginInfo:
|
||||
return None
|
||||
|
||||
return self.extension_set.get_extension(pluginInfo)
|
||||
def rescanPlugins(self):
|
||||
self.engine.garbage_collect()
|
||||
self.engine.rescan_plugins()
|
||||
def getApp(self):
|
||||
return self.app
|
||||
def getPluginInfoByName(self, pluginName, pluginType=PluginType.USER):
|
||||
"""Gets the plugin info for the specified plugin name.
|
||||
Args:
|
||||
pluginName (str): The name from the .plugin file of the module.
|
||||
Returns:
|
||||
Peas.PluginInfo: The plugin info if it exists. Otherwise, `None`.
|
||||
"""
|
||||
for pluginInfo in self.plugins:
|
||||
if pluginInfo.get_module_name() == pluginName and PluginSystemManager.getPluginType(pluginInfo) == pluginType:
|
||||
return pluginInfo
|
||||
return None
|
||||
def getActivePlugins(self):
|
||||
return self._activePlugins
|
||||
def setActivePlugins(self, activePlugins):
|
||||
self._activePlugins = activePlugins
|
||||
self.syncAllPluginsActive()
|
||||
def isPluginBuildIn(self, pluginInfo):
|
||||
return pluginInfo.is_builtin()
|
||||
def isPluginHidden(self, pluginInfo):
|
||||
return pluginInfo.is_hidden()
|
||||
def getPluginAuthors(self, pluginInfo):
|
||||
return pluginInfo.get_authors()
|
||||
def getPluginCopyright(self, pluginInfo):
|
||||
return pluginInfo.get_copyright()
|
||||
def getPluginDataDir(self, pluginInfo):
|
||||
return pluginInfo.get_data_dir()
|
||||
def getPluginDependencies(self, pluginInfo):
|
||||
return pluginInfo.get_dependencies()
|
||||
def getPluginDescription(self, pluginInfo):
|
||||
return pluginInfo.get_description()
|
||||
def getPlugingetHelpUri(self, pluginInfo):
|
||||
return pluginInfo.get_help_uri()
|
||||
def getPluginIconName(self, pluginInfo):
|
||||
return pluginInfo.get_icon_name()
|
||||
def getPluginModuleDir(self, pluginInfo):
|
||||
return pluginInfo.get_module_dir()
|
||||
def getPluginModuleName(self, pluginInfo):
|
||||
return pluginInfo.get_module_name()
|
||||
def getPluginName(self, pluginInfo):
|
||||
return pluginInfo.get_name()
|
||||
def getPluginSettings(self, pluginInfo):
|
||||
return pluginInfo.get_settings()
|
||||
def getPluginVersion(self, pluginInfo):
|
||||
return pluginInfo.get_version()
|
||||
def getPluginWebsite(self, pluginInfo):
|
||||
return pluginInfo.get_website()
|
||||
# has_dependency and get_external_data seems broken-> takes exactly 2 arguments (1 given) but documentation doesnt say any parameter
|
||||
#def hasPluginDependency(self, pluginInfo):
|
||||
# return pluginInfo.has_dependency()
|
||||
#def getPluginExternalData(self, pluginInfo):
|
||||
# return pluginInfo.get_external_data()
|
||||
def isPluginAvailable(self, pluginInfo):
|
||||
try:
|
||||
return pluginInfo.is_available()
|
||||
except:
|
||||
return False
|
||||
def isPluginLoaded(self, pluginInfo):
|
||||
try:
|
||||
return pluginInfo.is_loaded()
|
||||
except:
|
||||
return False
|
||||
|
||||
def getIgnoredPlugins(self):
|
||||
return self._ignorePluginModulePath
|
||||
def setIgnoredPlugins(self, pluginModulePath, ignored):
|
||||
if pluginModulePath.endswith('/'):
|
||||
pluginModulePath = pluginModulePath[:-1]
|
||||
if ignored:
|
||||
if not pluginModulePath in self.getIgnoredPlugins():
|
||||
self._ignorePluginModulePath.append(pluginModulePath)
|
||||
else:
|
||||
if pluginModulePath in self.getIgnoredPlugins():
|
||||
self._ignorePluginModulePath.remove(pluginModulePath)
|
||||
|
||||
def setPluginActive(self, pluginInfo, active):
|
||||
if self.isPluginBuildIn(pluginInfo):
|
||||
active = True
|
||||
pluginName = self.getPluginModuleName(pluginInfo)
|
||||
if active:
|
||||
if not pluginName in self.getActivePlugins():
|
||||
if self.loadPlugin(pluginInfo):
|
||||
self._activePlugins.append(pluginName )
|
||||
else:
|
||||
if pluginName in self.getActivePlugins():
|
||||
if self.unloadPlugin(pluginInfo):
|
||||
self._activePlugins.remove(pluginName )
|
||||
def isPluginActive(self, pluginInfo):
|
||||
if self.isPluginBuildIn(pluginInfo):
|
||||
return True
|
||||
if self.isPluginLoaded(pluginInfo):
|
||||
return True
|
||||
active_plugin_names = self.getActivePlugins()
|
||||
return self.getPluginModuleName(pluginInfo) in active_plugin_names
|
||||
def syncAllPluginsActive(self, ForceAllPlugins=False):
|
||||
self.unloadAllPlugins(ForceAllPlugins)
|
||||
self.loadAllPlugins(ForceAllPlugins)
|
||||
|
||||
def loadAllPlugins(self, ForceAllPlugins=False):
|
||||
"""Loads plugins from settings."""
|
||||
for pluginInfo in self.plugins:
|
||||
if self.isPluginActive(pluginInfo) or ForceAllPlugins:
|
||||
self.loadPlugin(pluginInfo)
|
||||
|
||||
def loadPlugin(self, pluginInfo):
|
||||
resourceManager = self.getApp().getResourceManager()
|
||||
moduleName = pluginInfo.get_module_name()
|
||||
try:
|
||||
if pluginInfo not in self.plugins:
|
||||
print("Plugin missing: {}".format(moduleName))
|
||||
return False
|
||||
resourceManager.addResourceContext(moduleName)
|
||||
self.engine.load_plugin(pluginInfo)
|
||||
except Exception as e:
|
||||
print('loadPlugin:',e)
|
||||
return False
|
||||
return True
|
||||
|
||||
def unloadAllPlugins(self, ForceAllPlugins=False):
|
||||
"""Loads plugins from settings."""
|
||||
for pluginInfo in self.plugins:
|
||||
if not self.isPluginActive(pluginInfo) or ForceAllPlugins:
|
||||
self.unloadPlugin(pluginInfo)
|
||||
|
||||
def unloadPlugin(self, pluginInfo):
|
||||
resourceManager = self.getApp().getResourceManager()
|
||||
moduleName = pluginInfo.get_module_name()
|
||||
try:
|
||||
if pluginInfo not in self.plugins:
|
||||
print("Plugin missing: {}".format(moduleName))
|
||||
return False
|
||||
if self.isPluginBuildIn(pluginInfo):
|
||||
return False
|
||||
self.engine.unload_plugin(pluginInfo)
|
||||
self.getApp().getResourceManager().removeResourceContext(moduleName)
|
||||
self.engine.garbage_collect()
|
||||
except Exception as e:
|
||||
print('unloadPlugin:',e)
|
||||
return False
|
||||
return True
|
||||
def installPlugin(self, pluginFilePath, pluginType=PluginType.USER):
|
||||
if not self.isValidPluginFile(pluginFilePath):
|
||||
return False
|
||||
pluginFolder = pluginType.get_root_dir()
|
||||
if not pluginFolder.endswith('/'):
|
||||
pluginFolder += '/'
|
||||
if not os.path.exists(pluginFolder):
|
||||
os.mkdir(pluginFolder)
|
||||
else:
|
||||
if not os.path.isdir(pluginFolder):
|
||||
return False
|
||||
try:
|
||||
with tarfile.open(pluginFilePath) as tar:
|
||||
tar.extractall(path=pluginFolder)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
pluginModulePath = self.getModuleDirByPluginFile(pluginFilePath)
|
||||
if pluginModulePath != '':
|
||||
pluginModulePath = pluginFolder + pluginModulePath
|
||||
self.setIgnoredPlugins(pluginModulePath[:-1], False) # without ending /
|
||||
print('install', pluginFilePath)
|
||||
self.callPackageTriggers(pluginModulePath, 'onPostInstall')
|
||||
self.rescanPlugins()
|
||||
|
||||
return True
|
||||
def getModuleDirByPluginFile(self, pluginFilePath):
|
||||
if not isinstance(pluginFilePath, str):
|
||||
return ''
|
||||
if pluginFilePath == '':
|
||||
return ''
|
||||
if not os.path.exists(pluginFilePath):
|
||||
return ''
|
||||
try:
|
||||
with tarfile.open(pluginFilePath) as tar:
|
||||
tarMembers = tar.getmembers()
|
||||
for tarMember in tarMembers:
|
||||
if tarMember.isdir():
|
||||
return tarMember.name
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return ''
|
||||
def isValidPluginFile(self, pluginFilePath):
|
||||
if not isinstance(pluginFilePath, str):
|
||||
return False
|
||||
if pluginFilePath == '':
|
||||
return False
|
||||
if not os.path.exists(pluginFilePath):
|
||||
return False
|
||||
pluginFolder = ''
|
||||
pluginFileExists = False
|
||||
packageFileExists = False
|
||||
try:
|
||||
with tarfile.open(pluginFilePath) as tar:
|
||||
tarMembers = tar.getmembers()
|
||||
for tarMember in tarMembers:
|
||||
if tarMember.isdir():
|
||||
if pluginFolder == '':
|
||||
pluginFolder = tarMember.name
|
||||
if tarMember.isfile():
|
||||
if tarMember.name.endswith('.plugin'):
|
||||
pluginFileExists = True
|
||||
if tarMember.name.endswith('package.py'):
|
||||
pluginFileExists = True
|
||||
if not tarMember.name.startswith(pluginFolder):
|
||||
return False
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return False
|
||||
return pluginFileExists
|
||||
def uninstallPlugin(self, pluginInfo):
|
||||
if self.isPluginBuildIn(pluginInfo):
|
||||
return False
|
||||
# do we want to allow removing system plugins?
|
||||
if PluginSystemManager.getPluginType(pluginInfo) == PluginType.SYSTEM:
|
||||
return False
|
||||
pluginFolder = pluginInfo.get_data_dir()
|
||||
if not pluginFolder.endswith('/'):
|
||||
pluginFolder += '/'
|
||||
if not os.path.isdir(pluginFolder):
|
||||
return False
|
||||
if self.isPluginActive(pluginInfo):
|
||||
self.setPluginActive(pluginInfo, False)
|
||||
SettingsManager = self.app.getSettingsManager()
|
||||
# TODO SettingsManager.set_settings_value_list('active-plugins', self.getActivePlugins())
|
||||
self.callPackageTriggers(pluginFolder, 'onPreUninstall')
|
||||
|
||||
try:
|
||||
shutil.rmtree(pluginFolder, ignore_errors=True)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return False
|
||||
self.setIgnoredPlugins(pluginFolder, True)
|
||||
self.rescanPlugins()
|
||||
|
||||
return True
|
||||
def callPackageTriggers(self, pluginPath, trigger):
|
||||
if not os.path.exists(pluginPath):
|
||||
return
|
||||
if not pluginPath.endswith('/'):
|
||||
pluginPath += '/'
|
||||
packageModulePath = pluginPath + 'package.py'
|
||||
if not os.path.isfile(packageModulePath):
|
||||
return
|
||||
if not os.access(packageModulePath, os.R_OK):
|
||||
return
|
||||
package = self.getApp().getAPIHelper().importModule('package', packageModulePath)
|
||||
|
||||
if trigger == 'onPostInstall':
|
||||
try:
|
||||
package.onPostInstall(pluginPath, self.getApp())
|
||||
except Exception as e:
|
||||
print(e)
|
||||
elif trigger == 'onPreUninstall':
|
||||
try:
|
||||
package.onPreUninstall(pluginPath, self.getApp())
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
def _setupExtensionSet(self):
|
||||
plugin_iface = API(self.getApp())
|
||||
self.extension_set = Peas.ExtensionSet.new(self.engine,
|
||||
Peas.Activatable,
|
||||
["object"],
|
||||
[plugin_iface])
|
||||
self.extension_set.connect("extension-removed",
|
||||
self.__extensionRemoved)
|
||||
self.extension_set.connect("extension-added",
|
||||
self.__extensionAdded)
|
||||
|
||||
def _setupPluginsDir(self):
|
||||
system_plugins_dir = PluginType.SYSTEM.get_root_dir()
|
||||
user_plugins_dir = PluginType.USER.get_root_dir()
|
||||
if os.path.exists(user_plugins_dir):
|
||||
self.engine.add_search_path(user_plugins_dir)
|
||||
if os.path.exists(system_plugins_dir):
|
||||
self.engine.add_search_path(system_plugins_dir)
|
||||
|
||||
def __extensionRemoved(self, unusedSet, pluginInfo, extension):
|
||||
extension.deactivate()
|
||||
|
||||
def __extensionAdded(self, unusedSet, pluginInfo, extension):
|
||||
extension.setApp(self.getApp())
|
||||
extension.setPluginInfo(pluginInfo)
|
||||
extension.activate()
|
||||
def __loadedPlugins(self, engine, unusedSet):
|
||||
"""Handles the changing of the loaded plugin list."""
|
||||
self.getApp().settings.ActivePlugins = engine.get_property("loaded-plugins")
|
||||
|
||||
class APIHelper():
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
self.cthulhuKeyBindings = None
|
||||
|
||||
'''
|
||||
_pluginAPIManager.seCthulhuAPI('Logger', _logger)
|
||||
_pluginAPIManager.setCthulhuAPI('SettingsManager', _settingsManager)
|
||||
_pluginAPIManager.setCthulhuAPI('ScriptManager', _scriptManager)
|
||||
_pluginAPIManager.setCthulhuAPI('EventManager', _eventManager)
|
||||
_pluginAPIManager.setCthulhuAPI('Speech', speech)
|
||||
_pluginAPIManager.setCthulhuAPI('Sound', sound)
|
||||
_pluginAPIManager.setCthulhuAPI('Braille', braille)
|
||||
_pluginAPIManager.setCthulhuAPI('Debug', debug)
|
||||
_pluginAPIManager.setCthulhuAPI('Messages', messages)
|
||||
_pluginAPIManager.setCthulhuAPI('MouseReview', mouse_review)
|
||||
_pluginAPIManager.setCthulhuAPI('NotificationMessages', notification_messages)
|
||||
_pluginAPIManager.setCthulhuAPI('CthulhuState', cthulhu_state)
|
||||
_pluginAPIManager.setCthulhuAPI('CthulhuPlatform', cthulhu_platform)
|
||||
_pluginAPIManager.setCthulhuAPI('Settings', settings)
|
||||
_pluginAPIManager.setCthulhuAPI('Keybindings', keybindings)
|
||||
'''
|
||||
def outputMessage(self, Message, interrupt=False):
|
||||
settings = self.app.getDynamicApiManager().getAPI('Settings')
|
||||
braille = self.app.getDynamicApiManager().getAPI('Braille')
|
||||
speech = self.app.getDynamicApiManager().getAPI('Speech')
|
||||
if speech != None:
|
||||
if (settings.enableSpeech):
|
||||
if interrupt:
|
||||
speech.cancel()
|
||||
if Message != '':
|
||||
speech.speak(Message)
|
||||
if braille != None:
|
||||
if (settings.enableBraille):
|
||||
braille.displayMessage(Message)
|
||||
def createInputEventHandler(self, function, name, learnModeEnabled=True):
|
||||
EventManager = self.app.getDynamicApiManager().getAPI('EventManager')
|
||||
newInputEventHandler = EventManager.input_event.InputEventHandler(function, name, learnModeEnabled)
|
||||
return newInputEventHandler
|
||||
def registerGestureByString(self, function, name, gestureString, profile, application, learnModeEnabled = True, contextName = None):
|
||||
gestureList = gestureString.split(',')
|
||||
registeredGestures = []
|
||||
for gesture in gestureList:
|
||||
if gesture.startswith('kb:'):
|
||||
shortcutString = gesture[3:]
|
||||
registuredGesture = self.registerShortcutByString(function, name, shortcutString, profile, application, learnModeEnabled, contextName=contextName)
|
||||
if registuredGesture:
|
||||
registeredGestures.append(registuredGesture)
|
||||
return registeredGestures
|
||||
def registerShortcutByString(self, function, name, shortcutString, profile, application, learnModeEnabled = True, contextName = None):
|
||||
keybindings = self.app.getDynamicApiManager().getAPI('Keybindings')
|
||||
settings = self.app.getDynamicApiManager().getAPI('Settings')
|
||||
resourceManager = self.app.getResourceManager()
|
||||
|
||||
clickCount = 0
|
||||
cthulhuKey = False
|
||||
shiftKey = False
|
||||
ctrlKey = False
|
||||
altKey = False
|
||||
key = ''
|
||||
shortcutList = shortcutString.split('+')
|
||||
for shortcutElement in shortcutList:
|
||||
shortcutElementLower = shortcutElement.lower()
|
||||
if shortcutElementLower == 'press':
|
||||
clickCount += 1
|
||||
elif shortcutElement == 'cthulhu':
|
||||
cthulhuKey = True
|
||||
elif shortcutElementLower == 'shift':
|
||||
shiftKey = True
|
||||
elif shortcutElementLower == 'control':
|
||||
ctrlKey = True
|
||||
elif shortcutElementLower == 'alt':
|
||||
altKey = True
|
||||
else:
|
||||
key = shortcutElementLower
|
||||
if clickCount == 0:
|
||||
clickCount = 1
|
||||
|
||||
if self.cthulhuKeyBindings == None:
|
||||
self.cthulhuKeyBindings = keybindings.KeyBindings()
|
||||
|
||||
tryFunction = resource_manager.TryFunction(function)
|
||||
newInputEventHandler = self.createInputEventHandler(tryFunction.runInputEvent, name, learnModeEnabled)
|
||||
|
||||
currModifierMask = keybindings.NO_MODIFIER_MASK
|
||||
if cthulhuKey:
|
||||
currModifierMask = currModifierMask | 1 << keybindings.MODIFIER_ORCA
|
||||
if shiftKey:
|
||||
currModifierMask = currModifierMask | 1 << Atspi.ModifierType.SHIFT
|
||||
if altKey:
|
||||
currModifierMask = currModifierMask | 1 << Atspi.ModifierType.ALT
|
||||
if ctrlKey:
|
||||
currModifierMask = currModifierMask | 1 << Atspi.ModifierType.CONTROL
|
||||
|
||||
newKeyBinding = keybindings.KeyBinding(key, keybindings.defaultModifierMask, currModifierMask, newInputEventHandler, clickCount)
|
||||
self.cthulhuKeyBindings.add(newKeyBinding)
|
||||
|
||||
settings.keyBindingsMap["default"] = self.cthulhuKeyBindings
|
||||
|
||||
if contextName:
|
||||
resourceContext = resourceManager.getResourceContext(contextName)
|
||||
if resourceContext:
|
||||
resourceEntry = resource_manager.ResourceEntry('keyboard', newKeyBinding, function, tryFunction, shortcutString)
|
||||
resourceContext.addGesture(profile, application, newKeyBinding, resourceEntry)
|
||||
|
||||
return newKeyBinding
|
||||
|
||||
def unregisterShortcut(self, KeyBindingToRemove, contextName = None):
|
||||
ok = False
|
||||
keybindings = self.app.getDynamicApiManager().getAPI('Keybindings')
|
||||
settings = self.app.getDynamicApiManager().getAPI('Settings')
|
||||
resourceManager = self.app.getResourceManager()
|
||||
|
||||
if self.cthulhuKeyBindings == None:
|
||||
self.cthulhuKeyBindings = keybindings.KeyBindings()
|
||||
try:
|
||||
self.cthulhuKeyBindings.remove(KeyBindingToRemove)
|
||||
settings.keyBindingsMap["default"] = self.cthulhuKeyBindings
|
||||
ok = True
|
||||
except KeyError:
|
||||
pass
|
||||
if contextName:
|
||||
resourceContext = resourceManager.getResourceContext(contextName)
|
||||
if resourceContext:
|
||||
resourceContext.removeGesture(KeyBindingToRemove)
|
||||
|
||||
return ok
|
||||
def importModule(self, moduleName, moduleLocation):
|
||||
if version in ["3.3","3.4"]:
|
||||
return SourceFileLoader(moduleName, moduleLocation).load_module()
|
||||
else:
|
||||
spec = importlib.util.spec_from_file_location(moduleName, moduleLocation)
|
||||
driver_mod = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(driver_mod)
|
||||
return driver_mod
|
6
src/cthulhu/plugins/ByeCthulhu/ByeCthulhu.plugin
Normal file
6
src/cthulhu/plugins/ByeCthulhu/ByeCthulhu.plugin
Normal file
@ -0,0 +1,6 @@
|
||||
[Plugin]
|
||||
Module=ByeCthulhu
|
||||
Loader=python3
|
||||
Name=Stop announcement for cthulhu
|
||||
Description=Test plugin for cthulhu
|
||||
Authors=Chrys chrys@linux-a11y.org
|
27
src/cthulhu/plugins/ByeCthulhu/ByeCthulhu.py
Normal file
27
src/cthulhu/plugins/ByeCthulhu/ByeCthulhu.py
Normal file
@ -0,0 +1,27 @@
|
||||
from cthulhu import plugin
|
||||
|
||||
import gi
|
||||
gi.require_version('Peas', '1.0')
|
||||
from gi.repository import GObject
|
||||
from gi.repository import Peas
|
||||
import time
|
||||
|
||||
|
||||
class ByeOrca(GObject.Object, Peas.Activatable, plugin.Plugin):
|
||||
#__gtype_name__ = 'ByeCthulhu'
|
||||
|
||||
object = GObject.Property(type=GObject.Object)
|
||||
def __init__(self):
|
||||
plugin.Plugin.__init__(self)
|
||||
def do_activate(self):
|
||||
API = self.object
|
||||
self.connectSignal("stop-application-completed", self.process)
|
||||
def do_deactivate(self):
|
||||
API = self.object
|
||||
def do_update_state(self):
|
||||
API = self.object
|
||||
def process(self, app):
|
||||
messages = app.getDynamicApiManager().getAPI('Messages')
|
||||
activeScript = app.getDynamicApiManager().getAPI('CthulhuState').activeScript
|
||||
activeScript.presentationInterrupt()
|
||||
activeScript.presentMessage(messages.STOP_ORCA, resetStyles=False)
|
7
src/cthulhu/plugins/ByeCthulhu/Makefile.am
Normal file
7
src/cthulhu/plugins/ByeCthulhu/Makefile.am
Normal file
@ -0,0 +1,7 @@
|
||||
orca_python_PYTHON = \
|
||||
__init__.py \
|
||||
ByeCthulhu.plugin \
|
||||
ByeCthulhu.py
|
||||
|
||||
orca_pythondir=$(pkgpythondir)/plugins/ByeCthulhu
|
||||
|
0
src/cthulhu/plugins/ByeCthulhu/__init__.py
Normal file
0
src/cthulhu/plugins/ByeCthulhu/__init__.py
Normal file
6
src/cthulhu/plugins/CapsLockHack/CapsLockHack.plugin
Normal file
6
src/cthulhu/plugins/CapsLockHack/CapsLockHack.plugin
Normal file
@ -0,0 +1,6 @@
|
||||
[Plugin]
|
||||
Module=CapsLockHack
|
||||
Loader=python3
|
||||
Name=Caps Lock Hack
|
||||
Description=Fix Capslock sometimes switch on / off when its used as modifier
|
||||
Authors=Chrys chrys@linux-a11y.org
|
119
src/cthulhu/plugins/CapsLockHack/CapsLockHack.py
Normal file
119
src/cthulhu/plugins/CapsLockHack/CapsLockHack.py
Normal file
@ -0,0 +1,119 @@
|
||||
from cthulhu import plugin
|
||||
|
||||
import gi
|
||||
gi.require_version('Peas', '1.0')
|
||||
from gi.repository import GObject
|
||||
from gi.repository import Peas
|
||||
from threading import Thread, Lock
|
||||
import subprocess, time, re, os
|
||||
|
||||
class CapsLockHack(GObject.Object, Peas.Activatable, plugin.Plugin):
|
||||
__gtype_name__ = 'CapsLockHack'
|
||||
|
||||
object = GObject.Property(type=GObject.Object)
|
||||
def __init__(self):
|
||||
plugin.Plugin.__init__(self)
|
||||
self.lock = Lock()
|
||||
self.active = False
|
||||
self.workerThread = Thread(target=self.worker)
|
||||
def do_activate(self):
|
||||
API = self.object
|
||||
"""Enable or disable use of the caps lock key as an Cthulhu modifier key."""
|
||||
self.interpretCapsLineProg = re.compile(
|
||||
r'^\s*interpret\s+Caps[_+]Lock[_+]AnyOfOrNone\s*\(all\)\s*{\s*$', re.I)
|
||||
self.normalCapsLineProg = re.compile(
|
||||
r'^\s*action\s*=\s*LockMods\s*\(\s*modifiers\s*=\s*Lock\s*\)\s*;\s*$', re.I)
|
||||
self.interpretShiftLineProg = re.compile(
|
||||
r'^\s*interpret\s+Shift[_+]Lock[_+]AnyOf\s*\(\s*Shift\s*\+\s*Lock\s*\)\s*{\s*$', re.I)
|
||||
self.normalShiftLineProg = re.compile(
|
||||
r'^\s*action\s*=\s*LockMods\s*\(\s*modifiers\s*=\s*Shift\s*\)\s*;\s*$', re.I)
|
||||
self.disabledModLineProg = re.compile(
|
||||
r'^\s*action\s*=\s*NoAction\s*\(\s*\)\s*;\s*$', re.I)
|
||||
self.normalCapsLine = ' action= LockMods(modifiers=Lock);'
|
||||
self.normalShiftLine = ' action= LockMods(modifiers=Shift);'
|
||||
self.disabledModLine = ' action= NoAction();'
|
||||
self.activateWorker()
|
||||
def do_deactivate(self):
|
||||
API = self.object
|
||||
self.deactivateWorker()
|
||||
def do_update_state(self):
|
||||
API = self.object
|
||||
def deactivateWorker(self):
|
||||
with self.lock:
|
||||
self.active = False
|
||||
self.workerThread.join()
|
||||
def activateWorker(self):
|
||||
with self.lock:
|
||||
self.active = True
|
||||
self.workerThread.start()
|
||||
def isActive(self):
|
||||
with self.lock:
|
||||
return self.active
|
||||
def worker(self):
|
||||
"""Makes an Cthulhu-specific Xmodmap so that the keys behave as we
|
||||
need them to do. This is especially the case for the Cthulhu modifier.
|
||||
"""
|
||||
API = self.object
|
||||
capsLockCleared = False
|
||||
settings = API.app.getDynamicApiManager().getAPI('Settings')
|
||||
time.sleep(3)
|
||||
while self.isActive():
|
||||
if "Caps_Lock" in settings.orcaModifierKeys \
|
||||
or "Shift_Lock" in settings.orcaModifierKeys:
|
||||
self.setCapsLockAsOrcaModifier(True)
|
||||
capsLockCleared = True
|
||||
elif capsLockCleared:
|
||||
self.setCapsLockAsOrcaModifier(False)
|
||||
capsLockCleared = False
|
||||
time.sleep(1)
|
||||
|
||||
def setCapsLockAsOrcaModifier(self, enable):
|
||||
originalXmodmap = None
|
||||
lines = None
|
||||
try:
|
||||
originalXmodmap = subprocess.check_output(['xkbcomp', os.environ['DISPLAY'], '-'])
|
||||
lines = originalXmodmap.decode('UTF-8').split('\n')
|
||||
except:
|
||||
return
|
||||
foundCapsInterpretSection = False
|
||||
foundShiftInterpretSection = False
|
||||
modified = False
|
||||
for i, line in enumerate(lines):
|
||||
if not foundCapsInterpretSection and not foundShiftInterpretSection:
|
||||
if self.interpretCapsLineProg.match(line):
|
||||
foundCapsInterpretSection = True
|
||||
elif self.interpretShiftLineProg.match(line):
|
||||
foundShiftInterpretSection = True
|
||||
elif foundCapsInterpretSection:
|
||||
if enable:
|
||||
if self.normalCapsLineProg.match(line):
|
||||
lines[i] = self.disabledModLine
|
||||
modified = True
|
||||
else:
|
||||
if self.disabledModLineProg.match(line):
|
||||
lines[i] = self.normalCapsLine
|
||||
modified = True
|
||||
if line.find('}'):
|
||||
foundCapsInterpretSection = False
|
||||
else: # foundShiftInterpretSection
|
||||
if enable:
|
||||
if self.normalShiftLineProg.match(line):
|
||||
lines[i] = self.disabledModLine
|
||||
modified = True
|
||||
else:
|
||||
if self.disabledModLineProg.match(line):
|
||||
lines[i] = self.normalShiftLine
|
||||
modified = True
|
||||
if line.find('}'):
|
||||
foundShiftInterpretSection = False
|
||||
if modified:
|
||||
newXmodMap = bytes('\n'.join(lines), 'UTF-8')
|
||||
self.setXmodmap(newXmodMap)
|
||||
def setXmodmap(self, xkbmap):
|
||||
"""Set the keyboard map using xkbcomp."""
|
||||
try:
|
||||
p = subprocess.Popen(['xkbcomp', '-w0', '-', os.environ['DISPLAY']],
|
||||
stdin=subprocess.PIPE, stdout=None, stderr=None)
|
||||
p.communicate(xkbmap)
|
||||
except:
|
||||
pass
|
7
src/cthulhu/plugins/CapsLockHack/Makefile.am
Normal file
7
src/cthulhu/plugins/CapsLockHack/Makefile.am
Normal file