mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2026-01-06 16:41:43 +01:00
Compare commits
48 Commits
jbr21.902
...
jb21.0.6-b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9fdf0eba5 | ||
|
|
009e79c25d | ||
|
|
116cbbbc11 | ||
|
|
a4190e583c | ||
|
|
2f672089a5 | ||
|
|
b7ce4c8fb9 | ||
|
|
fce8863e17 | ||
|
|
5b0e5fee58 | ||
|
|
e57d489db3 | ||
|
|
6f47232b48 | ||
|
|
7a85ad38eb | ||
|
|
dd96fe584d | ||
|
|
0c09c93635 | ||
|
|
8c769bbc7c | ||
|
|
2308ce139b | ||
|
|
25a97304ae | ||
|
|
edfe77a55c | ||
|
|
aa7902f385 | ||
|
|
ca2636829c | ||
|
|
58058b4e5c | ||
|
|
1ab48b4b72 | ||
|
|
c5d734adcc | ||
|
|
16d6b74709 | ||
|
|
7d32ca5b3c | ||
|
|
fb03844dc1 | ||
|
|
370f8353a6 | ||
|
|
24654f556a | ||
|
|
8308c81a9a | ||
|
|
6793c86251 | ||
|
|
6214b52d0a | ||
|
|
2e1051cd85 | ||
|
|
224ccc0fb8 | ||
|
|
e957e7045e | ||
|
|
f747edbbdc | ||
|
|
534b9afc34 | ||
|
|
cf90d55b49 | ||
|
|
c673e166b4 | ||
|
|
0828954308 | ||
|
|
163d3bf7fa | ||
|
|
effda670ac | ||
|
|
d6ebb4f51b | ||
|
|
23b0860f75 | ||
|
|
c5a3e6591e | ||
|
|
0c0dd61724 | ||
|
|
f5b2a21d22 | ||
|
|
499257dedb | ||
|
|
5cdc148b68 | ||
|
|
6253f4a137 |
15
README.md
15
README.md
@@ -39,6 +39,21 @@ can be found on the [releases page](https://github.com/JetBrains/JetBrainsRuntim
|
||||
| 2021.1 | [11.0.11+9-b1341.60](https://github.com/JetBrains/JetBrainsRuntime/issues/171#issuecomment-1248891540)| 15-Jun-2021 |
|
||||
| 2020.3 | [11_0_10-b1145.115](https://github.com/JetBrains/JetBrainsRuntime/issues/171#issuecomment-1249243977) | 21-Jun-2021 |
|
||||
|
||||
## Release Flavours
|
||||
|
||||
There are many kinds of JBR bundles available on the [Releases page](https://github.com/JetBrains/JetBrainsRuntime/releases):
|
||||
|
||||
| Flavour | Description |
|
||||
|---------------|---------------------------------------------------------------------------------------------------------------|
|
||||
| JBR | Contains the Java Runtime Environment (JRE) suitable to _run_ JVM-based programs. |
|
||||
| JBRSDK | Contains the Software Developmet Kit (SDK) suitable to _develop_ and _run_ JVM-based programs. |
|
||||
| JBR with JCEF | Contains both JBR and JCEF; this flavour is bundled by default with all IntelliJ IDEs. |
|
||||
| vanilla | Contains just JBR. |
|
||||
| fastdebug | The native binaries in this bundle are less optimized and are easier to debug. They also run much slower. |
|
||||
| FreeType | The bundle includes the freetype library built from sources; normally, the library is provided by the system. |
|
||||
| Vulkan | The bundle includes experimental Vulkan support. | |
|
||||
| debug symbols | In addition to the usual contents of the bundle the debug information is also included. |
|
||||
|
||||
## Contents
|
||||
- [Welcome to JetBrains Runtime](#welcome-to-jetbrains-runtime)
|
||||
- [Why Use JetBrains Runtime?](#why-use-jetbrains-runtime)
|
||||
|
||||
@@ -18,10 +18,12 @@ function getVersionProp() {
|
||||
}
|
||||
|
||||
DISABLE_WARNINGS_AS_ERRORS=""
|
||||
while getopts ":iw?" o; do
|
||||
CONTINUOUS_INTEGRATION=""
|
||||
while getopts ":iwc?" o; do
|
||||
case "${o}" in
|
||||
i) INC_BUILD=1 ;;
|
||||
w) DISABLE_WARNINGS_AS_ERRORS="--disable-warnings-as-errors" ;;
|
||||
c) CONTINUOUS_INTEGRATION=1 ;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND-1))
|
||||
|
||||
@@ -141,7 +141,7 @@ JBRSDK_BUNDLE=jbrsdk
|
||||
echo Fixing permissions
|
||||
chmod -R a+r $JSDK
|
||||
|
||||
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ]; then
|
||||
if [ "$bundle_type" == "jcef" ]; then
|
||||
git apply -p0 < jb/project/tools/patches/add_jcef_module_aarch64.patch || do_exit $?
|
||||
update_jsdk_mods $JSDK $JCEF_PATH/jmods $JSDK/jmods $JSDK_MODS_DIR || do_exit $?
|
||||
cp $JCEF_PATH/jmods/* $JSDK_MODS_DIR # $JSDK/jmods is not changed
|
||||
@@ -154,7 +154,7 @@ create_image_bundle "jbr${jbr_name_postfix}" "jbr" $JSDK_MODS_DIR "$modules" ||
|
||||
|
||||
# create sdk image bundle
|
||||
modules=$(cat $JSDK/release | grep MODULES | sed s/MODULES=//g | sed s/' '/','/g | sed s/\"//g | sed s/\\n//g) || do_exit $?
|
||||
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
|
||||
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
|
||||
modules=${modules},$(get_mods_list "$JCEF_PATH"/jmods)
|
||||
fi
|
||||
create_image_bundle "$JBRSDK_BUNDLE${jbr_name_postfix}" $JBRSDK_BUNDLE $JSDK_MODS_DIR "$modules" || do_exit $?
|
||||
|
||||
@@ -149,7 +149,7 @@ JBRSDK_BUNDLE=jbrsdk
|
||||
echo Fixing permissions
|
||||
chmod -R a+r $JSDK
|
||||
|
||||
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ]; then
|
||||
if [ "$bundle_type" == "jcef" ]; then
|
||||
git apply -p0 < jb/project/tools/patches/add_jcef_module.patch || do_exit $?
|
||||
update_jsdk_mods $JSDK $JCEF_PATH/jmods $JSDK/jmods $JSDK_MODS_DIR || do_exit $?
|
||||
cp $JCEF_PATH/jmods/* $JSDK_MODS_DIR # $JSDK/jmods is not changed
|
||||
@@ -162,7 +162,7 @@ create_image_bundle "jbr${jbr_name_postfix}" "jbr" $JSDK_MODS_DIR "$modules" ||
|
||||
|
||||
# create sdk image bundle
|
||||
modules=$(cat $JSDK/release | grep MODULES | sed s/MODULES=//g | sed s/' '/','/g | sed s/\"//g | sed s/\\n//g) || do_exit $?
|
||||
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
|
||||
if [ "$bundle_type" == "jcef" ]|| [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
|
||||
modules=${modules},$(get_mods_list "$JCEF_PATH"/jmods)
|
||||
fi
|
||||
create_image_bundle "$JBRSDK_BUNDLE${jbr_name_postfix}" $JBRSDK_BUNDLE $JSDK_MODS_DIR "$modules" || do_exit $?
|
||||
|
||||
@@ -24,6 +24,25 @@ source jb/project/tools/common/scripts/common.sh
|
||||
|
||||
JCEF_PATH=${JCEF_PATH:=./jcef_mac}
|
||||
BOOT_JDK=${BOOT_JDK:=$(/usr/libexec/java_home -v 17)}
|
||||
XCODE_PATH=${XCODE_PATH:-}
|
||||
if [ -d "$XCODE_PATH" ]; then
|
||||
WITH_XCODE_PATH="--with-xcode-path=$XCODE_PATH"
|
||||
else
|
||||
if [ -z "${CONTINUOUS_INTEGRATION:-}" ]; then
|
||||
WITH_XCODE_PATH=""
|
||||
if [ -n "${XCODE_PATH}" ]; then
|
||||
echo "XCode not found in the directory: ${XCODE_PATH}"
|
||||
echo "default XCode will be used"
|
||||
fi
|
||||
else
|
||||
if [ -z "${XCODE_PATH}" ]; then
|
||||
echo "specify XCode via setting XCODE_PATH"
|
||||
else
|
||||
echo "XCode not found in the directory: ${XCODE_PATH}"
|
||||
fi
|
||||
do_exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
function do_configure {
|
||||
sh configure \
|
||||
@@ -42,6 +61,7 @@ function do_configure {
|
||||
$STATIC_CONF_ARGS \
|
||||
$REPRODUCIBLE_BUILD_OPTS \
|
||||
$WITH_ZIPPED_NATIVE_DEBUG_SYMBOLS \
|
||||
$WITH_XCODE_PATH \
|
||||
|| do_exit $?
|
||||
}
|
||||
|
||||
@@ -136,9 +156,11 @@ JSDK_MODS_DIR=$IMAGES_DIR/jmods
|
||||
JBRSDK_BUNDLE=jbrsdk
|
||||
|
||||
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ]; then
|
||||
git apply -p0 < jb/project/tools/patches/add_jcef_module.patch || do_exit $?
|
||||
update_jsdk_mods "$JSDK" "$JCEF_PATH"/jmods "$JSDK"/jmods "$JSDK_MODS_DIR" || do_exit $?
|
||||
cp $JCEF_PATH/jmods/* $JSDK_MODS_DIR # $JSDK/jmods is not changed
|
||||
if [ "$bundle_type" == "jcef" ]; then
|
||||
git apply -p0 < jb/project/tools/patches/add_jcef_module.patch || do_exit $?
|
||||
update_jsdk_mods "$JSDK" "$JCEF_PATH"/jmods "$JSDK"/jmods "$JSDK_MODS_DIR" || do_exit $?
|
||||
cp $JCEF_PATH/jmods/* $JSDK_MODS_DIR # $JSDK/jmods is not changed
|
||||
fi
|
||||
|
||||
jbr_name_postfix="_${bundle_type}"
|
||||
else
|
||||
@@ -151,7 +173,7 @@ create_image_bundle "jbr${jbr_name_postfix}" "jbr" $JSDK_MODS_DIR "$modules" ||
|
||||
|
||||
# create sdk image bundle
|
||||
modules=$(cat "$JSDK"/release | grep MODULES | sed s/MODULES=//g | sed s/' '/','/g | sed s/\"//g | sed s/\\n//g) || do_exit $?
|
||||
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
|
||||
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
|
||||
modules=${modules},$(get_mods_list "$JCEF_PATH"/jmods)
|
||||
fi
|
||||
create_image_bundle "$JBRSDK_BUNDLE${jbr_name_postfix}" "$JBRSDK_BUNDLE" "$JSDK_MODS_DIR" "$modules" || do_exit $?
|
||||
|
||||
@@ -127,12 +127,13 @@ if [ $? -eq 0 ]; then
|
||||
fi
|
||||
|
||||
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ]; then
|
||||
git apply -p0 < jb/project/tools/patches/add_jcef_module_aarch64.patch || do_exit $?
|
||||
update_jsdk_mods "$BUILD_JDK" "$JCEF_PATH"/jmods "$JSDK"/jmods "$JSDK_MODS_DIR" || do_exit $?
|
||||
cp $JCEF_PATH/jmods/* $JSDK_MODS_DIR # $JSDK/jmods is not unchanged
|
||||
|
||||
if [ "$bundle_type" == "jcef" ]; then
|
||||
git apply -p0 < jb/project/tools/patches/add_jcef_module_aarch64.patch || do_exit $?
|
||||
update_jsdk_mods "$BUILD_JDK" "$JCEF_PATH"/jmods "$JSDK"/jmods "$JSDK_MODS_DIR" || do_exit $?
|
||||
cp $JCEF_PATH/jmods/* $JSDK_MODS_DIR # $JSDK/jmods is not unchanged
|
||||
cat $JCEF_PATH/jcef.version >> $JSDK/release
|
||||
fi
|
||||
jbr_name_postfix="_${bundle_type}"
|
||||
cat $JCEF_PATH/jcef.version >> $JSDK/release
|
||||
else
|
||||
jbr_name_postfix=""
|
||||
fi
|
||||
@@ -144,7 +145,7 @@ create_image_bundle "jbr${jbr_name_postfix}" "jbr" $JSDK_MODS_DIR "$modules" ||
|
||||
|
||||
# create sdk image bundle
|
||||
modules=$(cat ${JSDK}/release | grep MODULES | sed s/MODULES=//g | sed s/' '/','/g | sed s/\"//g | sed s/\\r//g | sed s/\\n//g) || do_exit $?
|
||||
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
|
||||
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
|
||||
modules=${modules},$(get_mods_list "$JCEF_PATH"/jmods)
|
||||
fi
|
||||
create_image_bundle "$JBRSDK_BUNDLE${jbr_name_postfix}" "$JBRSDK_BUNDLE" "$JSDK_MODS_DIR" "$modules" || do_exit $?
|
||||
|
||||
@@ -120,12 +120,13 @@ if [ $? -eq 0 ]; then
|
||||
fi
|
||||
|
||||
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ]; then
|
||||
git apply -p0 < jb/project/tools/patches/add_jcef_module.patch || do_exit $?
|
||||
update_jsdk_mods "$JSDK" "$JCEF_PATH"/jmods "$JSDK"/jmods "$JSDK_MODS_DIR" || do_exit $?
|
||||
cp $JCEF_PATH/jmods/* ${JSDK_MODS_DIR} # $JSDK/jmods is not unchanged
|
||||
|
||||
if [ "$bundle_type" == "jcef" ]; then
|
||||
git apply -p0 < jb/project/tools/patches/add_jcef_module.patch || do_exit $?
|
||||
update_jsdk_mods "$JSDK" "$JCEF_PATH"/jmods "$JSDK"/jmods "$JSDK_MODS_DIR" || do_exit $?
|
||||
cp $JCEF_PATH/jmods/* ${JSDK_MODS_DIR} # $JSDK/jmods is not unchanged
|
||||
cat $JCEF_PATH/jcef.version >> $JSDK/release
|
||||
fi
|
||||
jbr_name_postfix="_${bundle_type}"
|
||||
cat $JCEF_PATH/jcef.version >> $JSDK/release
|
||||
else
|
||||
jbr_name_postfix=""
|
||||
fi
|
||||
@@ -137,7 +138,7 @@ create_image_bundle "jbr${jbr_name_postfix}" "jbr" $JSDK_MODS_DIR "$modules" ||
|
||||
|
||||
# create sdk image bundle
|
||||
modules=$(cat ${JSDK}/release | grep MODULES | sed s/MODULES=//g | sed s/' '/','/g | sed s/\"//g | sed s/\\r//g | sed s/\\n//g)
|
||||
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
|
||||
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
|
||||
modules=${modules},$(get_mods_list "$JCEF_PATH"/jmods)
|
||||
fi
|
||||
create_image_bundle "$JBRSDK_BUNDLE${jbr_name_postfix}" "$JBRSDK_BUNDLE" "$JSDK_MODS_DIR" "$modules" || do_exit $?
|
||||
|
||||
@@ -110,9 +110,11 @@ JSDK_MODS_DIR=$IMAGES_DIR/jmods
|
||||
JBRSDK_BUNDLE=jbrsdk
|
||||
|
||||
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ]; then
|
||||
git apply -p0 < jb/project/tools/patches/add_jcef_module.patch || do_exit $?
|
||||
update_jsdk_mods "$JSDK" "$JCEF_PATH"/jmods "$JSDK"/jmods "$JSDK_MODS_DIR" || do_exit $?
|
||||
cp $JCEF_PATH/jmods/* ${JSDK_MODS_DIR} # $JSDK/jmods is not unchanged
|
||||
if [ "$bundle_type" == "jcef" ]; then
|
||||
git apply -p0 < jb/project/tools/patches/add_jcef_module.patch || do_exit $?
|
||||
update_jsdk_mods "$JSDK" "$JCEF_PATH"/jmods "$JSDK"/jmods "$JSDK_MODS_DIR" || do_exit $?
|
||||
cp $JCEF_PATH/jmods/* ${JSDK_MODS_DIR} # $JSDK/jmods is not unchanged
|
||||
fi
|
||||
|
||||
jbr_name_postfix="_${bundle_type}"
|
||||
else
|
||||
|
||||
@@ -127,7 +127,7 @@ AC_DEFUN_ONCE([LIB_SETUP_WAYLAND],
|
||||
VULKAN_FOUND=no
|
||||
|
||||
if test "x${with_vulkan_include}" != x; then
|
||||
AC_MSG_CHECKING([for vulkan.h])
|
||||
AC_MSG_CHECKING([for ${with_vulkan_include}/vulkan/vulkan.h])
|
||||
if test -s "${with_vulkan_include}/vulkan/vulkan.h"; then
|
||||
VULKAN_FOUND=yes
|
||||
VULKAN_FLAGS="-DVK_USE_PLATFORM_WAYLAND_KHR -I${with_vulkan_include} -DVULKAN_ENABLED"
|
||||
@@ -138,14 +138,15 @@ AC_DEFUN_ONCE([LIB_SETUP_WAYLAND],
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x$VULKAN_FOUND" = xno; then
|
||||
# Check vulkan sdk location
|
||||
AC_CHECK_HEADERS([$VULKAN_SDK/include/vulkan/vulkan.h],
|
||||
[ VULKAN_FOUND=yes
|
||||
VULKAN_FLAGS="-DVK_USE_PLATFORM_WAYLAND_KHR -I${VULKAN_SDK}/include -DVULKAN_ENABLED"
|
||||
],
|
||||
[ VULKAN_FOUND=no; break ]
|
||||
)
|
||||
if test "x$VULKAN_FOUND" = xno && test "x${VULKAN_SDK}" != x; then
|
||||
AC_MSG_CHECKING([for ${VULKAN_SDK}/include/vulkan/vulkan.h])
|
||||
if test -s "${VULKAN_SDK}/include/vulkan/vulkan.h"; then
|
||||
VULKAN_FOUND=yes
|
||||
VULKAN_FLAGS="-DVK_USE_PLATFORM_WAYLAND_KHR -I${VULKAN_SDK}/include -DVULKAN_ENABLED"
|
||||
AC_MSG_RESULT([yes])
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x$VULKAN_FOUND" = xno; then
|
||||
|
||||
@@ -375,6 +375,8 @@ inline int Backtrace::get_line_number(Method* method, int bci) {
|
||||
// "no LineNumberTable". JDK tests for -2.
|
||||
line_number = -2;
|
||||
} else {
|
||||
// (DCEVM): Line numbers from the newest version must be used
|
||||
method = method->newest_version();
|
||||
// Returns -1 if no LineNumberTable, and otherwise actual line number
|
||||
line_number = method->line_number_from_bci(bci);
|
||||
}
|
||||
|
||||
@@ -226,8 +226,13 @@ size_t G1HeapSizingPolicy::full_collection_resize_amount(bool& expand) {
|
||||
// results.
|
||||
_g1h->eden_regions_count() * HeapRegion::GrainBytes;
|
||||
|
||||
uintx max_heap_free_ratio = MaxHeapFreeRatio;
|
||||
if (_g1h->gc_cause() == GCCause::_jbr_shrinking_gc_run) {
|
||||
max_heap_free_ratio = MAX2(MinHeapFreeRatio, MIN2(JbrShrinkingGcMaxHeapFreeRatio, max_heap_free_ratio));
|
||||
}
|
||||
|
||||
size_t minimum_desired_capacity = target_heap_capacity(used_after_gc, MinHeapFreeRatio);
|
||||
size_t maximum_desired_capacity = target_heap_capacity(used_after_gc, MaxHeapFreeRatio);
|
||||
size_t maximum_desired_capacity = target_heap_capacity(used_after_gc, max_heap_free_ratio);
|
||||
|
||||
// This assert only makes sense here, before we adjust them
|
||||
// with respect to the min and max heap size.
|
||||
@@ -264,7 +269,7 @@ size_t G1HeapSizingPolicy::full_collection_resize_amount(bool& expand) {
|
||||
log_debug(gc, ergo, heap)("Attempt heap shrinking (capacity higher than max desired capacity). "
|
||||
"Capacity: " SIZE_FORMAT "B occupancy: " SIZE_FORMAT "B live: " SIZE_FORMAT "B "
|
||||
"maximum_desired_capacity: " SIZE_FORMAT "B (" UINTX_FORMAT " %%)",
|
||||
capacity_after_gc, used_after_gc, _g1h->used(), maximum_desired_capacity, MaxHeapFreeRatio);
|
||||
capacity_after_gc, used_after_gc, _g1h->used(), maximum_desired_capacity, max_heap_free_ratio);
|
||||
|
||||
expand = false;
|
||||
return shrink_bytes;
|
||||
|
||||
@@ -279,7 +279,7 @@ class CollectedHeap : public CHeapObj<mtGC> {
|
||||
DEBUG_ONLY(bool is_in_or_null(const void* p) const { return p == nullptr || is_in(p); })
|
||||
|
||||
void set_gc_cause(GCCause::Cause v);
|
||||
GCCause::Cause gc_cause() { return _gc_cause; }
|
||||
GCCause::Cause gc_cause() const { return _gc_cause; }
|
||||
|
||||
oop obj_allocate(Klass* klass, size_t size, TRAPS);
|
||||
virtual oop array_allocate(Klass* klass, size_t size, int length, bool do_zero, TRAPS);
|
||||
|
||||
@@ -132,6 +132,9 @@ const char* GCCause::to_string(GCCause::Cause cause) {
|
||||
case _jbr_gc_run:
|
||||
return "JBR full GC";
|
||||
|
||||
case _jbr_shrinking_gc_run:
|
||||
return "JBR shrinking GC";
|
||||
|
||||
default:
|
||||
return "unknown GCCause";
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ class GCCause : public AllStatic {
|
||||
|
||||
_dcmd_gc_run,
|
||||
_jbr_gc_run,
|
||||
_jbr_shrinking_gc_run,
|
||||
|
||||
_shenandoah_stop_vm,
|
||||
_shenandoah_allocation_failure_evac,
|
||||
@@ -94,7 +95,8 @@ class GCCause : public AllStatic {
|
||||
inline static bool is_user_requested_gc(GCCause::Cause cause) {
|
||||
return (cause == GCCause::_java_lang_system_gc ||
|
||||
cause == GCCause::_dcmd_gc_run ||
|
||||
cause == GCCause::_jbr_gc_run);
|
||||
cause == GCCause::_jbr_gc_run ||
|
||||
cause == GCCause::_jbr_shrinking_gc_run);
|
||||
}
|
||||
|
||||
inline static bool is_explicit_full_gc(GCCause::Cause cause) {
|
||||
|
||||
@@ -232,6 +232,7 @@ void XDriver::collect(const XDriverRequest& request) {
|
||||
case GCCause::_wb_full_gc:
|
||||
case GCCause::_dcmd_gc_run:
|
||||
case GCCause::_jbr_gc_run:
|
||||
case GCCause::_jbr_shrinking_gc_run:
|
||||
case GCCause::_java_lang_system_gc:
|
||||
case GCCause::_full_gc_alot:
|
||||
case GCCause::_scavenge_alot:
|
||||
|
||||
@@ -193,6 +193,7 @@ void ZCollectedHeap::collect(GCCause::Cause cause) {
|
||||
case GCCause::_wb_breakpoint:
|
||||
case GCCause::_dcmd_gc_run:
|
||||
case GCCause::_jbr_gc_run:
|
||||
case GCCause::_jbr_shrinking_gc_run:
|
||||
case GCCause::_java_lang_system_gc:
|
||||
case GCCause::_full_gc_alot:
|
||||
case GCCause::_jvmti_force_gc:
|
||||
|
||||
@@ -243,6 +243,7 @@ static bool should_clear_soft_references(GCCause::Cause cause) {
|
||||
case GCCause::_heap_inspection:
|
||||
case GCCause::_wb_breakpoint:
|
||||
case GCCause::_dcmd_gc_run:
|
||||
case GCCause::_jbr_shrinking_gc_run:
|
||||
case GCCause::_java_lang_system_gc:
|
||||
case GCCause::_full_gc_alot:
|
||||
case GCCause::_jvmti_force_gc:
|
||||
@@ -278,6 +279,7 @@ static bool should_preclean_young(GCCause::Cause cause) {
|
||||
case GCCause::_wb_breakpoint:
|
||||
case GCCause::_dcmd_gc_run:
|
||||
case GCCause::_jbr_gc_run:
|
||||
case GCCause::_jbr_shrinking_gc_run:
|
||||
case GCCause::_java_lang_system_gc:
|
||||
case GCCause::_full_gc_alot:
|
||||
case GCCause::_jvmti_force_gc:
|
||||
@@ -338,6 +340,7 @@ void ZDriverMajor::collect(const ZDriverRequest& request) {
|
||||
case GCCause::_wb_full_gc:
|
||||
case GCCause::_dcmd_gc_run:
|
||||
case GCCause::_jbr_gc_run:
|
||||
case GCCause::_jbr_shrinking_gc_run:
|
||||
case GCCause::_java_lang_system_gc:
|
||||
case GCCause::_full_gc_alot:
|
||||
case GCCause::_jvmti_force_gc:
|
||||
|
||||
@@ -600,6 +600,16 @@ Method* ConstantPoolCacheEntry::get_interesting_method_entry() {
|
||||
// Enhanced RedefineClasses() API support (DCEVM):
|
||||
// Clear cached entry, let it be re-resolved
|
||||
void ConstantPoolCacheEntry::clear_entry() {
|
||||
|
||||
if (is_method_entry() && indices() != constant_pool_index()) {
|
||||
Method* old_method = get_interesting_method_entry();
|
||||
if (old_method != nullptr && old_method->is_old() && old_method->is_deleted()) {
|
||||
// clean up entries with deleted methods
|
||||
initialize_entry(constant_pool_index());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Always clear for invokehandle/invokedynamic to re-resolve them
|
||||
bool clearData = bytecode_1() == Bytecodes::_invokehandle || bytecode_1() == Bytecodes::_invokedynamic;
|
||||
_indices = constant_pool_index();
|
||||
|
||||
@@ -560,6 +560,9 @@ JNI_ENTRY(jint, jni_ThrowNew(JNIEnv *env, jclass clazz, const char *message))
|
||||
} else if (name->equals("java/lang/Exception$JB$$FullGC")) {
|
||||
Universe::heap()->collect(GCCause::_jbr_gc_run);
|
||||
return 0;
|
||||
} else if (name->equals("java/lang/Exception$JB$$ShrinkingGC")) {
|
||||
Universe::heap()->collect(GCCause::_jbr_shrinking_gc_run);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Handle class_loader (THREAD, k->class_loader());
|
||||
|
||||
@@ -1526,17 +1526,17 @@ void VM_EnhancedRedefineClasses::calculate_instance_update_information(Klass* ne
|
||||
}
|
||||
} else {
|
||||
FieldInfo internal_field = holder->field(fd->index());
|
||||
InstanceKlass* old_klass = InstanceKlass::cast(holder->old_version());
|
||||
int java_fields_count = old_klass->java_fields_count();
|
||||
InstanceKlass* maybe_old_klass = holder->is_redefining() ? InstanceKlass::cast(holder->old_version()) : holder;
|
||||
int java_fields_count = maybe_old_klass->java_fields_count();
|
||||
int num_injected;
|
||||
const InjectedField* const injected = JavaClasses::get_injected(old_klass->name(), &num_injected);
|
||||
const InjectedField* const injected = JavaClasses::get_injected(maybe_old_klass->name(), &num_injected);
|
||||
for (int i = java_fields_count; i < java_fields_count+num_injected; i++) {
|
||||
FieldInfo old_field = old_klass->field(i);
|
||||
if (old_field.field_flags().is_injected() &&
|
||||
FieldInfo maybe_old_field = maybe_old_klass->field(i);
|
||||
if (maybe_old_field.field_flags().is_injected() &&
|
||||
internal_field.field_flags().is_injected() &&
|
||||
old_field.lookup_symbol(old_field.name_index()) == internal_field.lookup_symbol(internal_field.name_index())) {
|
||||
copy(old_field.offset(), type2aelembytes(Signature::basic_type(internal_field.signature_injected_dcevm())));
|
||||
if (old_field.offset() < internal_field.offset()) {
|
||||
maybe_old_field.lookup_symbol(maybe_old_field.name_index()) == internal_field.lookup_symbol(internal_field.name_index())) {
|
||||
copy(maybe_old_field.offset(), type2aelembytes(Signature::basic_type(internal_field.signature_injected_dcevm())));
|
||||
if (maybe_old_field.offset() < internal_field.offset()) {
|
||||
_copy_backwards = true;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1439,6 +1439,14 @@ const int ObjectAlignmentInBytes = 8;
|
||||
range(0, 100) \
|
||||
constraint(MaxHeapFreeRatioConstraintFunc,AfterErgo) \
|
||||
\
|
||||
product(uintx, JbrShrinkingGcMaxHeapFreeRatio, 100, MANAGEABLE, \
|
||||
"The maximum percentage of heap free after GC to avoid shrinking" \
|
||||
" when using JBR Shrinking GC run." \
|
||||
" The value is clamped between MinHeapFreeRatio" \
|
||||
" and MaxHeapFreeRatio." \
|
||||
" Defaults to MaxHeapFreeRatio option value.") \
|
||||
range(0, 100) \
|
||||
\
|
||||
product(bool, ShrinkHeapInSteps, true, \
|
||||
"When disabled, informs the GC to shrink the java heap directly" \
|
||||
" to the target size at the next full GC rather than requiring" \
|
||||
|
||||
@@ -126,4 +126,5 @@ public class Exception extends Throwable {
|
||||
private static class JB$$Assertion {}
|
||||
private static class JB$$Event {}
|
||||
private static class JB$$FullGC {}
|
||||
private static class JB$$ShrinkingGC {}
|
||||
}
|
||||
|
||||
@@ -2694,4 +2694,7 @@ public final class System {
|
||||
|
||||
@JBRApi.Provides("SystemUtils#fullGC")
|
||||
private static native void $$jb$FullGC();
|
||||
|
||||
@JBRApi.Provides("SystemUtils#shrinkingGC")
|
||||
private static native void $$jb$ShrinkingGC();
|
||||
}
|
||||
|
||||
@@ -335,3 +335,14 @@ Java_java_lang_System__00024_00024jb_00024FullGC(JNIEnv *env, jclass ign)
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_lang_System__00024_00024jb_00024ShrinkingGC(JNIEnv *env, jclass ign)
|
||||
{
|
||||
jclass cls = (*env)->FindClass(env, "java/lang/Exception$JB$$ShrinkingGC");
|
||||
if (cls != 0) {
|
||||
// Throwing an exception by this name will trigger a full GC with
|
||||
// a special cause indicating the need to shrink the heap
|
||||
(*env)->ThrowNew(env, cls, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,37 +26,111 @@
|
||||
package sun.lwawt;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Point;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import sun.awt.AWTAccessor;
|
||||
import sun.awt.CachedCursorManager;
|
||||
import sun.awt.SunToolkit;
|
||||
|
||||
public abstract class LWCursorManager extends CachedCursorManager {
|
||||
public abstract class LWCursorManager {
|
||||
|
||||
@Override
|
||||
protected Cursor getCursorByPosition(final Point cursorPos, Component c) {
|
||||
/**
|
||||
* A flag to indicate if the update is scheduled, so we don't process it
|
||||
* twice.
|
||||
*/
|
||||
private final AtomicBoolean updatePending = new AtomicBoolean(false);
|
||||
|
||||
protected LWCursorManager() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cursor to correspond the component currently under mouse.
|
||||
*
|
||||
* This method should not be executed on the toolkit thread as it
|
||||
* calls to user code (e.g. Container.findComponentAt).
|
||||
*/
|
||||
public final void updateCursor() {
|
||||
updatePending.set(false);
|
||||
updateCursorImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules updating the cursor on the corresponding event dispatch
|
||||
* thread for the given window.
|
||||
*
|
||||
* This method is called on the toolkit thread as a result of a
|
||||
* native update cursor request (e.g. WM_SETCURSOR on Windows).
|
||||
*/
|
||||
public final void updateCursorLater(final LWWindowPeer window) {
|
||||
if (updatePending.compareAndSet(false, true)) {
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateCursor();
|
||||
}
|
||||
};
|
||||
SunToolkit.executeOnEventHandlerThread(window.getTarget(), r);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCursorImpl() {
|
||||
final Point cursorPos = getCursorPosition();
|
||||
final Component c = findComponent(cursorPos);
|
||||
final Cursor cursor;
|
||||
final Object peer = LWToolkit.targetToPeer(c);
|
||||
if (peer instanceof LWComponentPeer) {
|
||||
final LWComponentPeer<?, ?> lwpeer = (LWComponentPeer<?, ?>) peer;
|
||||
final Point p = lwpeer.getLocationOnScreen();
|
||||
return lwpeer.getCursor(new Point(cursorPos.x - p.x,
|
||||
cursorPos.y - p.y));
|
||||
cursor = lwpeer.getCursor(new Point(cursorPos.x - p.x,
|
||||
cursorPos.y - p.y));
|
||||
} else {
|
||||
cursor = (c != null) ? c.getCursor() : null;
|
||||
}
|
||||
return null;
|
||||
setCursor(cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Component getComponentUnderCursor() {
|
||||
/**
|
||||
* Returns the first visible, enabled and showing component under cursor.
|
||||
* Returns null for modal blocked windows.
|
||||
*
|
||||
* @param cursorPos Current cursor position.
|
||||
* @return Component or null.
|
||||
*/
|
||||
private static final Component findComponent(final Point cursorPos) {
|
||||
final LWComponentPeer<?, ?> peer = LWWindowPeer.getPeerUnderCursor();
|
||||
Component c = null;
|
||||
if (peer != null && peer.getWindowPeerOrSelf().getBlocker() == null) {
|
||||
return peer.getTarget();
|
||||
c = peer.getTarget();
|
||||
if (c instanceof Container) {
|
||||
final Point p = peer.getLocationOnScreen();
|
||||
c = AWTAccessor.getContainerAccessor().findComponentAt(
|
||||
(Container) c, cursorPos.x - p.x, cursorPos.y - p.y, false);
|
||||
|
||||
}
|
||||
while (c != null) {
|
||||
final Object p = AWTAccessor.getComponentAccessor().getPeer(c);
|
||||
if (c.isVisible() && c.isEnabled() && p != null) {
|
||||
break;
|
||||
}
|
||||
c = c.getParent();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Point getLocationOnScreen(Component component) {
|
||||
return AWTAccessor.getComponentAccessor().getPeer(component).getLocationOnScreen();
|
||||
}
|
||||
/**
|
||||
* Returns the current cursor position.
|
||||
*/
|
||||
// TODO: make it public to reuse for MouseInfo
|
||||
protected abstract Point getCursorPosition();
|
||||
|
||||
/**
|
||||
* Sets a cursor. The cursor can be null if the mouse is not over a Java
|
||||
* window.
|
||||
* @param cursor the new {@code Cursor}.
|
||||
*/
|
||||
protected abstract void setCursor(Cursor cursor);
|
||||
}
|
||||
|
||||
@@ -783,7 +783,7 @@ public class LWWindowPeer
|
||||
|
||||
@Override
|
||||
public void notifyUpdateCursor() {
|
||||
getLWToolkit().getCursorManager().updateCursorLater(this.getTarget());
|
||||
getLWToolkit().getCursorManager().updateCursorLater(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -29,6 +29,7 @@ import java.awt.*;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.accessibility.*;
|
||||
import javax.swing.*;
|
||||
@@ -59,6 +60,10 @@ class CAccessible extends CFRetainedResource implements Accessible {
|
||||
}
|
||||
});
|
||||
SELECTED_CHILDREN_MILLISECONDS = scms >= 0 ? scms : SELECTED_CHILDREN_MILLISECONDS_DEFAULT;
|
||||
|
||||
EVENTS_COALESCING_ENABLED = Boolean.parseBoolean(
|
||||
System.getProperty("sun.lwawt.macosx.CAccessible.eventsCoalescingEnabled", "true")
|
||||
);
|
||||
}
|
||||
|
||||
public static CAccessible getCAccessible(final Accessible a) {
|
||||
@@ -83,8 +88,10 @@ class CAccessible extends CFRetainedResource implements Accessible {
|
||||
private static native void menuOpened(long ptr);
|
||||
private static native void menuClosed(long ptr);
|
||||
private static native void menuItemSelected(long ptr);
|
||||
private static native void treeNodeExpanded(long ptr);
|
||||
private static native void treeNodeCollapsed(long ptr);
|
||||
// JBR-7659: don't use this method directly; use {@link #postCoalescedTreeNodeExpanded()} instead
|
||||
private native void treeNodeExpanded(long ptr);
|
||||
// JBR-7659: don't use this method directly; use {@link #postCoalescedTreeNodeCollapsed()} instead
|
||||
private native void treeNodeCollapsed(long ptr);
|
||||
private static native void selectedCellsChanged(long ptr);
|
||||
private static native void tableContentCacheClear(long ptr);
|
||||
private static native void updateZoomCaretFocus(long ptr);
|
||||
@@ -123,6 +130,133 @@ class CAccessible extends CFRetainedResource implements Accessible {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =================================================== JBR-7659 ===================================================
|
||||
|
||||
// Since macOS 15 applications running a lot of nested CFRunLoop s has been crashing.
|
||||
// In AWT case, a new nested CFRunLoop is run whenever macOS asks some a11y info about a UI component.
|
||||
// macOS usually does that when AWT notifies it about changes in UI components.
|
||||
// So, if there happen N changes of UI components in a row, the AppKit thread may have N nested CFRunLoops.
|
||||
// If N is too high (>= ~700), the application will crash.
|
||||
// In JBR-7659 the problem is observed with UI tree expansion/collapsing events, but AFAIU in general it may happen
|
||||
// with any kind of UI change events.
|
||||
// As for now we're covering only those 2 kinds of events to make sure if the solution is effective enough.
|
||||
// The proposed solution is to make sure there is not more than one event of each kind in the AppKit queue
|
||||
// for the same UI component (i.e. for the same CAccessible).
|
||||
|
||||
/** Is a way to disable the fix */
|
||||
private static final boolean EVENTS_COALESCING_ENABLED;
|
||||
|
||||
/**
|
||||
* The variables indicate whether there's an "event" posted by
|
||||
* {@link #treeNodeExpanded}/{@link #treeNodeCollapsed(long)} onto the AppKit thread, but not processed by it yet.
|
||||
*/
|
||||
private final AtomicBoolean isProcessingTreeNodeExpandedEvent = new AtomicBoolean(false),
|
||||
isProcessingTreeNodeCollapsedEvent = new AtomicBoolean(false);
|
||||
/**
|
||||
* The variables indicate whether there was an attempt to post another
|
||||
* {@link #treeNodeExpanded}/{@link #treeNodeCollapsed(long)} while there was already one
|
||||
* on the AppKit thread (no matter if it's still in the queue or is being processed).
|
||||
* It's necessary to make sure there won't be unobserved changes of the component.
|
||||
*/
|
||||
private final AtomicBoolean hasDelayedTreeNodeExpandedEvent = new AtomicBoolean(false),
|
||||
hasDelayedTreeNodeCollapsedEvent = new AtomicBoolean(false);
|
||||
|
||||
private void postCoalescedTreeNodeExpanded() {
|
||||
postCoalescedEventImpl(
|
||||
isProcessingTreeNodeExpandedEvent,
|
||||
hasDelayedTreeNodeExpandedEvent,
|
||||
this::treeNodeExpanded,
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
private void postCoalescedTreeNodeCollapsed() {
|
||||
postCoalescedEventImpl(
|
||||
isProcessingTreeNodeCollapsedEvent,
|
||||
hasDelayedTreeNodeCollapsedEvent,
|
||||
this::treeNodeCollapsed,
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
private static void postCoalescedEventImpl(
|
||||
final AtomicBoolean isProcessingEventFlag,
|
||||
final AtomicBoolean hasDelayedEventFlag,
|
||||
final CFNativeAction eventPostingAction,
|
||||
// a reference to this is passed instead of making the method non-static to make sure the implementation
|
||||
// doesn't accidentally touch anything of the instance by mistake
|
||||
final CAccessible self
|
||||
) {
|
||||
if (!EVENTS_COALESCING_ENABLED) {
|
||||
self.execute(eventPostingAction);
|
||||
return;
|
||||
}
|
||||
|
||||
assert EventQueue.isDispatchThread();
|
||||
|
||||
final var result = self.executeGet(ptr -> {
|
||||
if (isProcessingEventFlag.compareAndSet(false, true)) {
|
||||
hasDelayedEventFlag.set(false);
|
||||
|
||||
try {
|
||||
eventPostingAction.run(ptr);
|
||||
} catch (Exception err) {
|
||||
isProcessingEventFlag.set(false);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
hasDelayedEventFlag.set(true);
|
||||
}
|
||||
return 1;
|
||||
});
|
||||
|
||||
if (result != 1) {
|
||||
// the routine hasn't been executed because there was no native resource (i.e. {@link #ptr} was 0)
|
||||
isProcessingEventFlag.set(false);
|
||||
hasDelayedEventFlag.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Called from native
|
||||
private void onProcessedTreeNodeExpandedEvent() {
|
||||
onProcessedEventImpl(
|
||||
isProcessingTreeNodeExpandedEvent,
|
||||
hasDelayedTreeNodeExpandedEvent,
|
||||
this::postCoalescedTreeNodeExpanded
|
||||
);
|
||||
}
|
||||
|
||||
// Called from native
|
||||
private void onProcessedTreeNodeCollapsedEvent() {
|
||||
onProcessedEventImpl(
|
||||
isProcessingTreeNodeCollapsedEvent,
|
||||
hasDelayedTreeNodeCollapsedEvent,
|
||||
this::postCoalescedTreeNodeCollapsed
|
||||
);
|
||||
}
|
||||
|
||||
private static void onProcessedEventImpl(
|
||||
final AtomicBoolean isProcessingEventFlag,
|
||||
final AtomicBoolean hasDelayedEventFlag,
|
||||
final Runnable postingCoalescedEventRoutine
|
||||
) {
|
||||
if (!EVENTS_COALESCING_ENABLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
isProcessingEventFlag.set(false);
|
||||
|
||||
if (hasDelayedEventFlag.compareAndSet(true, false)) {
|
||||
// We shouldn't call postCoalesced<...> synchronously from here to allow the current CFRunLoop
|
||||
// to finish, thus reducing the current number of nested CFRunLoop s.
|
||||
EventQueue.invokeLater(postingCoalescedEventRoutine);
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================== END of JBR-7659 ================================================
|
||||
|
||||
|
||||
private class AXChangeNotifier implements PropertyChangeListener {
|
||||
|
||||
@Override
|
||||
@@ -184,9 +318,17 @@ class CAccessible extends CFRetainedResource implements Accessible {
|
||||
}
|
||||
|
||||
if (newValue == AccessibleState.EXPANDED) {
|
||||
execute(ptr -> treeNodeExpanded(ptr));
|
||||
if (EVENTS_COALESCING_ENABLED) {
|
||||
postCoalescedTreeNodeExpanded();
|
||||
} else {
|
||||
execute(ptr -> treeNodeExpanded(ptr));
|
||||
}
|
||||
} else if (newValue == AccessibleState.COLLAPSED) {
|
||||
execute(ptr -> treeNodeCollapsed(ptr));
|
||||
if (EVENTS_COALESCING_ENABLED) {
|
||||
postCoalescedTreeNodeCollapsed();
|
||||
} else {
|
||||
execute(ptr -> treeNodeCollapsed(ptr));
|
||||
}
|
||||
}
|
||||
|
||||
if (thisRole == AccessibleRole.COMBO_BOX) {
|
||||
|
||||
@@ -56,7 +56,7 @@ final class CCursorManager extends LWCursorManager {
|
||||
private CCursorManager() { }
|
||||
|
||||
@Override
|
||||
public Point getCursorPosition() {
|
||||
protected Point getCursorPosition() {
|
||||
final Point2D nativePosition = nativeGetCursorPosition();
|
||||
return new Point((int)nativePosition.getX(), (int)nativePosition.getY());
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
#import "NSApplicationAWT.h"
|
||||
#import "PropertiesUtilities.h"
|
||||
#import "ApplicationDelegate.h"
|
||||
#import "SystemHotkey.h"
|
||||
|
||||
#import "sun_lwawt_macosx_LWCToolkit.h"
|
||||
|
||||
@@ -263,8 +262,6 @@ static void setUpAWTAppKit(BOOL installObservers)
|
||||
CFRelease(notBusyObserver);
|
||||
|
||||
setBusy(YES);
|
||||
|
||||
[SystemHotkey setUp];
|
||||
}
|
||||
|
||||
JNIEnv* env = [ThreadUtilities getJNIEnv];
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright 2024 JetBrains s.r.o.
|
||||
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
//
|
||||
// This code is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License version 2 only, as
|
||||
// published by the Free Software Foundation. Oracle designates this
|
||||
// particular file as subject to the "Classpath" exception as provided
|
||||
// by Oracle in the LICENSE file that accompanied this code.
|
||||
//
|
||||
// This code 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
|
||||
// version 2 for more details (a copy is included in the LICENSE file that
|
||||
// accompanied this code).
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License version
|
||||
// 2 along with this work; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
// or visit www.oracle.com if you need additional information or have any
|
||||
// questions.
|
||||
|
||||
|
||||
#ifndef JBR_SYSTEMHOTKEY_H
|
||||
#define JBR_SYSTEMHOTKEY_H
|
||||
|
||||
#import <objc/NSObject.h>
|
||||
|
||||
@interface SystemHotkey : NSObject
|
||||
+ (void)setUp;
|
||||
@end
|
||||
|
||||
#endif //JBR_SYSTEMHOTKEY_H
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "SystemHotkey.h"
|
||||
#include <pthread.h>
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Carbon/Carbon.h>
|
||||
@@ -15,6 +15,10 @@
|
||||
|
||||
extern JavaVM *jvm;
|
||||
|
||||
@interface SystemHotkey : NSObject
|
||||
+ (void)subscribeToChanges;
|
||||
@end
|
||||
|
||||
enum LOG_LEVEL {
|
||||
LL_TRACE,
|
||||
LL_DEBUG,
|
||||
@@ -23,7 +27,7 @@ enum LOG_LEVEL {
|
||||
LL_ERROR
|
||||
};
|
||||
|
||||
void plog(int logLevel, const char *formatMsg, ...) {
|
||||
static void plog(int logLevel, const char *formatMsg, ...) {
|
||||
if (!jvm)
|
||||
return;
|
||||
if (logLevel < LL_TRACE || logLevel > LL_ERROR || formatMsg == NULL)
|
||||
@@ -265,12 +269,20 @@ static const struct SymbolicHotKey defaultSymbolicHotKeys[] = {
|
||||
|
||||
static const int numSymbolicHotkeys = sizeof(defaultSymbolicHotKeys) / sizeof(defaultSymbolicHotKeys[0]);
|
||||
|
||||
// Current state of system shortcuts.
|
||||
// Should only be read and written inside a @synchronized([SystemHotkey class]) block
|
||||
static struct SymbolicHotKey currentSymbolicHotkeys[numSymbolicHotkeys];
|
||||
static struct SystemHotkeyState {
|
||||
bool symbolicHotkeysFilled;
|
||||
struct SymbolicHotKey currentSymbolicHotkeys[numSymbolicHotkeys];
|
||||
|
||||
// Should only be read and written inside a @synchronized([SystemHotkey class]) block
|
||||
static bool subscribedToShortcutUpdates = false;
|
||||
bool initialized;
|
||||
bool enabled;
|
||||
|
||||
pthread_mutex_t mutex;
|
||||
} state = {
|
||||
.symbolicHotkeysFilled = false,
|
||||
.initialized = false,
|
||||
.enabled = false,
|
||||
.mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER,
|
||||
};
|
||||
|
||||
@interface DefaultParams: NSObject
|
||||
@property (assign) BOOL enabled;
|
||||
@@ -454,9 +466,10 @@ static void updateAppleSymbolicHotkeysCache() {
|
||||
struct SymbolicHotKey hotkeys[numSymbolicHotkeys];
|
||||
readAppleSymbolicHotkeys(hotkeys);
|
||||
|
||||
@synchronized ([SystemHotkey class]) {
|
||||
memcpy(currentSymbolicHotkeys, hotkeys, numSymbolicHotkeys * sizeof(struct SymbolicHotKey));
|
||||
}
|
||||
pthread_mutex_lock(&state.mutex);
|
||||
memcpy(state.currentSymbolicHotkeys, hotkeys, sizeof(hotkeys));
|
||||
state.symbolicHotkeysFilled = true;
|
||||
pthread_mutex_unlock(&state.mutex);
|
||||
}
|
||||
|
||||
static void iterateAppleSymbolicHotkeys(struct SymbolicHotKey hotkeys[numSymbolicHotkeys], Visitor visitorBlock) {
|
||||
@@ -556,19 +569,46 @@ static void readPbsHotkeys(Visitor visitorBlock) {
|
||||
}
|
||||
}
|
||||
|
||||
static void readAppleSymbolicHotkeysCached(struct SymbolicHotKey hotkeys[numSymbolicHotkeys]) {
|
||||
@synchronized ([SystemHotkey class]) {
|
||||
if (!subscribedToShortcutUpdates) {
|
||||
[SystemHotkey setUp];
|
||||
}
|
||||
|
||||
memcpy(hotkeys, currentSymbolicHotkeys, numSymbolicHotkeys * sizeof(struct SymbolicHotKey));
|
||||
static bool ensureInitializedAndEnabled() {
|
||||
pthread_mutex_lock(&state.mutex);
|
||||
if (state.initialized) {
|
||||
bool enabled = state.enabled;
|
||||
pthread_mutex_unlock(&state.mutex);
|
||||
return enabled;
|
||||
}
|
||||
|
||||
// JBR-8422
|
||||
const NSOperatingSystemVersion macOSVersion = [[NSProcessInfo processInfo] operatingSystemVersion];
|
||||
if (macOSVersion.majorVersion > 15 || (macOSVersion.majorVersion == 15 && macOSVersion.minorVersion >= 4)) {
|
||||
state.initialized = true;
|
||||
state.enabled = false;
|
||||
pthread_mutex_unlock(&state.mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
state.initialized = true;
|
||||
state.enabled = true;
|
||||
[SystemHotkey subscribeToChanges];
|
||||
pthread_mutex_unlock(&state.mutex);
|
||||
updateAppleSymbolicHotkeysCache();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void readAppleSymbolicHotkeysCached(struct SymbolicHotKey hotkeys[numSymbolicHotkeys]) {
|
||||
memset(hotkeys, 0, sizeof(struct SymbolicHotKey) * numSymbolicHotkeys);
|
||||
|
||||
pthread_mutex_lock(&state.mutex);
|
||||
if (state.symbolicHotkeysFilled) {
|
||||
memcpy(hotkeys, state.currentSymbolicHotkeys, sizeof(struct SymbolicHotKey) * numSymbolicHotkeys);
|
||||
}
|
||||
pthread_mutex_unlock(&state.mutex);
|
||||
}
|
||||
|
||||
static void readSystemHotkeysImpl(Visitor visitorBlock) {
|
||||
// Normally, SystemHotkey would get initialized in LWCToolkit initialization.
|
||||
// But since we can (theoretically) use this API from headless, let's check again.
|
||||
if (!ensureInitializedAndEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct SymbolicHotKey hotkeys[numSymbolicHotkeys];
|
||||
readAppleSymbolicHotkeysCached(hotkeys);
|
||||
@@ -578,40 +618,32 @@ static void readSystemHotkeysImpl(Visitor visitorBlock) {
|
||||
}
|
||||
|
||||
@implementation SystemHotkey
|
||||
+ (void)setUp {
|
||||
// This should be called on LWCToolkit initialization.
|
||||
+ (void)subscribeToChanges {
|
||||
// Subscribe to changes
|
||||
NSUserDefaults *symbolicHotKeys = [[NSUserDefaults alloc] initWithSuiteName:@"com.apple.symbolichotkeys"];
|
||||
[symbolicHotKeys addObserver:self forKeyPath:@"AppleSymbolicHotKeys" options:NSKeyValueObservingOptionNew
|
||||
context:nil];
|
||||
|
||||
@synchronized (self) {
|
||||
if (subscribedToShortcutUpdates) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update cached values
|
||||
updateAppleSymbolicHotkeysCache();
|
||||
|
||||
// Subscribe to changes
|
||||
NSUserDefaults *symbolicHotKeys = [[NSUserDefaults alloc] initWithSuiteName:@"com.apple.symbolichotkeys"];
|
||||
[symbolicHotKeys addObserver:self forKeyPath:@"AppleSymbolicHotKeys" options:NSKeyValueObservingOptionNew
|
||||
context:nil];
|
||||
|
||||
NSUserDefaults *pbsHotKeys = [[NSUserDefaults alloc] initWithSuiteName:@"pbs"];
|
||||
[pbsHotKeys addObserver:self forKeyPath:@"NSServicesStatus" options:NSKeyValueObservingOptionNew context:nil];
|
||||
|
||||
subscribedToShortcutUpdates = true;
|
||||
}
|
||||
NSUserDefaults *pbsHotKeys = [[NSUserDefaults alloc] initWithSuiteName:@"pbs"];
|
||||
[pbsHotKeys addObserver:self forKeyPath:@"NSServicesStatus" options:NSKeyValueObservingOptionNew context:nil];
|
||||
}
|
||||
|
||||
+ (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context {
|
||||
// Called after AppleSymbolicHotKeys or pbs hotkeys change.
|
||||
// This method can be called from any thread.
|
||||
|
||||
if ([keyPath isEqualToString:@"AppleSymbolicHotKeys"]) {
|
||||
updateAppleSymbolicHotkeysCache();
|
||||
}
|
||||
|
||||
// This method should only be called from the main thread, but let's check anyway
|
||||
if (pthread_main_np() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Since this notification is sent *after* the configuration was updated,
|
||||
// the user can safely re-read the hotkeys info after receiving this callback.
|
||||
// On the Java side, this simply enqueues the change handler to run on the EDT later.
|
||||
|
||||
JNIEnv* env = [ThreadUtilities getJNIEnv];
|
||||
DECLARE_CLASS(jc_SystemHotkey, "java/awt/desktop/SystemHotkey");
|
||||
DECLARE_STATIC_METHOD(jsm_onChange, jc_SystemHotkey, "onChange", "()V");
|
||||
@@ -621,12 +653,13 @@ static void readSystemHotkeysImpl(Visitor visitorBlock) {
|
||||
@end
|
||||
|
||||
bool isSystemShortcut_NextWindowInApplication(NSUInteger modifiersMask, int keyCode, NSString *chars) {
|
||||
struct SymbolicHotKey shortcut;
|
||||
@synchronized ([SystemHotkey class]) {
|
||||
if (!subscribedToShortcutUpdates) {
|
||||
[SystemHotkey setUp];
|
||||
struct SymbolicHotKey shortcut = defaultSymbolicHotKeys[Shortcut_FocusNextApplicationWindow];
|
||||
if (ensureInitializedAndEnabled()) {
|
||||
pthread_mutex_lock(&state.mutex);
|
||||
if (state.symbolicHotkeysFilled) {
|
||||
shortcut = state.currentSymbolicHotkeys[Shortcut_FocusNextApplicationWindow];
|
||||
}
|
||||
shortcut = currentSymbolicHotkeys[Shortcut_FocusNextApplicationWindow];
|
||||
pthread_mutex_unlock(&state.mutex);
|
||||
}
|
||||
|
||||
int ignoredModifiers = NSAlphaShiftKeyMask | NSFunctionKeyMask | NSNumericPadKeyMask | NSHelpKeyMask;
|
||||
|
||||
@@ -1321,20 +1321,55 @@ static jobject sAccessibilityClass = NULL;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
static void treeNodeExpandedCollapsedImpl(
|
||||
JNIEnv * const env,
|
||||
const jobject cAccessible,
|
||||
const jlong ccAxJavaPtr,
|
||||
// @selector(postTreeNodeExpanded) or @selector(postTreeNodeCollapsed)
|
||||
const SEL ccAxSelector,
|
||||
// "onProcessedTreeNodeExpandedEvent" or "onProcessedTreeNodeCollapsedEvent"
|
||||
const char* const cAccessibleCompletionUpcallName
|
||||
) {
|
||||
assert((ccAxSelector != NULL));
|
||||
assert((cAccessibleCompletionUpcallName != NULL));
|
||||
|
||||
JNI_COCOA_ENTER(env);
|
||||
const jobject cAccessibleGlobal = (*env)->NewGlobalRef(env, cAccessible);
|
||||
|
||||
if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
|
||||
if (cAccessibleGlobal != NULL) {
|
||||
(*env)->DeleteGlobalRef(env, cAccessibleGlobal);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const id ccAx = (CommonComponentAccessibility *)jlong_to_ptr(ccAxJavaPtr);
|
||||
|
||||
[ThreadUtilities performOnMainThreadWaiting:NO block:^{
|
||||
[ccAx performSelector:ccAxSelector];
|
||||
|
||||
JNIEnv * const env = [ThreadUtilities getJNIEnv];
|
||||
if ( (env != NULL) && (cAccessibleGlobal != NULL) ) {
|
||||
(void)JNU_CallMethodByName(env, NULL, cAccessibleGlobal, cAccessibleCompletionUpcallName, "()V");
|
||||
|
||||
(*env)->DeleteGlobalRef(env, cAccessibleGlobal);
|
||||
|
||||
CHECK_EXCEPTION();
|
||||
}
|
||||
}];
|
||||
JNI_COCOA_EXIT(env);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_lwawt_macosx_CAccessible
|
||||
* Method: treeNodeExpanded
|
||||
* Signature: (J)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_treeNodeExpanded
|
||||
(JNIEnv *env, jclass jklass, jlong element)
|
||||
(JNIEnv *env, jobject self, jlong element)
|
||||
{
|
||||
JNI_COCOA_ENTER(env);
|
||||
[ThreadUtilities performOnMainThread:@selector(postTreeNodeExpanded)
|
||||
on:(CommonComponentAccessibility *)jlong_to_ptr(element)
|
||||
withObject:nil
|
||||
waitUntilDone:NO];
|
||||
JNI_COCOA_EXIT(env);
|
||||
treeNodeExpandedCollapsedImpl(env, self, element, @selector(postTreeNodeExpanded), "onProcessedTreeNodeExpandedEvent");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1343,14 +1378,9 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_treeNodeExpanded
|
||||
* Signature: (J)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_treeNodeCollapsed
|
||||
(JNIEnv *env, jclass jklass, jlong element)
|
||||
(JNIEnv *env, jobject self, jlong element)
|
||||
{
|
||||
JNI_COCOA_ENTER(env);
|
||||
[ThreadUtilities performOnMainThread:@selector(postTreeNodeCollapsed)
|
||||
on:(CommonComponentAccessibility *)jlong_to_ptr(element)
|
||||
withObject:nil
|
||||
waitUntilDone:NO];
|
||||
JNI_COCOA_EXIT(env);
|
||||
treeNodeExpandedCollapsedImpl(env, self, element, @selector(postTreeNodeCollapsed), "onProcessedTreeNodeCollapsedEvent");
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -39,6 +39,7 @@ import com.sun.java.swing.plaf.gtk.GTKConstants.ShadowType;
|
||||
import com.sun.java.swing.plaf.gtk.GTKConstants.TextDirection;
|
||||
|
||||
import sun.awt.image.SunWritableRaster;
|
||||
import sun.java2d.SunGraphics2D;
|
||||
import sun.swing.ImageCache;
|
||||
|
||||
/**
|
||||
@@ -118,9 +119,16 @@ class GTKEngine {
|
||||
private static HashMap<Region, Object> regionToWidgetTypeMap;
|
||||
private ImageCache cache = new ImageCache(CACHE_SIZE);
|
||||
private int x0, y0, w0, h0;
|
||||
private int scale = 1;
|
||||
private Graphics graphics;
|
||||
private Object[] cacheArgs;
|
||||
|
||||
private final static boolean isWayland;
|
||||
|
||||
static {
|
||||
isWayland = "sun.awt.wl.WLToolkit".equals(Toolkit.getDefaultToolkit().getClass().getName());
|
||||
}
|
||||
|
||||
private native void native_paint_arrow(
|
||||
int widgetType, int state, int shadowType, String detail,
|
||||
int x, int y, int width, int height, int arrowType);
|
||||
@@ -171,7 +179,7 @@ class GTKEngine {
|
||||
double min, double max,
|
||||
double visible);
|
||||
|
||||
private native void nativeStartPainting(int w, int h);
|
||||
private native void nativeStartPainting(int w, int h, int scale);
|
||||
private native int nativeFinishPainting(int[] buffer, int width, int height);
|
||||
private native void native_switch_theme();
|
||||
|
||||
@@ -533,14 +541,7 @@ class GTKEngine {
|
||||
native_paint_background(widget, state, x - x0, y - y0, w, h);
|
||||
}
|
||||
|
||||
private static final ColorModel[] COLOR_MODELS = {
|
||||
// Transparency.OPAQUE
|
||||
new DirectColorModel(24, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000),
|
||||
// Transparency.BITMASK
|
||||
new DirectColorModel(25, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x01000000),
|
||||
// Transparency.TRANSLUCENT
|
||||
ColorModel.getRGBdefault(),
|
||||
};
|
||||
private static final ColorModel[] COLOR_MODELS = new ColorModel[3];
|
||||
|
||||
private static final int[][] BAND_OFFSETS = {
|
||||
{ 0x00ff0000, 0x0000ff00, 0x000000ff }, // OPAQUE
|
||||
@@ -562,9 +563,10 @@ class GTKEngine {
|
||||
}
|
||||
|
||||
// look for cached image
|
||||
Image img = cache.getImage(getClass(), null, w, h, args);
|
||||
Image img = cache.getImage(getClass(), null, w * scale, h * scale, args);
|
||||
if (img != null) {
|
||||
g.drawImage(img, x, y, null);
|
||||
prepareGraphics(g);
|
||||
g.drawImage(img, x, y, w, h, null);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -575,7 +577,11 @@ class GTKEngine {
|
||||
*/
|
||||
public void startPainting(Graphics g,
|
||||
int x, int y, int w, int h, Object... args) {
|
||||
nativeStartPainting(w, h);
|
||||
if (isWayland) { // The X11 impl of GTKEngine is not HiDPI-aware yet
|
||||
double scaleX = graphics instanceof SunGraphics2D sg2d ? sg2d.getTransform().getScaleX() : 1.0;
|
||||
scale = scaleX <= 1.0 ? 1 : (int) scaleX;
|
||||
}
|
||||
nativeStartPainting(w, h, scale);
|
||||
x0 = x;
|
||||
y0 = y;
|
||||
w0 = w;
|
||||
@@ -598,7 +604,9 @@ class GTKEngine {
|
||||
* and paint it.
|
||||
*/
|
||||
public BufferedImage finishPainting(boolean useCache) {
|
||||
DataBufferInt dataBuffer = new DataBufferInt(w0 * h0);
|
||||
int nativeW = w0 * scale;
|
||||
int nativeH = h0 * scale;
|
||||
DataBufferInt dataBuffer = new DataBufferInt(nativeW * nativeH);
|
||||
// Note that stealData() requires a markDirty() afterwards
|
||||
// since we modify the data in it.
|
||||
int transparency =
|
||||
@@ -608,17 +616,40 @@ class GTKEngine {
|
||||
|
||||
int[] bands = BAND_OFFSETS[transparency - 1];
|
||||
WritableRaster raster = Raster.createPackedRaster(
|
||||
dataBuffer, w0, h0, w0, bands, null);
|
||||
dataBuffer, nativeW, nativeH, nativeW, bands, null);
|
||||
|
||||
ColorModel cm = COLOR_MODELS[transparency - 1];
|
||||
BufferedImage img = new BufferedImage(cm, raster, false, null);
|
||||
ColorModel cm = colorModelFor(transparency);
|
||||
BufferedImage img = new BufferedImage(cm, raster, true, null);
|
||||
if (useCache) {
|
||||
cache.setImage(getClass(), null, w0, h0, cacheArgs, img);
|
||||
cache.setImage(getClass(), null, nativeW, nativeH, cacheArgs, img);
|
||||
}
|
||||
graphics.drawImage(img, x0, y0, null);
|
||||
|
||||
prepareGraphics(graphics);
|
||||
graphics.drawImage(img, x0, y0, w0, h0, null);
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
private ColorModel colorModelFor(int transparency) {
|
||||
synchronized (COLOR_MODELS) {
|
||||
int index = transparency - 1;
|
||||
if (COLOR_MODELS[index] == null) {
|
||||
COLOR_MODELS[index] = GraphicsEnvironment.getLocalGraphicsEnvironment()
|
||||
.getDefaultScreenDevice().getDefaultConfiguration()
|
||||
.getColorModel(transparency);
|
||||
}
|
||||
return COLOR_MODELS[index];
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareGraphics(Graphics graphics) {
|
||||
if (scale > 1 && graphics instanceof SunGraphics2D sg2d) {
|
||||
sg2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
sg2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
||||
sg2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify native layer of theme change, and flush cache
|
||||
*/
|
||||
|
||||
@@ -44,8 +44,6 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import sun.awt.SunToolkit;
|
||||
import sun.awt.UNIXToolkit;
|
||||
import sun.awt.OSInfo;
|
||||
import sun.awt.X11GraphicsDevice;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
import sun.swing.DefaultLayoutStyle;
|
||||
import sun.swing.SwingAccessor;
|
||||
@@ -118,6 +116,12 @@ public class GTKLookAndFeel extends SynthLookAndFeel {
|
||||
return IS_3;
|
||||
}
|
||||
|
||||
private final static boolean isWayland;
|
||||
|
||||
static {
|
||||
isWayland = "sun.awt.wl.WLToolkit".equals(Toolkit.getDefaultToolkit().getClass().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a swing constant to a GTK constant.
|
||||
*/
|
||||
@@ -1451,16 +1455,18 @@ public class GTKLookAndFeel extends SynthLookAndFeel {
|
||||
aaTextInfo = new HashMap<>(2);
|
||||
SwingUtilities2.putAATextInfo(gtkAAFontSettingsCond, aaTextInfo);
|
||||
|
||||
Object value = GTKEngine.INSTANCE.getSetting(GTKEngine.Settings.GTK_XFT_DPI);
|
||||
if (value instanceof Integer) {
|
||||
int dpi = ((Integer)value).intValue() / 1024;
|
||||
if (dpi == -1) {
|
||||
dpi = 96;
|
||||
if (!isWayland) {
|
||||
Object value = GTKEngine.INSTANCE.getSetting(GTKEngine.Settings.GTK_XFT_DPI);
|
||||
if (value instanceof Integer) {
|
||||
int dpi = ((Integer) value).intValue() / 1024;
|
||||
if (dpi == -1) {
|
||||
dpi = 96;
|
||||
}
|
||||
if (dpi < 50) {
|
||||
dpi = 50;
|
||||
}
|
||||
sun.awt.X11GraphicsDevice.setXftDpi(dpi);
|
||||
}
|
||||
if (dpi < 50) {
|
||||
dpi = 50;
|
||||
}
|
||||
X11GraphicsDevice.setXftDpi(dpi);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4887,9 +4887,6 @@ public abstract class Component implements ImageObserver, MenuContainer,
|
||||
eventLog.finest("{0}", e);
|
||||
}
|
||||
|
||||
if (id == MouseEvent.MOUSE_ENTERED && getToolkit() instanceof SunToolkit toolkit) {
|
||||
toolkit.updateLastMouseEventComponent(this);
|
||||
}
|
||||
/*
|
||||
* 0. Set timestamp and modifiers of current event.
|
||||
*/
|
||||
@@ -7171,12 +7168,6 @@ public abstract class Component implements ImageObserver, MenuContainer,
|
||||
setGlobalPermanentFocusOwner(null);
|
||||
}
|
||||
|
||||
if (getToolkit() instanceof SunToolkit toolkit) {
|
||||
if (toolkit.getLastMouseEventComponent() == this) {
|
||||
toolkit.updateLastMouseEventComponent(null);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (getTreeLock()) {
|
||||
if (isFocusOwner() && KeyboardFocusManager.isAutoFocusTransferEnabledFor(this)) {
|
||||
transferFocus(true);
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.awt;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Point;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public abstract class CachedCursorManager {
|
||||
|
||||
/**
|
||||
* A flag to indicate if the update is scheduled, so we don't process it
|
||||
* twice.
|
||||
*/
|
||||
private final AtomicBoolean updatePending = new AtomicBoolean(false);
|
||||
|
||||
/**
|
||||
* Sets the cursor to correspond the component currently under mouse.
|
||||
*
|
||||
* This method should not be executed on the toolkit thread as it
|
||||
* calls to user code (e.g. Container.findComponentAt).
|
||||
*/
|
||||
public final void updateCursor() {
|
||||
updatePending.set(false);
|
||||
updateCursorImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules updating the cursor on the corresponding event dispatch
|
||||
* thread for the given window.
|
||||
*
|
||||
* This method is called on the toolkit thread as a result of a
|
||||
* native update cursor request (e.g. WM_SETCURSOR on Windows).
|
||||
*/
|
||||
public final void updateCursorLater(final Component window) {
|
||||
if (updatePending.compareAndSet(false, true)) {
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateCursor();
|
||||
}
|
||||
};
|
||||
SunToolkit.executeOnEventHandlerThread(window, r);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract Cursor getCursorByPosition(final Point cursorPos, Component c);
|
||||
|
||||
private void updateCursorImpl() {
|
||||
final Point cursorPos = getCursorPosition();
|
||||
final Component component = getComponentUnderCursor();
|
||||
if (component != null) {
|
||||
synchronized (component.getTreeLock()) {
|
||||
final Component parentComponent = findComponent(cursorPos, component);
|
||||
Cursor cursor = getCursorByPosition(cursorPos, parentComponent);
|
||||
|
||||
if (cursor == null) {
|
||||
cursor = (parentComponent != null) ? parentComponent.getCursor() : null;
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
setCursor(cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract Component getComponentUnderCursor();
|
||||
protected abstract Point getLocationOnScreen(Component component);
|
||||
|
||||
/**
|
||||
* Returns the first visible, enabled and showing component under cursor.
|
||||
* Returns null for modal blocked windows.
|
||||
*
|
||||
* @param cursorPos Current cursor position.
|
||||
* @return Component or null.
|
||||
*/
|
||||
private Component findComponent(final Point cursorPos, Component currentComponent) {
|
||||
Component component = currentComponent;
|
||||
if (component instanceof Container && component.isShowing()) {
|
||||
final Point p = getLocationOnScreen(component);
|
||||
component = AWTAccessor.getContainerAccessor().findComponentAt(
|
||||
(Container) component, cursorPos.x - p.x, cursorPos.y - p.y, false);
|
||||
|
||||
}
|
||||
while (component != null) {
|
||||
final Object p = AWTAccessor.getComponentAccessor().getPeer(component);
|
||||
if (component.isVisible() && component.isEnabled() && p != null) {
|
||||
break;
|
||||
}
|
||||
component = component.getParent();
|
||||
}
|
||||
return component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current cursor position.
|
||||
*/
|
||||
public abstract Point getCursorPosition();
|
||||
|
||||
/**
|
||||
* Sets a cursor. The cursor can be null if the mouse is not over a Java
|
||||
* window.
|
||||
* @param cursor the new {@code Cursor}.
|
||||
*/
|
||||
protected abstract void setCursor(Cursor cursor);
|
||||
}
|
||||
@@ -236,17 +236,6 @@ public abstract class SunToolkit extends Toolkit
|
||||
AccessController.doPrivileged(new GetBooleanAction("awt.lock.fair")));
|
||||
private static final Condition AWT_LOCK_COND = AWT_LOCK.newCondition();
|
||||
|
||||
/*
|
||||
* A component where the last mouse event came to. Used by cursor manager to
|
||||
* find the component under cursor. Currently, uses only on Windows
|
||||
*/
|
||||
public void updateLastMouseEventComponent(Component component) {
|
||||
}
|
||||
|
||||
public Component getLastMouseEventComponent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public interface AwtLockListener {
|
||||
void afterAwtLocked();
|
||||
void beforeAwtUnlocked();
|
||||
|
||||
@@ -35,6 +35,7 @@ public class VKInstance {
|
||||
|
||||
private static final PlatformLogger log = PlatformLogger.getLogger("sun.java2d.vulkan.VKInstance");
|
||||
private static Boolean initialized;
|
||||
private static Boolean sdAccelerated;
|
||||
|
||||
private static native boolean initNative(long nativePtr, boolean verbose, int deviceNumber);
|
||||
|
||||
@@ -55,10 +56,16 @@ public class VKInstance {
|
||||
final int deviceNumber = parsedDeviceNumber;
|
||||
final boolean verbose = "True".equals(vulkanOption);
|
||||
System.loadLibrary("awt");
|
||||
@SuppressWarnings("removal")
|
||||
String sdOption = AccessController.doPrivileged(
|
||||
(PrivilegedAction<String>) () -> System.getProperty("sun.java2d.vulkan.accelsd", ""));
|
||||
initialized = initNative(nativePtr, verbose, deviceNumber);
|
||||
sdAccelerated = initialized && "true".equalsIgnoreCase(sdOption);
|
||||
} else initialized = false;
|
||||
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("Vulkan rendering enabled: " + (initialized ? "YES" : "NO"));
|
||||
log.fine("Vulkan accelerated surface data enabled: " + (sdAccelerated ? "YES" : "NO"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,4 +73,9 @@ public class VKInstance {
|
||||
if (initialized == null) throw new RuntimeException("Vulkan not initialized");
|
||||
return initialized;
|
||||
}
|
||||
|
||||
public static boolean isSurfaceDataAccelerated() {
|
||||
if (initialized == null) throw new RuntimeException("Vulkan not initialized");
|
||||
return sdAccelerated;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2025 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.java2d.vulkan;
|
||||
|
||||
import java.awt.GraphicsConfiguration;
|
||||
import java.awt.Image;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.image.ColorModel;
|
||||
import sun.java2d.SurfaceData;
|
||||
import sun.java2d.pipe.BufferedContext;
|
||||
|
||||
/**
|
||||
* SurfaceData object representing an off-screen buffer
|
||||
*/
|
||||
public class VKOffScreenSurfaceData extends VKSurfaceData {
|
||||
private final Image offscreenImage;
|
||||
private native void initOps(int width, int height);
|
||||
|
||||
public VKOffScreenSurfaceData(VKGraphicsConfig gc, Image image, ColorModel cm,
|
||||
int type, int width, int height)
|
||||
{
|
||||
super(gc, cm, type, width, height);
|
||||
offscreenImage = image;
|
||||
initOps(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SurfaceData getReplacement() {
|
||||
return restoreContents(offscreenImage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphicsConfiguration getDeviceConfiguration() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNativeResource(int resType) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getBounds() {
|
||||
return new Rectangle(width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns destination Image associated with this SurfaceData.
|
||||
*/
|
||||
@Override
|
||||
public Object getDestination() {
|
||||
return offscreenImage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedContext getContext() {
|
||||
return getGraphicsConfig().getContext();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isOnScreen() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -26,89 +26,40 @@
|
||||
|
||||
package sun.java2d.vulkan;
|
||||
|
||||
import jdk.internal.misc.InnocuousThread;
|
||||
import sun.java2d.pipe.RenderQueue;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
import static sun.java2d.pipe.BufferedOpCodes.SYNC;
|
||||
|
||||
/**
|
||||
* VK-specific implementation of RenderQueue. This class provides a
|
||||
* single (daemon) thread that is responsible for periodically flushing
|
||||
* the queue.
|
||||
* VK-specific implementation of RenderQueue.
|
||||
*/
|
||||
public class VKRenderQueue extends RenderQueue {
|
||||
|
||||
private static VKRenderQueue theInstance;
|
||||
private final QueueFlusher flusher;
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
private VKRenderQueue() {
|
||||
/*
|
||||
* The thread must be a member of a thread group
|
||||
* which will not get GCed before VM exit.
|
||||
*/
|
||||
flusher = AccessController.doPrivileged((PrivilegedAction<QueueFlusher>) QueueFlusher::new);
|
||||
}
|
||||
private static final VKRenderQueue theInstance = new VKRenderQueue();
|
||||
|
||||
/**
|
||||
* Returns the single VKRenderQueue instance. If it has not yet been
|
||||
* initialized, this method will first construct the single instance
|
||||
* before returning it.
|
||||
* Returns the single VKRenderQueue instance.
|
||||
*/
|
||||
public static synchronized VKRenderQueue getInstance() {
|
||||
if (theInstance == null) {
|
||||
theInstance = new VKRenderQueue();
|
||||
}
|
||||
public static VKRenderQueue getInstance() {
|
||||
return theInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the single VKRenderQueue instance synchronously. If an
|
||||
* VKRenderQueue has not yet been instantiated, this method is a no-op.
|
||||
* This method is useful in the case of Toolkit.sync(), in which we want
|
||||
* to flush the Vulkan pipeline, but only if the Vulkan pipeline is currently
|
||||
* enabled.
|
||||
* Flushes the single VKRenderQueue instance synchronously.
|
||||
*/
|
||||
public static void sync() {
|
||||
if (theInstance != null) {
|
||||
theInstance.lock();
|
||||
try {
|
||||
theInstance.ensureCapacity(4);
|
||||
theInstance.getBuffer().putInt(SYNC);
|
||||
theInstance.flushNow();
|
||||
} finally {
|
||||
theInstance.unlock();
|
||||
}
|
||||
theInstance.lock();
|
||||
try {
|
||||
theInstance.ensureCapacity(4);
|
||||
theInstance.getBuffer().putInt(SYNC);
|
||||
theInstance.flushNow();
|
||||
} finally {
|
||||
theInstance.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushNow() {
|
||||
// assert lock.isHeldByCurrentThread();
|
||||
try {
|
||||
flusher.flushNow();
|
||||
} catch (Exception e) {
|
||||
System.err.println("exception in flushNow:");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void flushAndInvokeNow(Runnable r) {
|
||||
// assert lock.isHeldByCurrentThread();
|
||||
try {
|
||||
flusher.flushAndInvokeNow(r);
|
||||
} catch (Exception e) {
|
||||
System.err.println("exception in flushAndInvokeNow:");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private native void flushBuffer(long buf, int limit);
|
||||
|
||||
private void flushBuffer() {
|
||||
// assert lock.isHeldByCurrentThread();
|
||||
int limit = buf.position();
|
||||
if (limit > 0) {
|
||||
@@ -121,97 +72,10 @@ public class VKRenderQueue extends RenderQueue {
|
||||
refSet.clear();
|
||||
}
|
||||
|
||||
private class QueueFlusher implements Runnable {
|
||||
private boolean needsFlush;
|
||||
private Runnable task;
|
||||
private Error error;
|
||||
private final Thread thread;
|
||||
|
||||
public QueueFlusher() {
|
||||
thread = InnocuousThread.newThread("Java2D Queue Flusher", this);
|
||||
thread.setDaemon(true);
|
||||
thread.setPriority(Thread.MAX_PRIORITY);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
public synchronized void flushNow() {
|
||||
// wake up the flusher
|
||||
needsFlush = true;
|
||||
notify();
|
||||
|
||||
// wait for flush to complete
|
||||
while (needsFlush) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
// re-throw any error that may have occurred during the flush
|
||||
if (error != null) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void flushAndInvokeNow(Runnable task) {
|
||||
this.task = task;
|
||||
flushNow();
|
||||
}
|
||||
|
||||
public synchronized void run() {
|
||||
boolean timedOut = false;
|
||||
while (true) {
|
||||
while (!needsFlush) {
|
||||
try {
|
||||
timedOut = false;
|
||||
/*
|
||||
* Wait until we're woken up with a flushNow() call,
|
||||
* or the timeout period elapses (so that we can
|
||||
* flush the queue periodically).
|
||||
*/
|
||||
wait(100);
|
||||
/*
|
||||
* We will automatically flush the queue if the
|
||||
* following conditions apply:
|
||||
* - the wait() timed out
|
||||
* - we can lock the queue (without blocking)
|
||||
* - there is something in the queue to flush
|
||||
* Otherwise, just continue (we'll flush eventually).
|
||||
*/
|
||||
if (!needsFlush && (timedOut = tryLock())) {
|
||||
if (buf.position() > 0) {
|
||||
needsFlush = true;
|
||||
} else {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
try {
|
||||
// reset the throwable state
|
||||
error = null;
|
||||
// flush the buffer now
|
||||
flushBuffer();
|
||||
// if there's a task, invoke that now as well
|
||||
if (task != null) {
|
||||
task.run();
|
||||
}
|
||||
} catch (Error e) {
|
||||
error = e;
|
||||
} catch (Exception x) {
|
||||
System.err.println("exception in QueueFlusher:");
|
||||
x.printStackTrace();
|
||||
} finally {
|
||||
if (timedOut) {
|
||||
unlock();
|
||||
}
|
||||
task = null;
|
||||
// allow the waiting thread to continue
|
||||
needsFlush = false;
|
||||
notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
public void flushAndInvokeNow(Runnable r) {
|
||||
flushNow();
|
||||
r.run();
|
||||
}
|
||||
|
||||
private native void flushBuffer(long buf, int limit);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import sun.java2d.SurfaceData;
|
||||
import sun.java2d.loops.CompositeType;
|
||||
import sun.java2d.loops.GraphicsPrimitive;
|
||||
import sun.java2d.loops.SurfaceType;
|
||||
import static sun.java2d.pipe.BufferedOpCodes.CONFIGURE_SURFACE;
|
||||
import sun.java2d.pipe.ParallelogramPipe;
|
||||
import sun.java2d.pipe.PixelToParallelogramConverter;
|
||||
import sun.java2d.pipe.RenderBuffer;
|
||||
@@ -303,5 +304,22 @@ public abstract class VKSurfaceData extends SurfaceData
|
||||
return graphicsConfig;
|
||||
}
|
||||
|
||||
protected synchronized void configure() {
|
||||
VKRenderQueue rq = VKRenderQueue.getInstance();
|
||||
rq.lock();
|
||||
try {
|
||||
RenderBuffer buf = rq.getBuffer();
|
||||
rq.ensureCapacityAndAlignment(20, 4);
|
||||
buf.putInt(CONFIGURE_SURFACE);
|
||||
buf.putLong(getNativeOps());
|
||||
buf.putInt(width);
|
||||
buf.putInt(height);
|
||||
|
||||
rq.flushNow();
|
||||
} finally {
|
||||
rq.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract boolean isOnScreen();
|
||||
}
|
||||
|
||||
11
src/java.desktop/share/glsl/vulkan/clip.vert
Normal file
11
src/java.desktop/share/glsl/vulkan/clip.vert
Normal file
@@ -0,0 +1,11 @@
|
||||
#version 450
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
vec2 viewportNormalizer; // 2.0 / viewport
|
||||
} push;
|
||||
|
||||
layout(location = 0) in ivec2 in_Position;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(vec2(in_Position) * push.viewportNormalizer - vec2(1.0), 0.0, 1.0);
|
||||
}
|
||||
18
src/java.desktop/share/glsl/vulkan/mask_fill_color.frag
Normal file
18
src/java.desktop/share/glsl/vulkan/mask_fill_color.frag
Normal file
@@ -0,0 +1,18 @@
|
||||
#version 450
|
||||
|
||||
layout(set = 0, binding = 0, r8) uniform readonly restrict imageBuffer u_Mask;
|
||||
|
||||
layout(origin_upper_left) in vec4 gl_FragCoord;
|
||||
|
||||
layout(location = 0) in flat ivec4 in_OriginOffsetAndScanline;
|
||||
layout(location = 1) in flat vec4 in_Color;
|
||||
|
||||
layout(location = 0) out vec4 out_Color;
|
||||
|
||||
void main() {
|
||||
ivec2 maskPos = ivec2(gl_FragCoord.xy) - in_OriginOffsetAndScanline.xy;
|
||||
int offset = in_OriginOffsetAndScanline.z;
|
||||
int scanline = in_OriginOffsetAndScanline.w;
|
||||
int maskIndex = offset + scanline * maskPos.y + min(scanline, maskPos.x);
|
||||
out_Color = in_Color * imageLoad(u_Mask, maskIndex).r;
|
||||
}
|
||||
20
src/java.desktop/share/glsl/vulkan/mask_fill_color.vert
Normal file
20
src/java.desktop/share/glsl/vulkan/mask_fill_color.vert
Normal file
@@ -0,0 +1,20 @@
|
||||
#version 450
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
vec2 viewportNormalizer; // 2.0 / viewport
|
||||
} push;
|
||||
|
||||
layout(location = 0) in ivec4 in_PositionOffsetAndScanline;
|
||||
layout(location = 1) in vec4 in_Color;
|
||||
|
||||
// This starts with "Origin" and not "Position" intentionally.
|
||||
// When drawing, vertices are ordered in a such way, that provoking vertex is always the top-left one.
|
||||
// This gives us an easy way to calculate offset within the rectangle without additional inputs.
|
||||
layout(location = 0) out flat ivec4 out_OriginOffsetAndScanline;
|
||||
layout(location = 1) out flat vec4 out_Color;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(vec2(in_PositionOffsetAndScanline.xy) * push.viewportNormalizer - vec2(1.0), 0.0, 1.0);
|
||||
out_OriginOffsetAndScanline = in_PositionOffsetAndScanline;
|
||||
out_Color = in_Color;
|
||||
}
|
||||
@@ -824,8 +824,6 @@ ATexturePoolHandle* ATexturePool_getTexture(ATexturePool* pool,
|
||||
pool->memoryAllocated += requestedBytes;
|
||||
pool->totalMemoryAllocated += requestedBytes;
|
||||
|
||||
J2dTraceLn6(J2D_TRACE_VERBOSE, "ATexturePool_getTexture: created pool item: tex=%p, w=%d h=%d, pf=%d | allocated memory = %lld Kb (allocs: %d)",
|
||||
minDeltaTpi->texture, width, height, format, pool->memoryAllocated / UNIT_KB, pool->allocatedCount)
|
||||
if (TRACE_MEM_API) J2dRlsTraceLn6(J2D_TRACE_VERBOSE, "ATexturePool_getTexture: created pool item: tex=%p, w=%d h=%d, pf=%d | allocated memory = %lld Kb (allocs: %d)",
|
||||
minDeltaTpi->texture, width, height, format, pool->memoryAllocated / UNIT_KB, pool->allocatedCount)
|
||||
} else {
|
||||
|
||||
@@ -2,55 +2,381 @@
|
||||
#include <stddef.h>
|
||||
#include "CArrayUtil.h"
|
||||
|
||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||
#if defined(_MSC_VER)
|
||||
# include <malloc.h>
|
||||
# define ALIGNED_ALLOC(ALIGNMENT, SIZE) _aligned_malloc((SIZE), (ALIGNMENT))
|
||||
# define ALIGNED_FREE(PTR) _aligned_free(PTR)
|
||||
#else
|
||||
# include <stdlib.h>
|
||||
# define ALIGNED_ALLOC(ALIGNMENT, SIZE) aligned_alloc((ALIGNMENT), (SIZE))
|
||||
# define ALIGNED_FREE(PTR) free(PTR)
|
||||
#endif
|
||||
|
||||
void* CARR_array_alloc(size_t elem_size, size_t capacity) {
|
||||
CARR_array_t *pvec = malloc(elem_size * capacity + offsetof(CARR_array_t, data));
|
||||
if (pvec == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
pvec->size = 0;
|
||||
pvec->capacity = capacity;
|
||||
return pvec->data;
|
||||
// === Allocation helpers ===
|
||||
|
||||
typedef struct {
|
||||
size_t total_alignment;
|
||||
size_t aligned_header_size;
|
||||
void* new_data;
|
||||
} CARR_context_t;
|
||||
|
||||
static size_t CARR_align_size(size_t alignment, size_t size) {
|
||||
// assert alignment is power of 2
|
||||
size_t alignment_mask = alignment - 1;
|
||||
return (size + alignment_mask) & ~alignment_mask;
|
||||
}
|
||||
|
||||
void* CARR_array_realloc(CARR_array_t* vec, size_t elem_size, size_t new_capacity) {
|
||||
if (vec->capacity == new_capacity) {
|
||||
return vec->data;
|
||||
}
|
||||
CARR_array_t* new_vec =
|
||||
(CARR_array_t*)((char*)CARR_array_alloc(elem_size, new_capacity) - offsetof(CARR_array_t, data));
|
||||
if (new_vec == NULL) {
|
||||
return vec == NULL ? NULL : vec->data;
|
||||
}
|
||||
new_vec->capacity = new_capacity;
|
||||
new_vec->size = MIN(vec->size, new_capacity);
|
||||
memcpy(new_vec->data, vec->data, new_vec->size*elem_size);
|
||||
free(vec);
|
||||
return new_vec->data;
|
||||
static CARR_context_t CARR_context_init(size_t header_alignment, size_t header_size, size_t data_alignment) {
|
||||
CARR_context_t context;
|
||||
// assert header_alignment and data_alignment are powers of 2
|
||||
context.total_alignment = CARR_MAX(header_alignment, data_alignment);
|
||||
// assert header_size is multiple of header_alignment
|
||||
context.aligned_header_size = CARR_align_size(context.total_alignment, header_size);
|
||||
context.new_data = NULL;
|
||||
return context;
|
||||
}
|
||||
|
||||
void* CARR_ring_buffer_realloc(CARR_ring_buffer_t* buf, size_t elem_size, size_t new_capacity) {
|
||||
if (buf != NULL && buf->capacity == new_capacity) {
|
||||
return buf->data;
|
||||
static bool CARR_context_alloc(CARR_context_t* context, size_t data_size) {
|
||||
void* block = ALIGNED_ALLOC(context->total_alignment, context->aligned_header_size + data_size);
|
||||
if (block == NULL) return false;
|
||||
context->new_data = (char*)block + context->aligned_header_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void CARR_context_free(CARR_context_t* context, void* old_data) {
|
||||
if (old_data != NULL) {
|
||||
void* block = (char*)old_data - context->aligned_header_size;
|
||||
ALIGNED_FREE(block);
|
||||
}
|
||||
CARR_ring_buffer_t* new_buf =
|
||||
(CARR_ring_buffer_t*) malloc(elem_size * new_capacity + offsetof(CARR_ring_buffer_t, data));
|
||||
if (new_buf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
new_buf->head = new_buf->tail = 0;
|
||||
new_buf->capacity = new_capacity;
|
||||
if (buf != NULL) {
|
||||
if (buf->tail > buf->head) {
|
||||
new_buf->tail = buf->tail - buf->head;
|
||||
memcpy(new_buf->data, buf->data + buf->head*elem_size, new_buf->tail*elem_size);
|
||||
} else if (buf->tail < buf->head) {
|
||||
new_buf->tail = buf->capacity + buf->tail - buf->head;
|
||||
memcpy(new_buf->data, buf->data + buf->head*elem_size, (buf->capacity-buf->head)*elem_size);
|
||||
memcpy(new_buf->data + (new_buf->tail-buf->tail)*elem_size, buf->data, buf->tail*elem_size);
|
||||
}
|
||||
|
||||
// === Arrays ===
|
||||
|
||||
bool CARR_array_realloc(void** handle, size_t element_alignment, size_t element_size, size_t new_capacity) {
|
||||
void* old_data = *handle;
|
||||
if (old_data != NULL && CARR_ARRAY_T(old_data)->capacity == new_capacity) return true;
|
||||
CARR_context_t context = CARR_context_init(alignof(CARR_array_t), sizeof(CARR_array_t), element_alignment);
|
||||
if (new_capacity != 0) {
|
||||
if (!CARR_context_alloc(&context, element_size * new_capacity)) return false;
|
||||
CARR_ARRAY_T(context.new_data)->capacity = new_capacity;
|
||||
if (old_data == NULL) {
|
||||
CARR_ARRAY_T(context.new_data)->size = 0;
|
||||
} else {
|
||||
CARR_ARRAY_T(context.new_data)->size = CARR_MIN(CARR_ARRAY_T(old_data)->size, new_capacity);
|
||||
memcpy(context.new_data, old_data, element_size * CARR_ARRAY_T(context.new_data)->size);
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
return new_buf->data;
|
||||
CARR_context_free(&context, old_data);
|
||||
*handle = context.new_data;
|
||||
return true;
|
||||
}
|
||||
|
||||
// === Ring buffers ===
|
||||
|
||||
bool CARR_ring_buffer_realloc(void** handle, size_t element_alignment, size_t element_size, size_t new_capacity) {
|
||||
void* old_data = *handle;
|
||||
if (old_data != NULL) {
|
||||
CARR_ring_buffer_t* old_buf = CARR_RING_BUFFER_T(old_data);
|
||||
if (old_buf->capacity == new_capacity) return true;
|
||||
// Shrinking is not supported.
|
||||
if ((old_buf->capacity + old_buf->tail - old_buf->head) % old_buf->capacity > new_capacity) return false;
|
||||
}
|
||||
CARR_context_t context =
|
||||
CARR_context_init(alignof(CARR_ring_buffer_t), sizeof(CARR_ring_buffer_t), element_alignment);
|
||||
if (new_capacity != 0) {
|
||||
if (!CARR_context_alloc(&context, element_size * new_capacity)) return false;
|
||||
CARR_ring_buffer_t* new_buf = CARR_RING_BUFFER_T(context.new_data);
|
||||
new_buf->capacity = new_capacity;
|
||||
new_buf->head = new_buf->tail = 0;
|
||||
if (old_data != NULL) {
|
||||
CARR_ring_buffer_t* old_buf = CARR_RING_BUFFER_T(old_data);
|
||||
if (old_buf->tail > old_buf->head) {
|
||||
new_buf->tail = old_buf->tail - old_buf->head;
|
||||
memcpy(context.new_data, (char*)old_data + old_buf->head*element_size, new_buf->tail*element_size);
|
||||
} else if (old_buf->tail < old_buf->head) {
|
||||
new_buf->tail = old_buf->capacity + old_buf->tail - old_buf->head;
|
||||
memcpy(context.new_data, (char*)old_data + old_buf->head*element_size,
|
||||
(old_buf->capacity-old_buf->head)*element_size);
|
||||
memcpy((char*)context.new_data + (new_buf->tail-old_buf->tail)*element_size, old_data,
|
||||
old_buf->tail*element_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
CARR_context_free(&context, old_data);
|
||||
*handle = context.new_data;
|
||||
return true;
|
||||
}
|
||||
|
||||
// === Maps ===
|
||||
|
||||
static const size_t CARR_hash_map_primes[] = { 11U, 23U, 47U, 97U, 193U, 389U, 769U, 1543U, 3079U, 6151U,
|
||||
12289U, 24593U, 49157U, 98317U, 196613U, 393241U, 786433U,
|
||||
1572869U, 3145739U, 6291469U, 12582917U, 25165843U, 50331653U,
|
||||
100663319U, 201326611U, 402653189U, 805306457U, 1610612741U };
|
||||
static size_t CARR_hash_map_find_size(const size_t* table, unsigned int table_length, size_t min) {
|
||||
for (unsigned int i = 0; i < table_length; ++i) if (table[i] >= min) return table[i];
|
||||
return 0; // Do not return min, as this may break addressing variants which rely on specific numeric properties.
|
||||
}
|
||||
#define HASH_MAP_FIND_SIZE(TABLE, SIZE) CARR_hash_map_find_size(TABLE, SARRAY_COUNT_OF(TABLE), SIZE)
|
||||
|
||||
// Check whether memory chunk is non-zero.
|
||||
static bool CARR_check_range(const void* p, size_t alignment, size_t size) {
|
||||
switch (alignment) {
|
||||
case sizeof(uint8_t):
|
||||
case sizeof(uint16_t):{
|
||||
const uint8_t* data = p;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
if (data[i] != (uint8_t) 0) return true;
|
||||
}
|
||||
}break;
|
||||
case sizeof(uint32_t):{
|
||||
size >>= 2;
|
||||
const uint32_t* data = p;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
if (data[i] != (uint32_t) 0) return true;
|
||||
}
|
||||
}break;
|
||||
default:{
|
||||
size >>= 3;
|
||||
const uint64_t* data = p;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
if (data[i] != (uint64_t) 0) return true;
|
||||
}
|
||||
}break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool CARR_map_insert_all(CARR_MAP_LAYOUT_ARGS, void* src, void* dst) {
|
||||
if (src == NULL) return true;
|
||||
const CARR_map_dispatch_t* src_dispatch = ((const CARR_map_dispatch_t**)src)[-1];
|
||||
const CARR_map_dispatch_t* dst_dispatch = ((const CARR_map_dispatch_t**)dst)[-1];
|
||||
for (const void* key = NULL; (key = src_dispatch->next_key(CARR_MAP_LAYOUT_PASS, src, key)) != NULL;) {
|
||||
const void* value = src_dispatch->find(CARR_MAP_LAYOUT_PASS, src, key, NULL, false);
|
||||
void* new_value = dst_dispatch->find(CARR_MAP_LAYOUT_PASS, dst, key, NULL, true);
|
||||
if (new_value == NULL) return false; // Cannot insert.
|
||||
memcpy(new_value, value, value_size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// === Open addressing (probing) hash maps ===
|
||||
// Probing hash maps keep keys and values separately in two continuous aligned memory blocks.
|
||||
// This class is the most memory-efficient with no overhead other than fixed size header.
|
||||
// It provides O(1) lookup even when full, except for the cases of deletion and missing
|
||||
// key, which degrade down to O(N). This makes it a good choice for caches, which
|
||||
// only do "find or insert" and never delete elements.
|
||||
static const uint32_t CARR_hash_map_probing_rehash_bit = 0x80000000;
|
||||
static const uint32_t CARR_hash_map_probing_limit_mask = 0x7fffffff;
|
||||
typedef struct {
|
||||
size_t capacity;
|
||||
size_t size;
|
||||
uint32_t probing_limit;
|
||||
float load_factor;
|
||||
void* null_key_slot;
|
||||
CARR_equals_fp equals;
|
||||
CARR_hash_fp hash;
|
||||
void* dispatch_placeholder;
|
||||
} CARR_hash_map_probing_t;
|
||||
|
||||
static inline void* CARR_hash_map_probing_value_for(CARR_MAP_LAYOUT_ARGS, const void* data, const void* key_slot) {
|
||||
if (key_slot == NULL) return NULL;
|
||||
CARR_hash_map_probing_t* map = (CARR_hash_map_probing_t*) data - 1;
|
||||
size_t value_block_offset = CARR_align_size(value_alignment, key_size * map->capacity);
|
||||
return (char*)data + value_block_offset + ((const char*)key_slot - (char*)data) / key_size * value_size;
|
||||
}
|
||||
|
||||
static size_t CARR_hash_map_probing_check_extra_capacity(CARR_hash_map_probing_t* map, size_t count) {
|
||||
// Run length is a local metric, which directly correlate with lookup performance,
|
||||
// but can suffer from clustering, bad hash function, or bad luck.
|
||||
// Load factor is a global metric, which reflects "fullness",
|
||||
// but doesn't capture local effects, like clustering,
|
||||
// and is over-conservative for good distributions.
|
||||
// Therefore, we only rehash when both load factor and probing limit are exceeded.
|
||||
size_t new_capacity = map->size + count;
|
||||
if (new_capacity <= map->capacity) {
|
||||
if (!(map->probing_limit & CARR_hash_map_probing_rehash_bit)) { // Rehashing not requested.
|
||||
new_capacity = 0;
|
||||
} else if (map->size < (size_t)(map->load_factor * (float)map->capacity)) {
|
||||
map->probing_limit &= CARR_hash_map_probing_limit_mask; // Load factor too low, reset rehash flag.
|
||||
new_capacity = 0;
|
||||
} else new_capacity = map->capacity + 1;
|
||||
}
|
||||
return new_capacity;
|
||||
}
|
||||
|
||||
static const void* CARR_hash_map_probing_next_key(CARR_MAP_LAYOUT_ARGS, const void* data, const void* key_slot) {
|
||||
CARR_hash_map_probing_t* map = (CARR_hash_map_probing_t*) data - 1;
|
||||
char* slot;
|
||||
if (key_slot == NULL) slot = (char*)data;
|
||||
else if (key_slot < data) return NULL;
|
||||
else slot = (char*)key_slot + key_size;
|
||||
char* limit = (char*)data + key_size * (map->capacity - 1);
|
||||
for (; slot <= limit; slot += key_size) {
|
||||
if (CARR_check_range(slot, key_alignment, key_size) || slot == map->null_key_slot) return slot;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void CARR_hash_map_probing_clear(CARR_MAP_LAYOUT_ARGS, void* data) {
|
||||
CARR_hash_map_probing_t* map = (CARR_hash_map_probing_t*) data - 1;
|
||||
memset(data, 0, key_size * map->capacity);
|
||||
map->probing_limit &= CARR_hash_map_probing_limit_mask;
|
||||
map->null_key_slot = NULL;
|
||||
map->size = 0;
|
||||
}
|
||||
|
||||
static void CARR_hash_map_probing_free(CARR_MAP_LAYOUT_ARGS, void* data) {
|
||||
if (data == NULL) return;
|
||||
CARR_context_t context = CARR_context_init(alignof(CARR_hash_map_probing_t), sizeof(CARR_hash_map_probing_t),
|
||||
CARR_MAX(key_alignment, value_alignment));
|
||||
CARR_context_free(&context, data);
|
||||
}
|
||||
|
||||
// === Linear probing hash map ===
|
||||
static inline void CARR_hash_map_linear_probing_check_run(CARR_MAP_LAYOUT_ARGS, CARR_hash_map_probing_t* map,
|
||||
const char* from, const char* to) {
|
||||
if (map->probing_limit & CARR_hash_map_probing_rehash_bit) return; // Rehashing already requested.
|
||||
if (map->size < (size_t)(map->load_factor * (float)map->capacity)) return; // Load factor too low.
|
||||
ptrdiff_t offset = to - from;
|
||||
if (to < from) offset += (ptrdiff_t)(map->capacity * key_size);
|
||||
size_t run = (size_t)offset / key_size;
|
||||
// Set rehash bit if our probing length exceeded the limit.
|
||||
if (run > (size_t)map->probing_limit) map->probing_limit |= CARR_hash_map_probing_rehash_bit;
|
||||
}
|
||||
|
||||
static void* CARR_hash_map_linear_probing_find(CARR_MAP_LAYOUT_ARGS,
|
||||
void* data, const void* key, const void** resolved_key, bool insert) {
|
||||
CARR_hash_map_probing_t* map = (CARR_hash_map_probing_t*) data - 1;
|
||||
char* wrap = (char*)data + key_size * map->capacity;
|
||||
if (key >= data && key < (void*) wrap && ((const char*)key - (char*)data) % key_size == 0) {
|
||||
// Try fast access for resolved key.
|
||||
if (key == map->null_key_slot || CARR_check_range(key, key_alignment, key_size)) {
|
||||
if (resolved_key != NULL) *resolved_key = key;
|
||||
return CARR_hash_map_probing_value_for(CARR_MAP_LAYOUT_PASS, data, key);
|
||||
}
|
||||
}
|
||||
size_t hash = map->hash(key);
|
||||
char* start = (char*)data + key_size * (hash % map->capacity);
|
||||
char* slot = start;
|
||||
for (;;) {
|
||||
bool is_null = !CARR_check_range(slot, key_alignment, key_size);
|
||||
if (map->equals(key, slot)) {
|
||||
// Special case to distinguish null key from missing one.
|
||||
if (is_null) {
|
||||
if (map->null_key_slot == NULL && insert) {
|
||||
map->null_key_slot = slot;
|
||||
break; // Insert.
|
||||
}
|
||||
slot = map->null_key_slot;
|
||||
}
|
||||
if (resolved_key != NULL) *resolved_key = slot;
|
||||
return CARR_hash_map_probing_value_for(CARR_MAP_LAYOUT_PASS, data, slot);
|
||||
}
|
||||
if (is_null && slot != map->null_key_slot) { // Key not found.
|
||||
if (insert) break; // Insert.
|
||||
return resolved_key != NULL ? (void*)(*resolved_key = NULL) : NULL;
|
||||
}
|
||||
slot += key_size;
|
||||
if (slot == wrap) slot = (char*)data;
|
||||
if (slot == start) {
|
||||
return resolved_key != NULL ? (void*)(*resolved_key = NULL) : NULL; // We traversed the whole map.
|
||||
}
|
||||
}
|
||||
// Insert.
|
||||
void* value = CARR_hash_map_probing_value_for(CARR_MAP_LAYOUT_PASS, data, slot);
|
||||
memcpy(slot, key, key_size); // Copy key into slot.
|
||||
memset(value, 0, value_size); // Clear value.
|
||||
map->size++;
|
||||
if (resolved_key != NULL) {
|
||||
*resolved_key = slot;
|
||||
value = NULL; // Indicate that value was just inserted.
|
||||
}
|
||||
CARR_hash_map_linear_probing_check_run(CARR_MAP_LAYOUT_PASS, map, start, slot);
|
||||
return value;
|
||||
}
|
||||
|
||||
static bool CARR_hash_map_linear_probing_remove(CARR_MAP_LAYOUT_ARGS, void* data, const void* key) {
|
||||
char* key_slot;
|
||||
CARR_hash_map_linear_probing_find(CARR_MAP_LAYOUT_PASS, data, key, (const void**) &key_slot, false);
|
||||
if (key_slot == NULL) return false;
|
||||
char* start = key_slot;
|
||||
CARR_hash_map_probing_t* map = (CARR_hash_map_probing_t*) data - 1;
|
||||
char* wrap = (char*)data + key_size * map->capacity;
|
||||
for (;;) {
|
||||
if (map->null_key_slot == key_slot) map->null_key_slot = NULL;
|
||||
char* slot = key_slot;
|
||||
for (;;) {
|
||||
slot += key_size;
|
||||
if (slot == wrap) slot = (char*)data;
|
||||
if (slot == start || (!CARR_check_range(slot, key_alignment, key_size) && slot != map->null_key_slot)) {
|
||||
memset(key_slot, 0, key_size); // Clear key slot.
|
||||
CARR_hash_map_linear_probing_check_run(CARR_MAP_LAYOUT_PASS, map, start, slot);
|
||||
return true;
|
||||
}
|
||||
size_t hash = map->hash(slot);
|
||||
char* expected_slot = (char*)data + key_size * (hash % map->capacity);
|
||||
if (slot >= expected_slot) {
|
||||
if (key_slot >= expected_slot && key_slot <= slot) break;
|
||||
} else {
|
||||
if (key_slot >= expected_slot || key_slot <= slot) break;
|
||||
}
|
||||
}
|
||||
// Move another entry into the gap.
|
||||
if (map->null_key_slot == slot) map->null_key_slot = key_slot;
|
||||
memcpy(key_slot, slot, key_size);
|
||||
memcpy(CARR_hash_map_probing_value_for(CARR_MAP_LAYOUT_PASS, data, key_slot),
|
||||
CARR_hash_map_probing_value_for(CARR_MAP_LAYOUT_PASS, data, slot), value_size);
|
||||
key_slot = slot; // Repeat with the new entry.
|
||||
}
|
||||
}
|
||||
|
||||
static bool CARR_hash_map_linear_probing_ensure_extra_capacity(CARR_MAP_LAYOUT_ARGS, void** handle, size_t count) {
|
||||
void* data = *handle;
|
||||
CARR_hash_map_probing_t* map = (CARR_hash_map_probing_t*) data - 1;
|
||||
size_t new_capacity = CARR_hash_map_probing_check_extra_capacity(map, count);
|
||||
if (new_capacity == 0) return true;
|
||||
return CARR_hash_map_linear_probing_rehash(CARR_MAP_LAYOUT_PASS, handle, map->equals, map->hash, new_capacity,
|
||||
map->probing_limit & CARR_hash_map_probing_limit_mask, map->load_factor);
|
||||
}
|
||||
|
||||
bool CARR_hash_map_linear_probing_rehash(CARR_MAP_LAYOUT_ARGS, void** handle, CARR_equals_fp equals, CARR_hash_fp hash,
|
||||
size_t new_capacity, uint32_t probing_limit, float load_factor) {
|
||||
size_t table_capacity = HASH_MAP_FIND_SIZE(CARR_hash_map_primes, new_capacity);
|
||||
if (table_capacity != 0) new_capacity = table_capacity;
|
||||
|
||||
CARR_context_t context = CARR_context_init(alignof(CARR_hash_map_probing_t), sizeof(CARR_hash_map_probing_t),
|
||||
CARR_MAX(key_alignment, value_alignment));
|
||||
size_t value_block_offset = CARR_align_size(value_alignment, key_size * new_capacity);
|
||||
if (!CARR_context_alloc(&context, value_block_offset + value_size * new_capacity)) return false;
|
||||
|
||||
CARR_hash_map_probing_t* map = (CARR_hash_map_probing_t*) context.new_data - 1;
|
||||
*map = (CARR_hash_map_probing_t) {
|
||||
.capacity = new_capacity,
|
||||
.size = 0,
|
||||
.probing_limit = CARR_MIN(probing_limit, CARR_hash_map_probing_limit_mask),
|
||||
.load_factor = load_factor,
|
||||
.null_key_slot = NULL,
|
||||
.equals = equals,
|
||||
.hash = hash
|
||||
};
|
||||
static const CARR_map_dispatch_t dispatch = {
|
||||
&CARR_hash_map_probing_next_key,
|
||||
&CARR_hash_map_linear_probing_find,
|
||||
&CARR_hash_map_linear_probing_remove,
|
||||
&CARR_hash_map_linear_probing_ensure_extra_capacity,
|
||||
&CARR_hash_map_probing_clear,
|
||||
&CARR_hash_map_probing_free,
|
||||
};
|
||||
((const CARR_map_dispatch_t**)context.new_data)[-1] = &dispatch;
|
||||
|
||||
CARR_hash_map_probing_clear(CARR_MAP_LAYOUT_PASS, context.new_data);
|
||||
if (!CARR_map_insert_all(CARR_MAP_LAYOUT_PASS, *handle, context.new_data)) {
|
||||
CARR_context_free(&context, context.new_data);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*handle != NULL) ((const CARR_map_dispatch_t**)*handle)[-1]->free(CARR_MAP_LAYOUT_PASS, *handle);
|
||||
*handle = context.new_data;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2,72 +2,124 @@
|
||||
#define C_ARRAY_UTIL_H
|
||||
|
||||
#include <malloc.h>
|
||||
#include <stdalign.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// C_ARRAY_UTIL_ALLOCATION_FAILED is called when allocation fails.
|
||||
// Default implementation calls abort().
|
||||
// Functions that can call C_ARRAY_UTIL_ALLOCATION_FAILED explicitly state
|
||||
// this in the documentation. Functions with *_TRY_* return NULL on failure.
|
||||
// this in the documentation. Functions with *_TRY_* keep the data structure unchanged.
|
||||
#ifndef C_ARRAY_UTIL_ALLOCATION_FAILED
|
||||
#include <stdlib.h>
|
||||
#define C_ARRAY_UTIL_ALLOCATION_FAILED() abort()
|
||||
#endif
|
||||
|
||||
// === Allocation helpers ===
|
||||
|
||||
#define CARR_MIN(a,b) (((a)<(b))?(a):(b))
|
||||
#define CARR_MAX(a,b) (((a)>(b))?(a):(b))
|
||||
|
||||
static inline bool CARR_handle_alloc(bool CARR_result, bool CARR_force) {
|
||||
if (CARR_result || !CARR_force) return CARR_result;
|
||||
C_ARRAY_UTIL_ALLOCATION_FAILED();
|
||||
return false;
|
||||
}
|
||||
static inline void consume(const void* value) {}
|
||||
|
||||
// === Arrays ===
|
||||
|
||||
#ifndef ARRAY_CAPACITY_GROW
|
||||
#define ARRAY_CAPACITY_GROW(C) (((C) * 3 + 1) / 2) // 1.5 multiplier
|
||||
#endif
|
||||
#ifndef ARRAY_DEFAULT_CAPACITY
|
||||
#define ARRAY_DEFAULT_CAPACITY 10
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
char data[];
|
||||
} CARR_array_t;
|
||||
|
||||
void* CARR_array_alloc(size_t elem_size, size_t capacity);
|
||||
void* CARR_array_realloc(CARR_array_t* vec, size_t elem_size, size_t new_capacity);
|
||||
bool CARR_array_realloc(void** handle, size_t element_alignment, size_t element_size, size_t new_capacity);
|
||||
|
||||
typedef struct {
|
||||
size_t head;
|
||||
size_t tail;
|
||||
size_t capacity;
|
||||
char data[];
|
||||
} CARR_ring_buffer_t;
|
||||
#define CARR_ARRAY_T(P) ((CARR_array_t*)(P) - 1) // NULL unsafe!
|
||||
|
||||
void* CARR_ring_buffer_realloc(CARR_ring_buffer_t* buf, size_t elem_size, size_t new_capacity);
|
||||
static inline void* CARR_array_alloc(size_t element_alignment, size_t element_size, size_t new_capacity) {
|
||||
void* data = NULL;
|
||||
CARR_array_realloc(&data, element_alignment, element_size, new_capacity);
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline bool CARR_array_ensure_capacity(void** handle, size_t alignment, size_t size,
|
||||
size_t new_capacity, bool force) {
|
||||
void* data = *handle;
|
||||
if (new_capacity > (data == NULL ? 0 : CARR_ARRAY_T(data)->capacity)) {
|
||||
return CARR_handle_alloc(CARR_array_realloc(handle, alignment, size, new_capacity), force);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool CARR_array_resize(void** handle, size_t alignment, size_t size, size_t new_size, bool force) {
|
||||
if (CARR_array_ensure_capacity(handle, alignment, size, new_size, force)) {
|
||||
void* data = *handle;
|
||||
if (data != NULL) CARR_ARRAY_T(data)->size = new_size;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void CARR_array_push_back(void** handle, size_t alignment, size_t size) {
|
||||
void* data = *handle;
|
||||
if (data == NULL || CARR_ARRAY_T(data)->size >= CARR_ARRAY_T(data)->capacity) {
|
||||
size_t new_capacity = data == NULL ? ARRAY_DEFAULT_CAPACITY : ARRAY_CAPACITY_GROW(CARR_ARRAY_T(data)->size);
|
||||
if (!CARR_handle_alloc(CARR_array_realloc(handle, alignment, size, new_capacity), true)) return;
|
||||
data = *handle; // assert data != NULL
|
||||
}
|
||||
CARR_ARRAY_T(data)->size++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamic array declaration, e.g. ARRAY(int) my_array = NULL;
|
||||
* @param TYPE type of the array element.
|
||||
*/
|
||||
#define ARRAY(TYPE) TYPE*
|
||||
|
||||
/**
|
||||
* Allocate array. Returns NULL on allocation failure.
|
||||
* @param T type of elements
|
||||
* @param CAPACITY capacity of the array
|
||||
* @return pointer to the allocated array, or NULL
|
||||
*/
|
||||
#define ARRAY_ALLOC(T, CAPACITY) (T*)CARR_array_alloc(sizeof(T), CAPACITY)
|
||||
|
||||
#define ARRAY_T(P) ((CARR_array_t *)((P) == NULL ? NULL : (char*)(P) - offsetof(CARR_array_t, data)))
|
||||
#define ARRAY_ALLOC(T, CAPACITY) ((T*)CARR_array_alloc(alignof(T), sizeof(T), CAPACITY))
|
||||
|
||||
/**
|
||||
* @param P pointer to the first data element of the array
|
||||
* @param P array
|
||||
* @return size of the array
|
||||
*/
|
||||
#define ARRAY_SIZE(P) ((P) == NULL ? (size_t) 0 : (ARRAY_T(P))->size)
|
||||
#define ARRAY_SIZE(P) ((P) == NULL ? (size_t) 0 : (CARR_ARRAY_T(P))->size)
|
||||
|
||||
/**
|
||||
* @param P pointer to the first data element of the array
|
||||
* @param P array
|
||||
* @return capacity of the array
|
||||
*/
|
||||
#define ARRAY_CAPACITY(P) ((P) == NULL ? (size_t) 0 : (ARRAY_T(P))->capacity)
|
||||
#define ARRAY_CAPACITY(P) ((P) == NULL ? (size_t) 0 : (CARR_ARRAY_T(P))->capacity)
|
||||
|
||||
/**
|
||||
* @param P pointer to the first data element of the array
|
||||
* @return last element in the array
|
||||
* @param P array
|
||||
* @return dereferenced pointer to the last element in the array
|
||||
*/
|
||||
#define ARRAY_LAST(P) ((P)[ARRAY_SIZE(P) - 1])
|
||||
|
||||
/**
|
||||
* Deallocate the vector
|
||||
* @param P pointer to the first data element of the array
|
||||
* @param P array
|
||||
*/
|
||||
#define ARRAY_FREE(P) free(ARRAY_T(P))
|
||||
#define ARRAY_FREE(P) ((void)CARR_array_realloc((void**)&(P), alignof(*(P)), sizeof(*(P)), 0))
|
||||
|
||||
/**
|
||||
* Apply function to the array elements
|
||||
* @param P pointer to the first data element of the array
|
||||
* @param P array
|
||||
* @param F function to apply
|
||||
*/
|
||||
#define ARRAY_APPLY(P, F) do { \
|
||||
@@ -76,7 +128,7 @@ void* CARR_ring_buffer_realloc(CARR_ring_buffer_t* buf, size_t elem_size, size_t
|
||||
|
||||
/**
|
||||
* Apply function to the array elements, passing pointer to an element as first parameter
|
||||
* @param P pointer to the first data element of the array
|
||||
* @param P array
|
||||
* @param F function to apply
|
||||
*/
|
||||
#define ARRAY_APPLY_LEADING(P, F, ...) do { \
|
||||
@@ -85,7 +137,7 @@ void* CARR_ring_buffer_realloc(CARR_ring_buffer_t* buf, size_t elem_size, size_t
|
||||
|
||||
/**
|
||||
* Apply function to the array elements, passing pointer to an element as last parameter
|
||||
* @param P pointer to the first data element of the array
|
||||
* @param P array
|
||||
* @param F function to apply
|
||||
*/
|
||||
#define ARRAY_APPLY_TRAILING(P, F, ...) do { \
|
||||
@@ -93,157 +145,367 @@ void* CARR_ring_buffer_realloc(CARR_ring_buffer_t* buf, size_t elem_size, size_t
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* Ensure array capacity. Implicitly initializes when array is NULL.
|
||||
* On allocation failure, array is unchanged.
|
||||
* @param P pointer to the first data element of the array
|
||||
* Ensure array capacity. Array is implicitly initialized when necessary.
|
||||
* On allocation failure, array is left unchanged.
|
||||
* @param P array
|
||||
* @param CAPACITY required capacity of the array
|
||||
* @return true if the operation succeeded
|
||||
*/
|
||||
#define ARRAY_TRY_ENSURE_CAPACITY(P, CAPACITY) do { \
|
||||
if ((P) == NULL) { \
|
||||
if ((CAPACITY) > 0) (P) = CARR_array_alloc(sizeof((P)[0]), CAPACITY); \
|
||||
} else if (ARRAY_CAPACITY(P) < (CAPACITY)) { \
|
||||
(P) = CARR_array_realloc(ARRAY_T(P), sizeof((P)[0]), CAPACITY); \
|
||||
} \
|
||||
} while(0)
|
||||
#define ARRAY_TRY_ENSURE_CAPACITY(P, CAPACITY) \
|
||||
CARR_array_ensure_capacity((void**)&(P), alignof(*(P)), sizeof(*(P)), (CAPACITY), false)
|
||||
|
||||
/**
|
||||
* Ensure array capacity. Implicitly initializes when array is NULL.
|
||||
* Ensure array capacity. Array is implicitly initialized when necessary.
|
||||
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
|
||||
* @param P pointer to the first data element of the array
|
||||
* @param P array
|
||||
* @param CAPACITY required capacity of the array
|
||||
*/
|
||||
#define ARRAY_ENSURE_CAPACITY(P, CAPACITY) do { \
|
||||
ARRAY_TRY_ENSURE_CAPACITY(P, CAPACITY); \
|
||||
if (ARRAY_CAPACITY(P) < (CAPACITY)) C_ARRAY_UTIL_ALLOCATION_FAILED(); \
|
||||
} while(0)
|
||||
#define ARRAY_ENSURE_CAPACITY(P, CAPACITY) \
|
||||
((void)CARR_array_ensure_capacity((void**)&(P), alignof(*(P)), sizeof(*(P)), (CAPACITY), true))
|
||||
|
||||
/**
|
||||
* Shrink capacity of the array to its size.
|
||||
* On allocation failure, array is unchanged.
|
||||
* @param P pointer to the first data element of the array
|
||||
* On allocation failure, array is left unchanged.
|
||||
* @param P array
|
||||
* @return the array
|
||||
* @return true if the operation succeeded
|
||||
*/
|
||||
#define ARRAY_SHRINK_TO_FIT(P) do { \
|
||||
if ((P) != NULL) { \
|
||||
(P) = CARR_array_realloc(ARRAY_T(P), sizeof((P)[0]), ARRAY_SIZE(P)); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define ARRAY_RESIZE_IMPL(P, SIZE, ...) do { \
|
||||
if ((P) != NULL || (SIZE) > 0) { \
|
||||
ARRAY_ENSURE_CAPACITY(P, SIZE); \
|
||||
if ((P) != NULL && (ARRAY_T(P))->capacity >= (SIZE)) { \
|
||||
(ARRAY_T(P))->size = (SIZE); \
|
||||
} __VA_ARGS__ \
|
||||
} \
|
||||
} while(0)
|
||||
#define ARRAY_SHRINK_TO_FIT(P) CARR_array_realloc((void**)&(P), alignof(*(P)), sizeof(*(P)), ARRAY_SIZE(P))
|
||||
|
||||
/**
|
||||
* Resize an array. Implicitly initializes when array is NULL.
|
||||
* On allocation failure, array is unchanged.
|
||||
* @param P pointer to the first data element of the array
|
||||
* Resize an array. Array is implicitly initialized when necessary.
|
||||
* On allocation failure, array is left unchanged.
|
||||
* @param P array
|
||||
* @param SIZE required size of the array
|
||||
* @return true if the operation succeeded
|
||||
*/
|
||||
#define ARRAY_TRY_RESIZE(P, SIZE) \
|
||||
CARR_array_resize((void**)&(P), alignof(*(P)), sizeof(*(P)), (SIZE), false)
|
||||
|
||||
/**
|
||||
* Resize an array. Array is implicitly initialized when necessary.
|
||||
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
|
||||
* @param P array
|
||||
* @param SIZE required size of the array
|
||||
*/
|
||||
#define ARRAY_TRY_RESIZE(P, SIZE) ARRAY_RESIZE_IMPL(P, SIZE, )
|
||||
#define ARRAY_RESIZE(P, SIZE) \
|
||||
((void)CARR_array_resize((void**)&(P), alignof(*(P)), sizeof(*(P)), (SIZE), true))
|
||||
|
||||
/**
|
||||
* Resize an array. Implicitly initializes when array is NULL.
|
||||
* Add element to the end of the array. Array is implicitly initialized when necessary.
|
||||
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
|
||||
* @param P pointer to the first data element of the array
|
||||
* @param SIZE required size of the array
|
||||
* @param P array
|
||||
* @return dereferenced pointer to the inserted element
|
||||
*/
|
||||
#define ARRAY_RESIZE(P, SIZE) ARRAY_RESIZE_IMPL(P, SIZE, else if ((SIZE) > 0) C_ARRAY_UTIL_ALLOCATION_FAILED();)
|
||||
#define ARRAY_PUSH_BACK(P) \
|
||||
(*(CARR_array_push_back((void**)&(P), alignof(*(P)), sizeof(*(P))), (P) + ARRAY_SIZE(P) - 1))
|
||||
|
||||
/**
|
||||
* Add element to the end of the array. Implicitly initializes when array is NULL.
|
||||
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
|
||||
* @param P pointer to the first data element of the array
|
||||
* Compile-time length of the static array.
|
||||
*/
|
||||
#define ARRAY_PUSH_BACK(P, ...) do { \
|
||||
if ((P) == NULL) { \
|
||||
(P) = CARR_array_alloc(sizeof((P)[0]), ARRAY_DEFAULT_CAPACITY); \
|
||||
} else if (ARRAY_SIZE(P) >= ARRAY_CAPACITY(P)) { \
|
||||
(P) = CARR_array_realloc(ARRAY_T(P), sizeof((P)[0]), ARRAY_CAPACITY_GROW(ARRAY_SIZE(P))); \
|
||||
} \
|
||||
if (ARRAY_SIZE(P) >= ARRAY_CAPACITY(P)) C_ARRAY_UTIL_ALLOCATION_FAILED(); \
|
||||
*((P) + ARRAY_SIZE(P)) = (__VA_ARGS__); \
|
||||
(ARRAY_T(P))->size++; \
|
||||
} while(0)
|
||||
|
||||
#define SARRAY_COUNT_OF(STATIC_ARRAY) (sizeof(STATIC_ARRAY)/sizeof((STATIC_ARRAY)[0]))
|
||||
|
||||
#define RING_BUFFER_T(P) ((CARR_ring_buffer_t *)((P) == NULL ? NULL : (char*)(P) - offsetof(CARR_ring_buffer_t, data)))
|
||||
// === Ring buffers ===
|
||||
|
||||
typedef struct {
|
||||
size_t head;
|
||||
size_t tail;
|
||||
size_t capacity;
|
||||
} CARR_ring_buffer_t;
|
||||
|
||||
bool CARR_ring_buffer_realloc(void** handle, size_t element_alignment, size_t element_size, size_t new_capacity);
|
||||
|
||||
#define CARR_RING_BUFFER_T(P) ((CARR_ring_buffer_t*)(P) - 1) // NULL / type unsafe!
|
||||
#define CARR_RING_BUFFER_IS_NULL(P) (&(P)->CARR_elem == NULL) // Guard against wrong pointer types.
|
||||
#define CARR_RING_BUFFER_GUARD(P, ...) (consume(&(P)->CARR_elem), __VA_ARGS__) // Guard against wrong pointer types.
|
||||
|
||||
static inline size_t CARR_ring_buffer_size(void* data) {
|
||||
CARR_ring_buffer_t* buffer = CARR_RING_BUFFER_T(data);
|
||||
return (buffer->capacity + buffer->tail - buffer->head) % buffer->capacity;
|
||||
}
|
||||
|
||||
static inline bool CARR_ring_buffer_ensure_can_push(void** handle, size_t alignment, size_t size, bool force) {
|
||||
void* data = *handle;
|
||||
if (data == NULL || CARR_ring_buffer_size(data) + 1 >= CARR_RING_BUFFER_T(data)->capacity) {
|
||||
size_t new_capacity = data == NULL ?
|
||||
ARRAY_DEFAULT_CAPACITY : ARRAY_CAPACITY_GROW(CARR_RING_BUFFER_T(data)->capacity);
|
||||
return CARR_handle_alloc(CARR_ring_buffer_realloc(handle, alignment, size, new_capacity), force);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline size_t CARR_ring_buffer_push_front(void* data) {
|
||||
if (data == NULL) return 0;
|
||||
CARR_ring_buffer_t* buffer = CARR_RING_BUFFER_T(data);
|
||||
return buffer->head = (buffer->head + buffer->capacity - 1) % buffer->capacity;
|
||||
}
|
||||
|
||||
static inline size_t CARR_ring_buffer_push_back(void* data) {
|
||||
if (data == NULL) return 0;
|
||||
CARR_ring_buffer_t* buffer = CARR_RING_BUFFER_T(data);
|
||||
size_t i = buffer->tail;
|
||||
buffer->tail = (buffer->tail + 1) % buffer->capacity;
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param P pointer to the first data element of the ring buffer
|
||||
* Ring buffer declaration, e.g. RING_BUFFER(int) my_ring = NULL;
|
||||
* @param TYPE type of the ring buffer element.
|
||||
*/
|
||||
#define RING_BUFFER(TYPE) struct { TYPE CARR_elem; }*
|
||||
|
||||
/**
|
||||
* @param P ring buffer
|
||||
* @return size of the ring buffer
|
||||
*/
|
||||
#define RING_BUFFER_SIZE(P) ((P) == NULL ? (size_t) 0 : \
|
||||
(RING_BUFFER_T(P)->capacity + RING_BUFFER_T(P)->tail - RING_BUFFER_T(P)->head) % RING_BUFFER_T(P)->capacity)
|
||||
#define RING_BUFFER_SIZE(P) (CARR_RING_BUFFER_IS_NULL(P) ? (size_t) 0 : CARR_ring_buffer_size(P))
|
||||
|
||||
/**
|
||||
* @param P pointer to the first data element of the ring buffer
|
||||
* @param P ring buffer
|
||||
* @return capacity of the ring buffer
|
||||
*/
|
||||
#define RING_BUFFER_CAPACITY(P) ((P) == NULL ? (size_t) 0 : RING_BUFFER_T(P)->capacity)
|
||||
#define RING_BUFFER_CAPACITY(P) (CARR_RING_BUFFER_IS_NULL(P) ? (size_t) 0 : CARR_RING_BUFFER_T(P)->capacity)
|
||||
|
||||
/**
|
||||
* Ensure enough capacity to push an element into ring buffer. Implicitly initializes when buffer is NULL.
|
||||
* On allocation failure, buffer is left unchanged.
|
||||
* @param P ring buffer
|
||||
* @return true if the operation succeeded
|
||||
*/
|
||||
#define RING_BUFFER_TRY_ENSURE_CAN_PUSH(P) CARR_RING_BUFFER_GUARD((P), \
|
||||
CARR_ring_buffer_ensure_can_push((void**)&(P), alignof(*(P)), sizeof(*(P)), false))
|
||||
|
||||
/**
|
||||
* Ensure enough capacity to push an element into ring buffer. Implicitly initializes when buffer is NULL.
|
||||
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
|
||||
* @param P ring buffer
|
||||
*/
|
||||
#define RING_BUFFER_ENSURE_CAN_PUSH(P) CARR_RING_BUFFER_GUARD((P), \
|
||||
(void)CARR_ring_buffer_ensure_can_push((void**)&(P), alignof(*(P)), sizeof(*(P)), true))
|
||||
|
||||
/**
|
||||
* Add element to the beginning of the ring buffer. Implicitly initializes when buffer is NULL.
|
||||
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
|
||||
* @param P pointer to the first data element of the buffer
|
||||
* @param P ring buffer
|
||||
* @return dereferenced pointer to the inserted element
|
||||
*/
|
||||
#define RING_BUFFER_PUSH_FRONT(P, ...) do { \
|
||||
if ((P) == NULL) (P) = CARR_ring_buffer_realloc(NULL, sizeof((P)[0]), ARRAY_DEFAULT_CAPACITY); \
|
||||
else if ((RING_BUFFER_T(P)->head + RING_BUFFER_T(P)->capacity - 1) % RING_BUFFER_T(P)->capacity == RING_BUFFER_T(P)->tail) \
|
||||
(P) = CARR_ring_buffer_realloc(RING_BUFFER_T(P), sizeof((P)[0]), ARRAY_CAPACITY_GROW(RING_BUFFER_T(P)->capacity)); \
|
||||
if ((P) == NULL) C_ARRAY_UTIL_ALLOCATION_FAILED(); \
|
||||
RING_BUFFER_T(P)->head = (RING_BUFFER_T(P)->head + RING_BUFFER_T(P)->capacity - 1) % RING_BUFFER_T(P)->capacity; \
|
||||
(P)[RING_BUFFER_T(P)->head] = (__VA_ARGS__); \
|
||||
} while(0)
|
||||
#define RING_BUFFER_PUSH_FRONT(P) \
|
||||
((RING_BUFFER_ENSURE_CAN_PUSH(P), (P) + CARR_ring_buffer_push_front(P))->CARR_elem)
|
||||
|
||||
/**
|
||||
* Add element to the end of the ring buffer. Implicitly initializes when buffer is NULL.
|
||||
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
|
||||
* @param P pointer to the first data element of the buffer
|
||||
* @param P ring buffer
|
||||
* @return dereferenced pointer to the inserted element
|
||||
*/
|
||||
#define RING_BUFFER_PUSH_BACK(P, ...) do { \
|
||||
if ((P) == NULL) (P) = CARR_ring_buffer_realloc(NULL, sizeof((P)[0]), ARRAY_DEFAULT_CAPACITY); \
|
||||
else if ((RING_BUFFER_T(P)->tail + 1) % RING_BUFFER_T(P)->capacity == RING_BUFFER_T(P)->head) \
|
||||
(P) = CARR_ring_buffer_realloc(RING_BUFFER_T(P), sizeof((P)[0]), ARRAY_CAPACITY_GROW(RING_BUFFER_T(P)->capacity)); \
|
||||
if ((P) == NULL) C_ARRAY_UTIL_ALLOCATION_FAILED(); \
|
||||
(P)[RING_BUFFER_T(P)->tail] = (__VA_ARGS__); \
|
||||
RING_BUFFER_T(P)->tail = (RING_BUFFER_T(P)->tail + 1) % RING_BUFFER_T(P)->capacity; \
|
||||
} while(0)
|
||||
#define RING_BUFFER_PUSH_BACK(P) \
|
||||
((RING_BUFFER_ENSURE_CAN_PUSH(P), (P) + CARR_ring_buffer_push_back(P))->CARR_elem)
|
||||
|
||||
/**
|
||||
* Get pointer to the first element of the ring buffer.
|
||||
* @param P pointer to the first data element of the buffer
|
||||
* @param P ring buffer
|
||||
* @return pointer to the first element of the ring buffer, or NULL
|
||||
*/
|
||||
#define RING_BUFFER_FRONT(P) ((P) == NULL || RING_BUFFER_T(P)->head == RING_BUFFER_T(P)->tail ? NULL : &(P)[RING_BUFFER_T(P)->head])
|
||||
#define RING_BUFFER_FRONT(P) (CARR_RING_BUFFER_IS_NULL(P) || \
|
||||
CARR_RING_BUFFER_T(P)->head == CARR_RING_BUFFER_T(P)->tail ? NULL : &(P)[CARR_RING_BUFFER_T(P)->head].CARR_elem)
|
||||
|
||||
/**
|
||||
* Get pointer to the last element of the ring buffer.
|
||||
* @param P pointer to the first data element of the buffer
|
||||
* @param P ring buffer
|
||||
* @return pointer to the last element of the ring buffer, or NULL
|
||||
*/
|
||||
#define RING_BUFFER_BACK(P) ((P) == NULL || RING_BUFFER_T(P)->head == RING_BUFFER_T(P)->tail ? NULL : \
|
||||
&(P)[(RING_BUFFER_T(P)->tail + RING_BUFFER_T(P)->capacity - 1) % RING_BUFFER_T(P)->capacity])
|
||||
#define RING_BUFFER_BACK(P) (CARR_RING_BUFFER_IS_NULL(P) || \
|
||||
CARR_RING_BUFFER_T(P)->head == CARR_RING_BUFFER_T(P)->tail ? NULL : \
|
||||
&(P)[(CARR_RING_BUFFER_T(P)->tail+CARR_RING_BUFFER_T(P)->capacity-1) % CARR_RING_BUFFER_T(P)->capacity].CARR_elem)
|
||||
|
||||
/**
|
||||
* Move beginning of the ring buffer forward (remove first element).
|
||||
* @param P pointer to the first data element of the buffer
|
||||
* @param P ring buffer
|
||||
*/
|
||||
#define RING_BUFFER_POP_FRONT(P) RING_BUFFER_T(P)->head = (RING_BUFFER_T(P)->head + 1) % RING_BUFFER_T(P)->capacity
|
||||
#define RING_BUFFER_POP_FRONT(P) CARR_RING_BUFFER_GUARD((P), (void)(CARR_RING_BUFFER_T(P)->head = \
|
||||
(CARR_RING_BUFFER_T(P)->head + 1) % CARR_RING_BUFFER_T(P)->capacity))
|
||||
|
||||
/**
|
||||
* Move end of the ring buffer backward (remove last element).
|
||||
* @param P pointer to the first data element of the buffer
|
||||
* @param P ring buffer
|
||||
*/
|
||||
#define RING_BUFFER_POP_BACK(P) \
|
||||
RING_BUFFER_T(P)->tail = (RING_BUFFER_T(P)->tail + RING_BUFFER_T(P)->capacity - 1) % RING_BUFFER_T(P)->capacity
|
||||
#define RING_BUFFER_POP_BACK(P) CARR_RING_BUFFER_GUARD((P), (void)(CARR_RING_BUFFER_T(P)->tail = \
|
||||
(CARR_RING_BUFFER_T(P)->tail + CARR_RING_BUFFER_T(P)->capacity - 1) % CARR_RING_BUFFER_T(P)->capacity))
|
||||
|
||||
/**
|
||||
* Deallocate the ring buffer
|
||||
* @param P pointer to the first data element of the buffer
|
||||
* @param P ring buffer
|
||||
*/
|
||||
#define RING_BUFFER_FREE(P) free(RING_BUFFER_T(P))
|
||||
#define RING_BUFFER_FREE(P) CARR_RING_BUFFER_GUARD((P), \
|
||||
(void)CARR_ring_buffer_realloc((void**)&(P), alignof(*(P)), sizeof(*(P)), 0))
|
||||
|
||||
#endif // CARRAYUTILS_H
|
||||
// === Maps ===
|
||||
|
||||
typedef bool (*CARR_equals_fp)(const void* a, const void* b);
|
||||
typedef size_t (*CARR_hash_fp)(const void* data);
|
||||
|
||||
#define CARR_MAP_LAYOUT_ARGS size_t key_alignment, size_t key_size, size_t value_alignment, size_t value_size
|
||||
#define CARR_MAP_LAYOUT_PASS key_alignment, key_size, value_alignment, value_size
|
||||
#define CARR_MAP_LAYOUT(P) \
|
||||
alignof((P)->CARR_keys[0].CARR_key[0]), sizeof((P)->CARR_keys[0].CARR_key[0]), \
|
||||
alignof((P)->CARR_values[0].CARR_value[0]), sizeof((P)->CARR_values[0].CARR_value[0])
|
||||
|
||||
typedef const void* (*CARR_map_dispatch_next_key_fp)(CARR_MAP_LAYOUT_ARGS, const void* data, const void* key_slot);
|
||||
typedef void* (*CARR_map_dispatch_find_fp)(CARR_MAP_LAYOUT_ARGS,
|
||||
void* data, const void* key, const void** resolved_key, bool insert);
|
||||
typedef bool (*CARR_map_dispatch_remove_fp)(CARR_MAP_LAYOUT_ARGS, void* data, const void* key);
|
||||
typedef bool (*CARR_map_dispatch_ensure_extra_capacity_fp)(CARR_MAP_LAYOUT_ARGS, void** handle, size_t count);
|
||||
typedef void (*CARR_map_dispatch_clear_fp)(CARR_MAP_LAYOUT_ARGS, void* data);
|
||||
typedef void (*CARR_map_dispatch_free_fp)(CARR_MAP_LAYOUT_ARGS, void* data);
|
||||
|
||||
typedef struct {
|
||||
CARR_map_dispatch_next_key_fp next_key;
|
||||
CARR_map_dispatch_find_fp find;
|
||||
CARR_map_dispatch_remove_fp remove;
|
||||
CARR_map_dispatch_ensure_extra_capacity_fp ensure_extra_capacity;
|
||||
CARR_map_dispatch_clear_fp clear;
|
||||
CARR_map_dispatch_free_fp free;
|
||||
} CARR_map_dispatch_t;
|
||||
|
||||
#define CARR_MAP_KEY_PTR(P, ...) \
|
||||
(&((true ? NULL : (P))->CARR_keys[((uintptr_t)(__VA_ARGS__) / sizeof((P)->CARR_keys[0]) - 1)].CARR_key[0]))
|
||||
#define CARR_MAP_VALUE_PTR(P, ...) \
|
||||
(&((true ? NULL : (P))->CARR_values[((uintptr_t)(__VA_ARGS__) / sizeof((P)->CARR_values[0]) - 1)].CARR_value[0]))
|
||||
#define CARR_MAP_KEY_GUARD(P, ...) \
|
||||
(true ? (__VA_ARGS__) : &(P)->CARR_keys[0].CARR_key[0]) // Guard against wrong key types.
|
||||
#define CARR_MAP_DISPATCH(P, NAME, ...) \
|
||||
(((const CARR_map_dispatch_t**)(P))[-1]->NAME(CARR_MAP_LAYOUT(P), __VA_ARGS__))
|
||||
|
||||
bool CARR_hash_map_linear_probing_rehash(CARR_MAP_LAYOUT_ARGS, void** handle, CARR_equals_fp equals, CARR_hash_fp hash,
|
||||
size_t new_capacity, uint32_t probing_limit, float load_factor);
|
||||
|
||||
/**
|
||||
* Map declaration, e.g. MAP(int, int) my_map = NULL;
|
||||
* Map must be explicitly initialized before usage, e.g. via HASH_MAP_REHASH.
|
||||
* @param KEY_TYPE type of the map key.
|
||||
* @param VALUE_TYPE type of the map value.
|
||||
*/
|
||||
#define MAP(KEY_TYPE, VALUE_TYPE) union { \
|
||||
struct { char CARR_dummy; const KEY_TYPE CARR_key[]; } CARR_keys[1]; \
|
||||
struct { char CARR_dummy; VALUE_TYPE CARR_value[]; } CARR_values[1]; \
|
||||
}*
|
||||
|
||||
/**
|
||||
* Rehash a hash map with given strategy. It will be initialized if NULL.
|
||||
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
|
||||
* List of available strategies with accepted parameters and sensible defaults:
|
||||
* 1. linear_probing (
|
||||
* CARR_equals_fp equals, // Key comparison function.
|
||||
* CARR_hash_fp hash, // Key hash calculation function.
|
||||
* size_t new_capacity, // New (min) capacity, must not be less than current number of items. Can be 0.
|
||||
* uint32_t probing_limit, // Search length, triggering rehash. Must not be too low, around 10 should be fine?
|
||||
* float load_factor // Min load factor needed to allow rehash triggered by probing_limit. 0.75 is fine.
|
||||
* )
|
||||
* @param P map
|
||||
* @param STRATEGY strategy to use
|
||||
* @param ... parameters for the rehash strategy
|
||||
*/
|
||||
#define HASH_MAP_REHASH(P, STRATEGY, ...) \
|
||||
((void)CARR_handle_alloc(CARR_hash_map_##STRATEGY##_rehash(CARR_MAP_LAYOUT(P), (void**)&(P), __VA_ARGS__), true))
|
||||
|
||||
/**
|
||||
* Rehash a hash map with given strategy. It will be initialized if NULL.
|
||||
* On allocation failure, map is left unchanged.
|
||||
* For list of available strategies see HASH_MAP_REHASH.
|
||||
* @param P map
|
||||
* @param STRATEGY strategy to use
|
||||
* @return true if the operation succeeded
|
||||
*/
|
||||
#define HASH_MAP_TRY_REHASH(P, STRATEGY, ...) \
|
||||
(CARR_hash_map_##STRATEGY##_rehash(CARR_MAP_LAYOUT(P), (void**)&(P), __VA_ARGS__))
|
||||
|
||||
/**
|
||||
* Find the next resolved key present in the map, or NULL.
|
||||
* Enumeration order is implementation-defined.
|
||||
* @param P map
|
||||
* @param KEY_PTR pointer to the current resolved key, or NULL
|
||||
* @return pointer to the next resolved key
|
||||
*/
|
||||
#define MAP_NEXT_KEY(P, KEY_PTR) \
|
||||
CARR_MAP_KEY_PTR((P), CARR_MAP_DISPATCH((P), next_key, (P), CARR_MAP_KEY_GUARD((P), (KEY_PTR))))
|
||||
|
||||
/**
|
||||
* Find a value for the provided key.
|
||||
* @param P map
|
||||
* @param KEY key to find, can be a compound literal, like (int){0}
|
||||
* @return pointer to the found value, or NULL
|
||||
*/
|
||||
#define MAP_FIND(P, KEY) \
|
||||
CARR_MAP_VALUE_PTR((P), CARR_MAP_DISPATCH((P), find, (P), CARR_MAP_KEY_GUARD((P), &(KEY)), NULL, false))
|
||||
|
||||
/**
|
||||
* Find a value for the provided key, or insert a new one.
|
||||
* Value is zeroed for newly inserted items.
|
||||
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
|
||||
* @param P map
|
||||
* @param KEY key to find, can be a compound literal, like (int){0}
|
||||
* @return dereferenced pointer to the found value
|
||||
*/
|
||||
#define MAP_AT(P, KEY) (*(MAP_ENSURE_EXTRA_CAPACITY((P), 1), \
|
||||
CARR_MAP_VALUE_PTR((P), CARR_MAP_DISPATCH((P), find, (P), CARR_MAP_KEY_GUARD((P), &(KEY)), NULL, true))))
|
||||
|
||||
/**
|
||||
* Resolve provided key and find corresponding value.
|
||||
* Using resolved key addresses speeds up subsequent map operations.
|
||||
* @param P map
|
||||
* @param KEY_PTR pointer to the key to find, replaced with resolved key address, or NULL
|
||||
* @return pointer to the found value, or NULL
|
||||
*/
|
||||
#define MAP_RESOLVE(P, KEY_PTR) CARR_MAP_VALUE_PTR((P), \
|
||||
CARR_MAP_DISPATCH((P), find, (P), CARR_MAP_KEY_GUARD((P), (KEY_PTR)), (const void**) &(KEY_PTR), false))
|
||||
|
||||
/**
|
||||
* Resolve provided key and find corresponding value, or insert a new one.
|
||||
* Using resolved key addresses speeds up subsequent map operations.
|
||||
* Returned value pointer may be NULL, indicating that the entry was just inserted, use MAP_FIND or MAP_AT to access it.
|
||||
* On allocation failure, map is left unchanged.
|
||||
* @param P map
|
||||
* @param KEY_PTR pointer to the key to find, replaced with resolved key address
|
||||
* @return pointer to the found value, or NULL
|
||||
*/
|
||||
#define MAP_RESOLVE_OR_INSERT(P, KEY_PTR) (MAP_TRY_ENSURE_EXTRA_CAPACITY((P), 1), CARR_MAP_VALUE_PTR((P), \
|
||||
CARR_MAP_DISPATCH((P), find, (P), CARR_MAP_KEY_GUARD((P), (KEY_PTR)), (const void**) &(KEY_PTR), true)))
|
||||
|
||||
/**
|
||||
* Remove the provided key, if one exists.
|
||||
* @param P map
|
||||
* @param KEY key to remove, can be a compound literal, like (int){0}
|
||||
* @return true if the key was removed
|
||||
*/
|
||||
#define MAP_REMOVE(P, KEY) CARR_MAP_DISPATCH((P), remove, (P), CARR_MAP_KEY_GUARD((P), &(KEY)))
|
||||
|
||||
/**
|
||||
* Ensure that map has enough capacity to insert COUNT more items without reallocation.
|
||||
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
|
||||
* @param P map
|
||||
* @param COUNT number of new items
|
||||
*/
|
||||
#define MAP_ENSURE_EXTRA_CAPACITY(P, COUNT) ((void)CARR_handle_alloc(MAP_TRY_ENSURE_EXTRA_CAPACITY((P), (COUNT)), true))
|
||||
|
||||
/**
|
||||
* Ensure that map has enough capacity to insert COUNT more items without reallocation.
|
||||
* On allocation failure, map is left unchanged.
|
||||
* @param P map
|
||||
* @param COUNT number of new items
|
||||
* @return true if the operation succeeded
|
||||
*/
|
||||
#define MAP_TRY_ENSURE_EXTRA_CAPACITY(P, COUNT) CARR_MAP_DISPATCH((P), ensure_extra_capacity, (void**)&(P), (COUNT))
|
||||
|
||||
/**
|
||||
* Clear the map.
|
||||
* @param P map
|
||||
*/
|
||||
#define MAP_CLEAR(P) CARR_MAP_DISPATCH((P), clear, (P))
|
||||
|
||||
/**
|
||||
* Free the map.
|
||||
* @param P map
|
||||
*/
|
||||
#define MAP_FREE(P) ((P) == NULL ? 0 : CARR_MAP_DISPATCH((P), free, (P)), (void)((P) = NULL))
|
||||
|
||||
#endif // C_ARRAY_UTIL_H
|
||||
|
||||
@@ -87,12 +87,12 @@ typedef union MemoryHandle {
|
||||
#define MAX_SHARED_PAGE_SIZE ((1ULL << MAX_BLOCK_LEVEL) * BLOCK_SIZE)
|
||||
|
||||
typedef struct {
|
||||
BlockPair* blockPairs;
|
||||
void* mappedData;
|
||||
uint32_t freeLevelIndices[MAX_BLOCK_LEVEL+1]; // Indices start from 1
|
||||
uint32_t freeBlockPairIndex; // Indices start from 1
|
||||
uint32_t nextPageIndex;
|
||||
uint32_t memoryType;
|
||||
ARRAY(BlockPair) blockPairs;
|
||||
void* mappedData;
|
||||
uint32_t freeLevelIndices[MAX_BLOCK_LEVEL+1]; // Indices start from 1
|
||||
uint32_t freeBlockPairIndex; // Indices start from 1
|
||||
uint32_t nextPageIndex;
|
||||
uint32_t memoryType;
|
||||
} SharedPageData;
|
||||
|
||||
typedef struct {
|
||||
@@ -120,9 +120,9 @@ struct VKAllocator {
|
||||
VKDevice* device;
|
||||
VkPhysicalDeviceMemoryProperties memoryProperties;
|
||||
|
||||
Page* pages;
|
||||
uint32_t freePageIndex;
|
||||
Pool pools[VK_MAX_MEMORY_TYPES];
|
||||
ARRAY(Page) pages;
|
||||
uint32_t freePageIndex;
|
||||
Pool pools[VK_MAX_MEMORY_TYPES];
|
||||
};
|
||||
|
||||
#define NO_PAGE_INDEX (~0U)
|
||||
@@ -222,7 +222,7 @@ static uint32_t VKAllocator_AllocatePage(VKAllocator* alloc, uint32_t memoryType
|
||||
} else {
|
||||
index = ARRAY_SIZE(alloc->pages);
|
||||
VK_RUNTIME_ASSERT(index < MAX_PAGES);
|
||||
ARRAY_PUSH_BACK(alloc->pages, (Page) {});
|
||||
ARRAY_PUSH_BACK(alloc->pages) = (Page) {};
|
||||
page = &ARRAY_LAST(alloc->pages);
|
||||
}
|
||||
assert(page->memory == VK_NULL_HANDLE);
|
||||
@@ -275,7 +275,7 @@ static uint32_t VKAllocator_PopFreeBlockPair(SharedPageData* data, uint32_t leve
|
||||
pair = &data->blockPairs[pairIndex-1];
|
||||
data->freeBlockPairIndex = pair->nextFree;
|
||||
} else {
|
||||
ARRAY_PUSH_BACK(data->blockPairs, (BlockPair) {});
|
||||
ARRAY_PUSH_BACK(data->blockPairs) = (BlockPair) {};
|
||||
pairIndex = ARRAY_SIZE(data->blockPairs);
|
||||
pair = &data->blockPairs[pairIndex-1];
|
||||
}
|
||||
@@ -369,7 +369,7 @@ static AllocationResult VKAllocator_AllocateForResource(VKMemoryRequirements* re
|
||||
// Adjust level to ensure proper alignment. Not very optimal, but this is a very rare case.
|
||||
while (blockSize % alignment != 0) { level++; blockSize <<= 1; }
|
||||
|
||||
J2dRlsTraceLn6(J2D_TRACE_VERBOSE,
|
||||
J2dRlsTraceLn6(J2D_TRACE_VERBOSE2,
|
||||
"VKAllocator_Allocate: level=%d, blockSize=%d, size=%d, alignment=%d, memoryType=%d, dedicated=%d",
|
||||
level, blockSize, size, alignment, memoryType, dedicated);
|
||||
|
||||
@@ -401,13 +401,13 @@ static AllocationResult VKAllocator_AllocateForResource(VKMemoryRequirements* re
|
||||
data = page->sharedPageData = (SharedPageData*) calloc(1, sizeof(SharedPageData));
|
||||
VK_RUNTIME_ASSERT(page->sharedPageData);
|
||||
data->memoryType = memoryType;
|
||||
ARRAY_PUSH_BACK(data->blockPairs, (BlockPair) {
|
||||
.offset = 0,
|
||||
.parent = 0,
|
||||
.nextFree = 0,
|
||||
.firstFree = 1,
|
||||
.secondFree = 0,
|
||||
});
|
||||
ARRAY_PUSH_BACK(data->blockPairs) = (BlockPair) {
|
||||
.offset = 0,
|
||||
.parent = 0,
|
||||
.nextFree = 0,
|
||||
.firstFree = 1,
|
||||
.secondFree = 0,
|
||||
};
|
||||
data->freeLevelIndices[pageLevel] = 1;
|
||||
data->nextPageIndex = pool->sharedPagesIndex;
|
||||
pool->sharedPagesIndex = pageIndex;
|
||||
|
||||
@@ -83,6 +83,8 @@ static void vulkanLibClose() {
|
||||
}
|
||||
#endif
|
||||
|
||||
VKComposites_Destroy(geInstance->composites);
|
||||
|
||||
if (geInstance->vkDestroyInstance != NULL) {
|
||||
geInstance->vkDestroyInstance(geInstance->vkInstance, NULL);
|
||||
}
|
||||
@@ -205,13 +207,13 @@ static jboolean VK_InitGraphicsEnvironment(PFN_vkGetInstanceProcAddr vkGetInstan
|
||||
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, " %s", (char *) extensions[i].extensionName)
|
||||
}
|
||||
|
||||
pchar* enabledLayers = NULL;
|
||||
pchar* enabledExtensions = NULL;
|
||||
ARRAY(pchar) enabledLayers = NULL;
|
||||
ARRAY(pchar) enabledExtensions = NULL;
|
||||
void *pNext = NULL;
|
||||
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||
ARRAY_PUSH_BACK(enabledExtensions, VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
|
||||
ARRAY_PUSH_BACK(enabledExtensions) = VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME;
|
||||
#endif
|
||||
ARRAY_PUSH_BACK(enabledExtensions, VK_KHR_SURFACE_EXTENSION_NAME);
|
||||
ARRAY_PUSH_BACK(enabledExtensions) = VK_KHR_SURFACE_EXTENSION_NAME;
|
||||
|
||||
// Check required layers & extensions.
|
||||
for (uint32_t i = 0; i < ARRAY_SIZE(enabledExtensions); i++) {
|
||||
@@ -264,8 +266,8 @@ static jboolean VK_InitGraphicsEnvironment(PFN_vkGetInstanceProcAddr vkGetInstan
|
||||
}
|
||||
|
||||
if (foundDebugLayer && foundDebugExt) {
|
||||
ARRAY_PUSH_BACK(enabledLayers, VALIDATION_LAYER_NAME);
|
||||
ARRAY_PUSH_BACK(enabledExtensions, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||
ARRAY_PUSH_BACK(enabledLayers) = VALIDATION_LAYER_NAME;
|
||||
ARRAY_PUSH_BACK(enabledExtensions) = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
|
||||
pNext = &features;
|
||||
} else {
|
||||
J2dRlsTraceLn2(J2D_TRACE_WARNING, "Vulkan: %s and %s are not supported",
|
||||
@@ -302,6 +304,8 @@ static jboolean VK_InitGraphicsEnvironment(PFN_vkGetInstanceProcAddr vkGetInstan
|
||||
ARRAY_FREE(enabledLayers);
|
||||
ARRAY_FREE(enabledExtensions);
|
||||
|
||||
geInstance->composites = VKComposites_Create();
|
||||
|
||||
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||
INSTANCE_PROC(vkGetPhysicalDeviceWaylandPresentationSupportKHR);
|
||||
INSTANCE_PROC(vkCreateWaylandSurfaceKHR);
|
||||
@@ -323,25 +327,26 @@ static jboolean VK_InitGraphicsEnvironment(PFN_vkGetInstanceProcAddr vkGetInstan
|
||||
|
||||
// Create debug messenger
|
||||
#if defined(DEBUG)
|
||||
INSTANCE_PROC(vkCreateDebugUtilsMessengerEXT);
|
||||
INSTANCE_PROC(vkDestroyDebugUtilsMessengerEXT);
|
||||
if (pNext) {
|
||||
VkDebugUtilsMessengerCreateInfoEXT debugUtilsMessengerCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
|
||||
.flags = 0,
|
||||
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT,
|
||||
.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
|
||||
.pfnUserCallback = &debugCallback
|
||||
};
|
||||
VK_IF_ERROR(geInstance->vkCreateDebugUtilsMessengerEXT(geInstance->vkInstance, &debugUtilsMessengerCreateInfo,
|
||||
NULL, &geInstance->debugMessenger)) {}
|
||||
if (foundDebugLayer && foundDebugExt) {
|
||||
INSTANCE_PROC(vkCreateDebugUtilsMessengerEXT);
|
||||
INSTANCE_PROC(vkDestroyDebugUtilsMessengerEXT);
|
||||
if (pNext) {
|
||||
VkDebugUtilsMessengerCreateInfoEXT debugUtilsMessengerCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
|
||||
.flags = 0,
|
||||
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT,
|
||||
.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
|
||||
.pfnUserCallback = &debugCallback
|
||||
};
|
||||
VK_IF_ERROR(geInstance->vkCreateDebugUtilsMessengerEXT(geInstance->vkInstance, &debugUtilsMessengerCreateInfo,
|
||||
NULL, &geInstance->debugMessenger)) {}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return JNI_TRUE;
|
||||
@@ -473,9 +478,9 @@ static jboolean VK_FindDevices() {
|
||||
continue;
|
||||
}
|
||||
|
||||
pchar* deviceEnabledLayers = NULL;
|
||||
pchar* deviceEnabledExtensions = NULL;
|
||||
ARRAY_PUSH_BACK(deviceEnabledExtensions, VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||
ARRAY(pchar) deviceEnabledLayers = NULL;
|
||||
ARRAY(pchar) deviceEnabledExtensions = NULL;
|
||||
ARRAY_PUSH_BACK(deviceEnabledExtensions) = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
|
||||
|
||||
// Validation layer
|
||||
#ifdef DEBUG
|
||||
@@ -483,7 +488,7 @@ static jboolean VK_FindDevices() {
|
||||
for (uint32_t j = 0; j < layerCount; j++) {
|
||||
if (strcmp(VALIDATION_LAYER_NAME, layers[j].layerName) == 0) {
|
||||
validationLayerNotSupported = 0;
|
||||
ARRAY_PUSH_BACK(deviceEnabledLayers, VALIDATION_LAYER_NAME);
|
||||
ARRAY_PUSH_BACK(deviceEnabledLayers) = VALIDATION_LAYER_NAME;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -497,15 +502,14 @@ static jboolean VK_FindDevices() {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
ARRAY_PUSH_BACK(geInstance->devices,
|
||||
((VKDevice) {
|
||||
.name = deviceName,
|
||||
.handle = VK_NULL_HANDLE,
|
||||
.physicalDevice = geInstance->physicalDevices[i],
|
||||
.queueFamily = queueFamily,
|
||||
.enabledLayers = deviceEnabledLayers,
|
||||
.enabledExtensions = deviceEnabledExtensions
|
||||
}));
|
||||
ARRAY_PUSH_BACK(geInstance->devices) = (VKDevice) {
|
||||
.name = deviceName,
|
||||
.handle = VK_NULL_HANDLE,
|
||||
.physicalDevice = geInstance->physicalDevices[i],
|
||||
.queueFamily = queueFamily,
|
||||
.enabledLayers = deviceEnabledLayers,
|
||||
.enabledExtensions = deviceEnabledExtensions
|
||||
};
|
||||
}
|
||||
if (ARRAY_SIZE(geInstance->devices) == 0) {
|
||||
J2dRlsTraceLn(J2D_TRACE_ERROR, "Vulkan: No compatible device found")
|
||||
@@ -614,10 +618,14 @@ static jboolean VK_InitDevice(VKDevice* device) {
|
||||
DEVICE_PROC(vkDestroyDescriptorSetLayout);
|
||||
DEVICE_PROC(vkUpdateDescriptorSets);
|
||||
DEVICE_PROC(vkCreateDescriptorPool);
|
||||
DEVICE_PROC(vkDestroyDescriptorPool);
|
||||
DEVICE_PROC(vkAllocateDescriptorSets);
|
||||
DEVICE_PROC(vkCmdBindDescriptorSets);
|
||||
DEVICE_PROC(vkGetImageMemoryRequirements2);
|
||||
DEVICE_PROC(vkCreateBuffer);
|
||||
DEVICE_PROC(vkDestroyBuffer);
|
||||
DEVICE_PROC(vkCreateBufferView);
|
||||
DEVICE_PROC(vkDestroyBufferView);
|
||||
DEVICE_PROC(vkGetBufferMemoryRequirements2);
|
||||
DEVICE_PROC(vkBindBufferMemory);
|
||||
DEVICE_PROC(vkMapMemory);
|
||||
@@ -625,7 +633,6 @@ static jboolean VK_InitDevice(VKDevice* device) {
|
||||
DEVICE_PROC(vkCmdBindVertexBuffers);
|
||||
DEVICE_PROC(vkCreateRenderPass);
|
||||
DEVICE_PROC(vkDestroyRenderPass);
|
||||
DEVICE_PROC(vkDestroyBuffer);
|
||||
DEVICE_PROC(vkFreeMemory);
|
||||
DEVICE_PROC(vkDestroyImageView);
|
||||
DEVICE_PROC(vkDestroyImage);
|
||||
|
||||
@@ -27,16 +27,18 @@
|
||||
#ifndef VKBase_h_Included
|
||||
#define VKBase_h_Included
|
||||
#include "VKTypes.h"
|
||||
#include "VKComposites.h"
|
||||
#include "VKTexturePool.h"
|
||||
#include "VKRenderState.h"
|
||||
#include "VKUtil.h"
|
||||
|
||||
struct VKDevice {
|
||||
VkDevice handle;
|
||||
VkPhysicalDevice physicalDevice;
|
||||
char* name;
|
||||
uint32_t queueFamily;
|
||||
pchar* enabledLayers;
|
||||
pchar* enabledExtensions;
|
||||
ARRAY(pchar) enabledLayers;
|
||||
ARRAY(pchar) enabledExtensions;
|
||||
VkQueue queue;
|
||||
|
||||
VKAllocator* allocator;
|
||||
@@ -92,10 +94,14 @@ struct VKDevice {
|
||||
PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout;
|
||||
PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets;
|
||||
PFN_vkCreateDescriptorPool vkCreateDescriptorPool;
|
||||
PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool;
|
||||
PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets;
|
||||
PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets;
|
||||
PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2;
|
||||
PFN_vkCreateBuffer vkCreateBuffer;
|
||||
PFN_vkDestroyBuffer vkDestroyBuffer;
|
||||
PFN_vkCreateBufferView vkCreateBufferView;
|
||||
PFN_vkDestroyBufferView vkDestroyBufferView;
|
||||
PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2;
|
||||
PFN_vkBindBufferMemory vkBindBufferMemory;
|
||||
PFN_vkMapMemory vkMapMemory;
|
||||
@@ -103,7 +109,6 @@ struct VKDevice {
|
||||
PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers;
|
||||
PFN_vkCreateRenderPass vkCreateRenderPass;
|
||||
PFN_vkDestroyRenderPass vkDestroyRenderPass;
|
||||
PFN_vkDestroyBuffer vkDestroyBuffer;
|
||||
PFN_vkFreeMemory vkFreeMemory;
|
||||
PFN_vkDestroyImageView vkDestroyImageView;
|
||||
PFN_vkDestroyImage vkDestroyImage;
|
||||
@@ -116,10 +121,12 @@ struct VKDevice {
|
||||
};
|
||||
|
||||
struct VKGraphicsEnvironment {
|
||||
VkInstance vkInstance;
|
||||
VkPhysicalDevice* physicalDevices;
|
||||
VKDevice* devices;
|
||||
VKDevice* currentDevice;
|
||||
VkInstance vkInstance;
|
||||
ARRAY(VkPhysicalDevice) physicalDevices;
|
||||
ARRAY(VKDevice) devices;
|
||||
VKDevice* currentDevice;
|
||||
|
||||
VKComposites composites;
|
||||
|
||||
#if defined(DEBUG)
|
||||
VkDebugUtilsMessengerEXT debugMessenger;
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
#include "Trace.h"
|
||||
#include "VKImage.h"
|
||||
#include "VKBuffer.h"
|
||||
#include "CArrayUtil.h"
|
||||
#include "VKUtil.h"
|
||||
|
||||
static void VKBlitSwToTextureViaPooledTexture(VKRenderingContext* context, VKImage* dest, const SurfaceDataRasInfo *srcInfo,
|
||||
int dx1, int dy1, int dx2, int dy2) {
|
||||
@@ -54,7 +54,7 @@ static void VKBlitSwToTextureViaPooledTexture(VKRenderingContext* context, VKIma
|
||||
return;
|
||||
}
|
||||
|
||||
VKTxVertex* vertices = ARRAY_ALLOC(VKTxVertex, 4);
|
||||
ARRAY(VKTxVertex) vertices = ARRAY_ALLOC(VKTxVertex, 4);
|
||||
/*
|
||||
* (p1)---------(p2)
|
||||
* | |
|
||||
@@ -67,10 +67,10 @@ static void VKBlitSwToTextureViaPooledTexture(VKRenderingContext* context, VKIma
|
||||
double u = (double)sw / VKTexturePoolHandle_GetActualWidth(hnd);
|
||||
double v = (double)sh / VKTexturePoolHandle_GetActualHeight(hnd);
|
||||
|
||||
ARRAY_PUSH_BACK(vertices, ((VKTxVertex) {dx1, dy1, 0.0f, 0.0f}));
|
||||
ARRAY_PUSH_BACK(vertices, ((VKTxVertex) {dx2, dy1, u, 0.0f}));
|
||||
ARRAY_PUSH_BACK(vertices, ((VKTxVertex) {dx1, dy2, 0.0f, v}));
|
||||
ARRAY_PUSH_BACK(vertices, ((VKTxVertex) {dx2, dy2, u, v}));
|
||||
ARRAY_PUSH_BACK(vertices) = (VKTxVertex) {dx1, dy1, 0.0f, 0.0f};
|
||||
ARRAY_PUSH_BACK(vertices) = (VKTxVertex) {dx2, dy1, u, 0.0f};
|
||||
ARRAY_PUSH_BACK(vertices) = (VKTxVertex) {dx1, dy2, 0.0f, v};
|
||||
ARRAY_PUSH_BACK(vertices) = (VKTxVertex) {dx2, dy2, u, v};
|
||||
|
||||
VKBuffer* renderVertexBuffer = ARRAY_TO_VERTEX_BUF(device, vertices);
|
||||
ARRAY_FREE(vertices);
|
||||
@@ -137,6 +137,128 @@ static void VKBlitSwToTextureViaPooledTexture(VKRenderingContext* context, VKIma
|
||||
// VKBuffer_Destroy(device, renderVertexBuffer);
|
||||
}
|
||||
|
||||
static void VKBlitTextureToTexture(VKRenderingContext* context, VKImage* src, VKImage* dest,
|
||||
int sx1, int sy1, int sx2, int sy2,
|
||||
double dx1, double dy1, double dx2, double dy2)
|
||||
{
|
||||
VKSDOps* surface = context->surface;
|
||||
|
||||
VKRenderer_FlushRenderPass(surface);
|
||||
|
||||
VKDevice* device = surface->device;
|
||||
|
||||
VKTxVertex* vertices = ARRAY_ALLOC(VKTxVertex, 4);
|
||||
/*
|
||||
* (p1)---------(p2)
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* (p4)---------(p3)
|
||||
*/
|
||||
|
||||
double u1 = (double)sx1 / src->extent.width;
|
||||
double v1 = (double)sy1 / src->extent.height;
|
||||
double u2 = (double)sx2 / src->extent.width;
|
||||
double v2 = (double)sy2 / src->extent.height;
|
||||
|
||||
ARRAY_PUSH_BACK(vertices) = (VKTxVertex) {dx1, dy1, u1, v1};
|
||||
ARRAY_PUSH_BACK(vertices) = (VKTxVertex) {dx2, dy1, u2, v1};
|
||||
ARRAY_PUSH_BACK(vertices) = (VKTxVertex) {dx1, dy2, u1, v2};
|
||||
ARRAY_PUSH_BACK(vertices) = (VKTxVertex) {dx2, dy2, u2, v2};
|
||||
|
||||
VKBuffer* renderVertexBuffer = ARRAY_TO_VERTEX_BUF(device, vertices);
|
||||
ARRAY_FREE(vertices);
|
||||
|
||||
VkCommandBuffer cb = VKRenderer_Record(device->renderer);
|
||||
{
|
||||
VkImageMemoryBarrier barrier;
|
||||
VKBarrierBatch barrierBatch = {};
|
||||
VKRenderer_AddImageBarrier(&barrier, &barrierBatch, src,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
VK_ACCESS_SHADER_READ_BIT,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
if (barrierBatch.barrierCount > 0) {
|
||||
device->vkCmdPipelineBarrier(cb, barrierBatch.srcStages, barrierBatch.dstStages,
|
||||
0, 0, NULL,
|
||||
0, NULL,
|
||||
barrierBatch.barrierCount, &barrier);
|
||||
}
|
||||
}
|
||||
|
||||
VKRenderer_TextureRender(context, dest, src,
|
||||
renderVertexBuffer->handle, 4);
|
||||
|
||||
// TODO: Not optimal but required for releasing raster buffer. Such Buffers should also be managed by special pools
|
||||
// TODO: Also, consider using VKRenderer_FlushRenderPass here to process pending command
|
||||
VKRenderer_Flush(device->renderer);
|
||||
VKRenderer_Sync(device->renderer);
|
||||
// TODO: Add proper sync for renderVertexBuffer
|
||||
// VKBuffer_Destroy(device, renderVertexBuffer);
|
||||
}
|
||||
|
||||
static jboolean clipDestCoords(
|
||||
VKRenderingContext* context,
|
||||
jdouble *dx1, jdouble *dy1, jdouble *dx2, jdouble *dy2,
|
||||
jint *sx1, jint *sy1, jint *sx2, jint *sy2,
|
||||
jint destW, jint destH) {
|
||||
// Trim destination rect by clip-rect (or dest.bounds)
|
||||
const jint sw = *sx2 - *sx1;
|
||||
const jint sh = *sy2 - *sy1;
|
||||
const jdouble dw = *dx2 - *dx1;
|
||||
const jdouble dh = *dy2 - *dy1;
|
||||
VkRect2D* clipRect = &context->clipRect;
|
||||
jdouble dcx1 = 0;
|
||||
jdouble dcx2 = destW;
|
||||
jdouble dcy1 = 0;
|
||||
jdouble dcy2 = destH;
|
||||
if (clipRect->offset.x > dcx1)
|
||||
dcx1 = clipRect->offset.x;
|
||||
const int maxX = clipRect->offset.x + clipRect->extent.width;
|
||||
if (dcx2 > maxX)
|
||||
dcx2 = maxX;
|
||||
if (clipRect->offset.y > dcy1)
|
||||
dcy1 = clipRect->offset.y;
|
||||
const int maxY = clipRect->offset.y + clipRect->extent.height;
|
||||
if (dcy2 > maxY)
|
||||
dcy2 = maxY;
|
||||
|
||||
if (dcx1 >= dcx2) {
|
||||
J2dTraceLn2(J2D_TRACE_ERROR, "\tclipDestCoords: dcx1=%1.2f, dcx2=%1.2f", dcx1, dcx2);
|
||||
dcx1 = dcx2;
|
||||
}
|
||||
if (dcy1 >= dcy2) {
|
||||
J2dTraceLn2(J2D_TRACE_ERROR, "\tclipDestCoords: dcy1=%1.2f, dcy2=%1.2f", dcy1, dcy2);
|
||||
dcy1 = dcy2;
|
||||
}
|
||||
if (*dx2 <= dcx1 || *dx1 >= dcx2 || *dy2 <= dcy1 || *dy1 >= dcy2) {
|
||||
J2dTraceLn(J2D_TRACE_INFO, "\tclipDestCoords: dest rect doesn't intersect clip area");
|
||||
J2dTraceLn4(J2D_TRACE_INFO, "\tdx2=%1.4f <= dcx1=%1.4f || *dx1=%1.4f >= dcx2=%1.4f", *dx2, dcx1, *dx1, dcx2);
|
||||
J2dTraceLn4(J2D_TRACE_INFO, "\t*dy2=%1.4f <= dcy1=%1.4f || *dy1=%1.4f >= dcy2=%1.4f", *dy2, dcy1, *dy1, dcy2);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
if (*dx1 < dcx1) {
|
||||
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdx1=%1.2f, will be clipped to %1.2f | sx1+=%d", *dx1, dcx1, (jint)((dcx1 - *dx1) * (sw/dw)));
|
||||
*sx1 += (jint)((dcx1 - *dx1) * (sw/dw));
|
||||
*dx1 = dcx1;
|
||||
}
|
||||
if (*dx2 > dcx2) {
|
||||
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdx2=%1.2f, will be clipped to %1.2f | sx2-=%d", *dx2, dcx2, (jint)((*dx2 - dcx2) * (sw/dw)));
|
||||
*sx2 -= (jint)((*dx2 - dcx2) * (sw/dw));
|
||||
*dx2 = dcx2;
|
||||
}
|
||||
if (*dy1 < dcy1) {
|
||||
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdy1=%1.2f, will be clipped to %1.2f | sy1+=%d", *dy1, dcy1, (jint)((dcy1 - *dy1) * (sh/dh)));
|
||||
*sy1 += (jint)((dcy1 - *dy1) * (sh/dh));
|
||||
*dy1 = dcy1;
|
||||
}
|
||||
if (*dy2 > dcy2) {
|
||||
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdy2=%1.2f, will be clipped to %1.2f | sy2-=%d", *dy2, dcy2, (jint)((*dy2 - dcy2) * (sh/dh)));
|
||||
*sy2 -= (jint)((*dy2 - dcy2) * (sh/dh));
|
||||
*dy2 = dcy2;
|
||||
}
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
void VKBlitLoops_IsoBlit(JNIEnv *env,
|
||||
VKRenderingContext* context, jlong pSrcOps,
|
||||
@@ -147,75 +269,81 @@ void VKBlitLoops_IsoBlit(JNIEnv *env,
|
||||
jdouble dx1, jdouble dy1,
|
||||
jdouble dx2, jdouble dy2)
|
||||
{
|
||||
J2dRlsTraceLn8(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: BLIT_IsoBlit (%d %d %d %d) -> (%f %f %f %f) ",
|
||||
J2dRlsTraceLn8(J2D_TRACE_VERBOSE, "VKBlitLoops_IsoBlit: (%d %d %d %d) -> (%f %f %f %f) ",
|
||||
sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
|
||||
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: BLIT_IsoBlit texture=%d xform=%d",
|
||||
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "VKBlitLoops_IsoBlit: texture=%d xform=%d",
|
||||
texture, xform)
|
||||
}
|
||||
static jboolean clipDestCoords(
|
||||
VKRenderingContext* context,
|
||||
jdouble *dx1, jdouble *dy1, jdouble *dx2, jdouble *dy2,
|
||||
jint *sx1, jint *sy1, jint *sx2, jint *sy2,
|
||||
jint destW, jint destH) {
|
||||
// Trim destination rect by clip-rect (or dest.bounds)
|
||||
const jint sw = *sx2 - *sx1;
|
||||
const jint sh = *sy2 - *sy1;
|
||||
const jdouble dw = *dx2 - *dx1;
|
||||
const jdouble dh = *dy2 - *dy1;
|
||||
VkRect2D* clipRect = &context->clipRect;
|
||||
jdouble dcx1 = 0;
|
||||
jdouble dcx2 = destW;
|
||||
jdouble dcy1 = 0;
|
||||
jdouble dcy2 = destH;
|
||||
if (clipRect->offset.x > dcx1)
|
||||
dcx1 = clipRect->offset.x;
|
||||
const int maxX = clipRect->offset.x + clipRect->extent.width;
|
||||
if (dcx2 > maxX)
|
||||
dcx2 = maxX;
|
||||
if (clipRect->offset.y > dcy1)
|
||||
dcy1 = clipRect->offset.y;
|
||||
const int maxY = clipRect->offset.y + clipRect->extent.height;
|
||||
if (dcy2 > maxY)
|
||||
dcy2 = maxY;
|
||||
|
||||
if (dcx1 >= dcx2) {
|
||||
J2dTraceLn2(J2D_TRACE_ERROR, "\tclipDestCoords: dcx1=%1.2f, dcx2=%1.2f", dcx1, dcx2);
|
||||
dcx1 = dcx2;
|
||||
VKSDOps *srcOps = (VKSDOps *)jlong_to_ptr(pSrcOps);
|
||||
|
||||
if (context == NULL || srcOps == NULL) {
|
||||
J2dRlsTraceLn2(J2D_TRACE_ERROR, "VKBlitLoops_IsoBlit: context(%p) or srcOps(%p) is null",
|
||||
context, srcOps)
|
||||
return;
|
||||
}
|
||||
if (dcy1 >= dcy2) {
|
||||
J2dTraceLn2(J2D_TRACE_ERROR, "\tclipDestCoords: dcy1=%1.2f, dcy2=%1.2f", dcy1, dcy2);
|
||||
dcy1 = dcy2;
|
||||
|
||||
if (srcOps->image == NULL) {
|
||||
J2dRlsTraceLn(J2D_TRACE_WARNING, "VKBlitLoops_IsoBlit: srcOps->image is null");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!VKRenderer_Validate(context, SHADER_BLIT, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP)) {
|
||||
J2dTraceLn(J2D_TRACE_INFO, "VKBlitLoops_IsoBlit: VKRenderer_Validate cannot validate renderer");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: check if srctype is supported
|
||||
|
||||
SurfaceDataRasInfo srcInfo;
|
||||
jint sw = sx2 - sx1;
|
||||
jint sh = sy2 - sy1;
|
||||
jdouble dw = dx2 - dx1;
|
||||
jdouble dh = dy2 - dy1;
|
||||
|
||||
if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0) {
|
||||
J2dTraceLn(J2D_TRACE_WARNING,
|
||||
"VKBlitLoops_IsoBlit: invalid dimensions");
|
||||
return;
|
||||
}
|
||||
|
||||
srcInfo.bounds.x1 = sx1;
|
||||
srcInfo.bounds.y1 = sy1;
|
||||
srcInfo.bounds.x2 = sx2;
|
||||
srcInfo.bounds.y2 = sy2;
|
||||
|
||||
SurfaceData_IntersectBoundsXYXY(&srcInfo.bounds,
|
||||
0, 0,
|
||||
srcOps->image->extent.width,
|
||||
srcOps->image->extent.height);
|
||||
|
||||
if (srcInfo.bounds.x2 > srcInfo.bounds.x1 &&
|
||||
srcInfo.bounds.y2 > srcInfo.bounds.y1) {
|
||||
if (srcInfo.bounds.x1 != sx1) {
|
||||
dx1 += (srcInfo.bounds.x1 - sx1) * (dw / sw);
|
||||
sx1 = srcInfo.bounds.x1;
|
||||
}
|
||||
if (srcInfo.bounds.y1 != sy1) {
|
||||
dy1 += (srcInfo.bounds.y1 - sy1) * (dh / sh);
|
||||
sy1 = srcInfo.bounds.y1;
|
||||
}
|
||||
if (srcInfo.bounds.x2 != sx2) {
|
||||
dx2 += (srcInfo.bounds.x2 - sx2) * (dw / sw);
|
||||
sx2 = srcInfo.bounds.x2;
|
||||
}
|
||||
if (srcInfo.bounds.y2 != sy2) {
|
||||
dy2 += (srcInfo.bounds.y2 - sy2) * (dh / sh);
|
||||
sy2 = srcInfo.bounds.y2;
|
||||
}
|
||||
|
||||
if (sx2 > sx1 && sy2 > sy1) {
|
||||
VKBlitTextureToTexture(context, srcOps->image, context->surface->image,
|
||||
sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
|
||||
}
|
||||
}
|
||||
if (*dx2 <= dcx1 || *dx1 >= dcx2 || *dy2 <= dcy1 || *dy1 >= dcy2) {
|
||||
J2dTraceLn(J2D_TRACE_INFO, "\tclipDestCoords: dest rect doesn't intersect clip area");
|
||||
J2dTraceLn4(J2D_TRACE_INFO, "\tdx2=%1.4f <= dcx1=%1.4f || *dx1=%1.4f >= dcx2=%1.4f", *dx2, dcx1, *dx1, dcx2);
|
||||
J2dTraceLn4(J2D_TRACE_INFO, "\t*dy2=%1.4f <= dcy1=%1.4f || *dy1=%1.4f >= dcy2=%1.4f", *dy2, dcy1, *dy1, dcy2);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
if (*dx1 < dcx1) {
|
||||
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdx1=%1.2f, will be clipped to %1.2f | sx1+=%d", *dx1, dcx1, (jint)((dcx1 - *dx1) * (sw/dw)));
|
||||
*sx1 += (jint)((dcx1 - *dx1) * (sw/dw));
|
||||
*dx1 = dcx1;
|
||||
}
|
||||
if (*dx2 > dcx2) {
|
||||
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdx2=%1.2f, will be clipped to %1.2f | sx2-=%d", *dx2, dcx2, (jint)((*dx2 - dcx2) * (sw/dw)));
|
||||
*sx2 -= (jint)((*dx2 - dcx2) * (sw/dw));
|
||||
*dx2 = dcx2;
|
||||
}
|
||||
if (*dy1 < dcy1) {
|
||||
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdy1=%1.2f, will be clipped to %1.2f | sy1+=%d", *dy1, dcy1, (jint)((dcy1 - *dy1) * (sh/dh)));
|
||||
*sy1 += (jint)((dcy1 - *dy1) * (sh/dh));
|
||||
*dy1 = dcy1;
|
||||
}
|
||||
if (*dy2 > dcy2) {
|
||||
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdy2=%1.2f, will be clipped to %1.2f | sy2-=%d", *dy2, dcy2, (jint)((*dy2 - dcy2) * (sh/dh)));
|
||||
*sy2 -= (jint)((*dy2 - dcy2) * (sh/dh));
|
||||
*dy2 = dcy2;
|
||||
}
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void VKBlitLoops_Blit(JNIEnv *env,
|
||||
VKRenderingContext* context, jlong pSrcOps,
|
||||
jboolean xform, jint hint,
|
||||
@@ -240,7 +368,7 @@ void VKBlitLoops_Blit(JNIEnv *env,
|
||||
}
|
||||
|
||||
|
||||
if (!VKRenderer_Validate(context, PIPELINE_BLIT)) {
|
||||
if (!VKRenderer_Validate(context, SHADER_BLIT, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP)) {
|
||||
J2dTraceLn(J2D_TRACE_INFO, "VKBlitLoops_Blit: VKRenderer_Validate cannot validate renderer");
|
||||
return;
|
||||
}
|
||||
@@ -333,8 +461,8 @@ VKBlitLoops_SurfaceToSwBlit(JNIEnv *env, VKRenderingContext* context,
|
||||
SurfaceDataOps *dstOps = (SurfaceDataOps *)jlong_to_ptr(pDstOps);
|
||||
SurfaceDataRasInfo srcInfo, dstInfo;
|
||||
|
||||
J2dTraceLn6(J2D_TRACE_INFO, "VKBlitLoops_SurfaceToSwBlit: (%d %d %d %d) -> (%d %d)",
|
||||
srcx, srcy, width, height, dstx, dsty);
|
||||
J2dTraceLn8(J2D_TRACE_INFO, "VKBlitLoops_SurfaceToSwBlit: (%p) (%d %d %d %d) -> (%p) (%d %d)",
|
||||
srcOps, srcx, srcy, width, height, dstOps, dstx, dsty);
|
||||
|
||||
if (width <= 0 || height <= 0) {
|
||||
J2dTraceLn(J2D_TRACE_WARNING,
|
||||
|
||||
@@ -106,6 +106,77 @@ VKMemory VKBuffer_CreateBuffers(VKDevice* device, VkBufferUsageFlags usageFlags,
|
||||
return page;
|
||||
}
|
||||
|
||||
static VkDescriptorPool VKBuffer_DestroyTexelBuffersOnFailure(VKDevice* device, VkDescriptorPool pool, uint32_t bufferCount, VKTexelBuffer* texelBuffers) {
|
||||
assert(device != NULL);
|
||||
for (uint32_t i = 0; i < bufferCount; i++) {
|
||||
device->vkDestroyBufferView(device->handle, texelBuffers[i].view, NULL);
|
||||
}
|
||||
device->vkDestroyDescriptorPool(device->handle, pool, NULL);
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VkDescriptorPool VKBuffer_CreateTexelBuffers(VKDevice* device, VkFormat format,
|
||||
VkDescriptorType descriptorType, VkDescriptorSetLayout descriptorSetLayout,
|
||||
uint32_t bufferCount, VKBuffer* buffers, VKTexelBuffer* texelBuffers) {
|
||||
assert(device != NULL);
|
||||
|
||||
// Create descriptor pool.
|
||||
VkDescriptorPoolSize poolSize = { .type = descriptorType, .descriptorCount = bufferCount };
|
||||
VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
||||
.flags = 0,
|
||||
.maxSets = bufferCount,
|
||||
.poolSizeCount = 1,
|
||||
.pPoolSizes = &poolSize
|
||||
};
|
||||
VkDescriptorPool pool;
|
||||
VK_IF_ERROR(device->vkCreateDescriptorPool(device->handle, &descriptorPoolCreateInfo, NULL, &pool)) return VK_NULL_HANDLE;
|
||||
|
||||
// Allocate descriptor sets.
|
||||
VkDescriptorSetLayout layouts[bufferCount];
|
||||
for (uint32_t i = 0; i < bufferCount; i++) layouts[i] = descriptorSetLayout;
|
||||
VkDescriptorSetAllocateInfo allocateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
||||
.descriptorPool = pool,
|
||||
.descriptorSetCount = bufferCount,
|
||||
.pSetLayouts = layouts
|
||||
};
|
||||
VkDescriptorSet descriptorSets[bufferCount];
|
||||
VK_IF_ERROR(device->vkAllocateDescriptorSets(device->handle, &allocateInfo, descriptorSets)) {
|
||||
return VKBuffer_DestroyTexelBuffersOnFailure(device, pool, 0, texelBuffers);
|
||||
}
|
||||
|
||||
// Create buffer views and record them into descriptors.
|
||||
VkBufferViewCreateInfo bufferViewCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO,
|
||||
.format = format,
|
||||
.offset = 0,
|
||||
.range = VK_WHOLE_SIZE
|
||||
};
|
||||
VkWriteDescriptorSet writeDescriptorSets[bufferCount];
|
||||
for (uint32_t i = 0; i < bufferCount; i++) {
|
||||
texelBuffers[i] = (VKTexelBuffer) {
|
||||
.buffer = buffers[i],
|
||||
.descriptorSet = descriptorSets[i]
|
||||
};
|
||||
bufferViewCreateInfo.buffer = buffers[i].handle;
|
||||
VK_IF_ERROR(device->vkCreateBufferView(device->handle, &bufferViewCreateInfo, NULL, &texelBuffers[i].view)) {
|
||||
return VKBuffer_DestroyTexelBuffersOnFailure(device, pool, i, texelBuffers);
|
||||
}
|
||||
writeDescriptorSets[i] = (VkWriteDescriptorSet) {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.dstSet = descriptorSets[i],
|
||||
.dstBinding = 0,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = descriptorType,
|
||||
.pTexelBufferView = &texelBuffers[i].view
|
||||
};
|
||||
}
|
||||
device->vkUpdateDescriptorSets(device->handle, bufferCount, writeDescriptorSets, 0, NULL);
|
||||
return pool;
|
||||
}
|
||||
|
||||
VKBuffer* VKBuffer_Create(VKDevice* device, VkDeviceSize size,
|
||||
VkBufferUsageFlags usage, VkMemoryPropertyFlags properties)
|
||||
{
|
||||
|
||||
@@ -42,6 +42,12 @@ struct VKBuffer {
|
||||
void* data;
|
||||
};
|
||||
|
||||
struct VKTexelBuffer {
|
||||
VKBuffer buffer;
|
||||
VkBufferView view;
|
||||
VkDescriptorSet descriptorSet;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create buffers, allocate a memory page and bind them together.
|
||||
* 'pageSize' can be 0, meaning that page size is calculated based on buffer memory requirements.
|
||||
@@ -54,6 +60,16 @@ VKMemory VKBuffer_CreateBuffers(VKDevice* device, VkBufferUsageFlags usageFlags,
|
||||
VkDeviceSize bufferSize, VkDeviceSize pageSize,
|
||||
uint32_t* bufferCount, VKBuffer* buffers);
|
||||
|
||||
/**
|
||||
* Create texel buffers from existing array of buffers.
|
||||
* It returns created descriptor pool, or VK_NULL_HANDLE on failure.
|
||||
* Created texel buffers are written in 'texelBuffers',
|
||||
* original buffers are taken from 'buffers'.
|
||||
*/
|
||||
VkDescriptorPool VKBuffer_CreateTexelBuffers(VKDevice* device, VkFormat format,
|
||||
VkDescriptorType descriptorType, VkDescriptorSetLayout descriptorSetLayout,
|
||||
uint32_t bufferCount, VKBuffer* buffers, VKTexelBuffer* texelBuffers);
|
||||
|
||||
// TODO usage of this function is suboptimal, we need to avoid creating individual buffers.
|
||||
VKBuffer* VKBuffer_Create(VKDevice* device, VkDeviceSize size,
|
||||
VkBufferUsageFlags usage, VkMemoryPropertyFlags properties);
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
// Copyright 2025 JetBrains s.r.o.
|
||||
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
//
|
||||
// This code is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License version 2 only, as
|
||||
// published by the Free Software Foundation. Oracle designates this
|
||||
// particular file as subject to the "Classpath" exception as provided
|
||||
// by Oracle in the LICENSE file that accompanied this code.
|
||||
//
|
||||
// This code 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
|
||||
// version 2 for more details (a copy is included in the LICENSE file that
|
||||
// accompanied this code).
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License version
|
||||
// 2 along with this work; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
// or visit www.oracle.com if you need additional information or have any
|
||||
// questions.
|
||||
|
||||
#include "VKComposites.h"
|
||||
|
||||
#define ALPHA_BLEND(NAME, SRC_COLOR, DST_COLOR, SRC_ALPHA, DST_ALPHA) \
|
||||
VKComposites_AddState(&map, ALPHA_COMPOSITE_ ## NAME, (VKCompositeState) \
|
||||
{{ .blendEnable = VK_TRUE, \
|
||||
.srcColorBlendFactor = VK_BLEND_FACTOR_ ## SRC_COLOR, \
|
||||
.dstColorBlendFactor = VK_BLEND_FACTOR_ ## DST_COLOR, \
|
||||
.colorBlendOp = VK_BLEND_OP_ADD, \
|
||||
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ ## SRC_ALPHA, \
|
||||
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ ## DST_ALPHA, \
|
||||
.alphaBlendOp = VK_BLEND_OP_ADD, \
|
||||
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | \
|
||||
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT }, \
|
||||
{ .logicOpEnable = VK_FALSE }})
|
||||
|
||||
static size_t hash(const void* ptr) {
|
||||
return (size_t) *(VKCompositeMode*)ptr;
|
||||
}
|
||||
|
||||
static bool equals(const void* ap, const void* bp) {
|
||||
return *(VKCompositeMode*)ap == *(VKCompositeMode*)bp;
|
||||
}
|
||||
|
||||
VKComposites VKComposites_Create() {
|
||||
VKComposites map = NULL;
|
||||
HASH_MAP_REHASH(map, linear_probing, &equals, &hash, ALPHA_COMPOSITE_GROUP + 2, 10, 0.75);
|
||||
|
||||
VKComposites_AddState(&map, LOGIC_COMPOSITE_XOR, (VKCompositeState) {
|
||||
{ .blendEnable = VK_FALSE,
|
||||
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
|
||||
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT },
|
||||
{ .logicOpEnable = VK_TRUE,
|
||||
.logicOp = VK_LOGIC_OP_XOR }});
|
||||
|
||||
// NAME | SRC_COLOR | DST_COLOR | SRC_ALPHA | DST_ALPHA ||
|
||||
ALPHA_BLEND( CLEAR , ZERO , ZERO , ZERO , ZERO );
|
||||
ALPHA_BLEND( SRC , ONE , ZERO , ONE , ZERO );
|
||||
ALPHA_BLEND( SRC_OVER , ONE , ONE_MINUS_SRC_ALPHA , ONE , ONE_MINUS_SRC_ALPHA );
|
||||
ALPHA_BLEND( DST_OVER , ONE_MINUS_DST_ALPHA , ONE , ONE_MINUS_DST_ALPHA , ONE );
|
||||
ALPHA_BLEND( SRC_IN , DST_ALPHA , ZERO , DST_ALPHA , ZERO );
|
||||
ALPHA_BLEND( DST_IN , ZERO , SRC_ALPHA , ZERO , SRC_ALPHA );
|
||||
ALPHA_BLEND( SRC_OUT , ONE_MINUS_DST_ALPHA , ZERO , ONE_MINUS_DST_ALPHA , ZERO );
|
||||
ALPHA_BLEND( DST_OUT , ZERO , ONE_MINUS_SRC_ALPHA , ZERO , ONE_MINUS_SRC_ALPHA );
|
||||
ALPHA_BLEND( DST , ZERO , ONE , ZERO , ONE );
|
||||
ALPHA_BLEND( SRC_ATOP , DST_ALPHA , ONE_MINUS_SRC_ALPHA , ZERO , ONE );
|
||||
ALPHA_BLEND( DST_ATOP , ONE_MINUS_DST_ALPHA , SRC_ALPHA , ONE , ZERO );
|
||||
ALPHA_BLEND( XOR , ONE_MINUS_DST_ALPHA , ONE_MINUS_SRC_ALPHA , ONE_MINUS_DST_ALPHA , ONE_MINUS_SRC_ALPHA );
|
||||
|
||||
VKComposites_AddState(&map, NO_COMPOSITE, (VKCompositeState) {
|
||||
{ .blendEnable = VK_FALSE,
|
||||
.colorWriteMask = 0 }, // For stencil-only operations.
|
||||
{ .logicOpEnable = VK_FALSE }});
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void VKComposites_Destroy(VKComposites composites) {
|
||||
MAP_FREE(composites);
|
||||
}
|
||||
|
||||
void VKComposites_AddState(VKComposites* composites, VKCompositeMode mode, VKCompositeState state) {
|
||||
state.blendState.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||
state.blendState.pNext = NULL;
|
||||
state.blendState.attachmentCount = 1;
|
||||
MAP_AT(*composites, mode) = state;
|
||||
}
|
||||
|
||||
const VKCompositeState* VKComposites_GetState(VKComposites* composites, VKCompositeMode mode) {
|
||||
VKCompositeState* state = MAP_FIND(*composites, mode);
|
||||
state->blendState.pAttachments = &state->attachmentState;
|
||||
return state;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright 2025 JetBrains s.r.o.
|
||||
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
//
|
||||
// This code is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License version 2 only, as
|
||||
// published by the Free Software Foundation. Oracle designates this
|
||||
// particular file as subject to the "Classpath" exception as provided
|
||||
// by Oracle in the LICENSE file that accompanied this code.
|
||||
//
|
||||
// This code 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
|
||||
// version 2 for more details (a copy is included in the LICENSE file that
|
||||
// accompanied this code).
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License version
|
||||
// 2 along with this work; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
// or visit www.oracle.com if you need additional information or have any
|
||||
// questions.
|
||||
|
||||
#ifndef VKComposites_h_Included
|
||||
#define VKComposites_h_Included
|
||||
|
||||
#include "java_awt_AlphaComposite.h"
|
||||
#include "VKUtil.h"
|
||||
|
||||
/**
|
||||
* There are two groups of composite modes:
|
||||
* - Logic composite - using logicOp.
|
||||
* - Alpha compisite - using blending.
|
||||
*/
|
||||
typedef enum {
|
||||
LOGIC_COMPOSITE_XOR = 0,
|
||||
LOGIC_COMPOSITE_GROUP = LOGIC_COMPOSITE_XOR,
|
||||
ALPHA_COMPOSITE_CLEAR = java_awt_AlphaComposite_CLEAR,
|
||||
ALPHA_COMPOSITE_SRC = java_awt_AlphaComposite_SRC,
|
||||
ALPHA_COMPOSITE_DST = java_awt_AlphaComposite_DST,
|
||||
ALPHA_COMPOSITE_SRC_OVER = java_awt_AlphaComposite_SRC_OVER,
|
||||
ALPHA_COMPOSITE_DST_OVER = java_awt_AlphaComposite_DST_OVER,
|
||||
ALPHA_COMPOSITE_SRC_IN = java_awt_AlphaComposite_SRC_IN,
|
||||
ALPHA_COMPOSITE_DST_IN = java_awt_AlphaComposite_DST_IN,
|
||||
ALPHA_COMPOSITE_SRC_OUT = java_awt_AlphaComposite_SRC_OUT,
|
||||
ALPHA_COMPOSITE_DST_OUT = java_awt_AlphaComposite_DST_OUT,
|
||||
ALPHA_COMPOSITE_SRC_ATOP = java_awt_AlphaComposite_SRC_ATOP,
|
||||
ALPHA_COMPOSITE_DST_ATOP = java_awt_AlphaComposite_DST_ATOP,
|
||||
ALPHA_COMPOSITE_XOR = java_awt_AlphaComposite_XOR,
|
||||
ALPHA_COMPOSITE_GROUP = ALPHA_COMPOSITE_XOR,
|
||||
NO_COMPOSITE = 0x7FFFFFFF
|
||||
} VKCompositeMode;
|
||||
#define COMPOSITE_GROUP(COMPOSITE) ( \
|
||||
(COMPOSITE) <= LOGIC_COMPOSITE_GROUP ? LOGIC_COMPOSITE_GROUP : \
|
||||
(COMPOSITE) <= ALPHA_COMPOSITE_GROUP ? ALPHA_COMPOSITE_GROUP : \
|
||||
NO_COMPOSITE )
|
||||
|
||||
typedef struct {
|
||||
VkPipelineColorBlendAttachmentState attachmentState;
|
||||
VkPipelineColorBlendStateCreateInfo blendState;
|
||||
} VKCompositeState;
|
||||
|
||||
typedef MAP(VKCompositeMode, VKCompositeState) VKComposites;
|
||||
|
||||
VKComposites VKComposites_Create();
|
||||
void VKComposites_Destroy(VKComposites composites);
|
||||
void VKComposites_AddState(VKComposites* composites, VKCompositeMode mode, VKCompositeState state);
|
||||
const VKCompositeState* VKComposites_GetState(VKComposites* composites, VKCompositeMode mode);
|
||||
|
||||
#endif //VKComposites_h_Included
|
||||
@@ -40,7 +40,7 @@ static VkBool32 VKImage_CreateView(VKDevice* device, VKImage* image) {
|
||||
.image = image->handle,
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = image->format,
|
||||
.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.subresourceRange.aspectMask = VKImage_GetAspect(image),
|
||||
.subresourceRange.baseMipLevel = 0,
|
||||
.subresourceRange.levelCount = 1,
|
||||
.subresourceRange.baseArrayLayer = 0,
|
||||
@@ -53,6 +53,11 @@ static VkBool32 VKImage_CreateView(VKDevice* device, VKImage* image) {
|
||||
return VK_TRUE;
|
||||
}
|
||||
|
||||
VkImageAspectFlagBits VKImage_GetAspect(VKImage* image) {
|
||||
return VKUtil_GetFormatGroup(image->format).bytes == 0 ? // Unknown format group means stencil.
|
||||
VK_IMAGE_ASPECT_STENCIL_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
}
|
||||
|
||||
VKImage* VKImage_Create(VKDevice* device, uint32_t width, uint32_t height,
|
||||
VkImageCreateFlags flags, VkFormat format,
|
||||
VkImageTiling tiling, VkImageUsageFlags usage, VkSampleCountFlagBits samples,
|
||||
|
||||
@@ -42,6 +42,8 @@ struct VKImage {
|
||||
VkAccessFlagBits lastAccess;
|
||||
};
|
||||
|
||||
VkImageAspectFlagBits VKImage_GetAspect(VKImage* image);
|
||||
|
||||
VKImage* VKImage_Create(VKDevice* device, uint32_t width, uint32_t height,
|
||||
VkImageCreateFlags flags, VkFormat format,
|
||||
VkImageTiling tiling, VkImageUsageFlags usage, VkSampleCountFlagBits samples,
|
||||
|
||||
@@ -33,9 +33,29 @@
|
||||
#undef SHADER_ENTRY
|
||||
#undef BYTECODE_END
|
||||
|
||||
typedef struct VKPipelineSet {
|
||||
VkPipeline pipelines[PIPELINE_COUNT];
|
||||
} VKPipelineSet;
|
||||
inline void hash(uint32_t* result, int i) { // Good for hashing enums.
|
||||
uint32_t x = (uint32_t) i;
|
||||
x = ((x >> 16U) ^ x) * 0x45d9f3bU;
|
||||
x = ((x >> 16U) ^ x) * 0x45d9f3bU;
|
||||
x = (x >> 16U) ^ x;
|
||||
*result ^= x + 0x9e3779b9U + (*result << 6U) + (*result >> 2U);
|
||||
}
|
||||
static size_t pipelineDescriptorHash(const void* ptr) {
|
||||
const VKPipelineDescriptor* d = ptr;
|
||||
uint32_t h = 0U;
|
||||
hash(&h, d->stencilMode);
|
||||
hash(&h, d->composite);
|
||||
hash(&h, d->shader);
|
||||
hash(&h, d->topology);
|
||||
return (size_t) h;
|
||||
}
|
||||
static bool pipelineDescriptorEquals(const void* ap, const void* bp) {
|
||||
const VKPipelineDescriptor *a = ap, *b = bp;
|
||||
return a->stencilMode == b->stencilMode &&
|
||||
a->composite == b->composite &&
|
||||
a->shader == b->shader &&
|
||||
a->topology == b->topology;
|
||||
}
|
||||
|
||||
typedef struct VKShaders {
|
||||
# define SHADER_ENTRY(NAME, TYPE) VkPipelineShaderStageCreateInfo NAME ## _ ## TYPE;
|
||||
@@ -79,218 +99,183 @@ static VKShaders* VKPipelines_CreateShaders(VKDevice* device) {
|
||||
return shaders;
|
||||
}
|
||||
|
||||
#define MAKE_INPUT_STATE(NAME, TYPE, ...) \
|
||||
static const VkVertexInputAttributeDescription INPUT_STATE_ATTRIBUTES_##NAME[] = { __VA_ARGS__ }; \
|
||||
static const VkVertexInputBindingDescription INPUT_STATE_BINDING_##NAME = { \
|
||||
.binding = 0, \
|
||||
.stride = sizeof(TYPE), \
|
||||
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX \
|
||||
}; \
|
||||
static const VkPipelineVertexInputStateCreateInfo INPUT_STATE_##NAME = { \
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, \
|
||||
.vertexBindingDescriptionCount = 1, \
|
||||
.pVertexBindingDescriptions = &INPUT_STATE_BINDING_##NAME, \
|
||||
.vertexAttributeDescriptionCount = SARRAY_COUNT_OF(INPUT_STATE_ATTRIBUTES_##NAME), \
|
||||
.pVertexAttributeDescriptions = INPUT_STATE_ATTRIBUTES_##NAME \
|
||||
}
|
||||
#define MAKE_INPUT_STATE(NAME, TYPE, ...) \
|
||||
const VkFormat INPUT_STATE_ATTRIBUTE_FORMATS_##NAME[] = { __VA_ARGS__ }; \
|
||||
VkVertexInputAttributeDescription \
|
||||
INPUT_STATE_ATTRIBUTES_##NAME[SARRAY_COUNT_OF(INPUT_STATE_ATTRIBUTE_FORMATS_##NAME)]; \
|
||||
const VkVertexInputBindingDescription INPUT_STATE_BINDING_##NAME = { \
|
||||
.binding = 0, \
|
||||
.stride = sizeof(TYPE), \
|
||||
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX \
|
||||
}; \
|
||||
const VkPipelineVertexInputStateCreateInfo INPUT_STATE_##NAME = { \
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, \
|
||||
.vertexBindingDescriptionCount = 1, \
|
||||
.pVertexBindingDescriptions = &INPUT_STATE_BINDING_##NAME, \
|
||||
.vertexAttributeDescriptionCount = SARRAY_COUNT_OF(INPUT_STATE_ATTRIBUTES_##NAME), \
|
||||
.pVertexAttributeDescriptions = INPUT_STATE_ATTRIBUTES_##NAME \
|
||||
}; \
|
||||
uint32_t INPUT_STATE_BINDING_SIZE_##NAME = 0; \
|
||||
for (uint32_t i = 0; i < SARRAY_COUNT_OF(INPUT_STATE_ATTRIBUTES_##NAME); i++) { \
|
||||
INPUT_STATE_ATTRIBUTES_##NAME[i] = (VkVertexInputAttributeDescription) { \
|
||||
i, 0, INPUT_STATE_ATTRIBUTE_FORMATS_##NAME[i], INPUT_STATE_BINDING_SIZE_##NAME}; \
|
||||
INPUT_STATE_BINDING_SIZE_##NAME += \
|
||||
VKUtil_GetFormatGroup(INPUT_STATE_ATTRIBUTE_FORMATS_##NAME[i]).bytes; \
|
||||
} if (sizeof(TYPE) != INPUT_STATE_BINDING_SIZE_##NAME) VK_FATAL_ERROR("Vertex size mismatch for input state " #NAME)
|
||||
|
||||
typedef struct {
|
||||
VkGraphicsPipelineCreateInfo createInfo;
|
||||
VkPipelineMultisampleStateCreateInfo multisampleState;
|
||||
VkPipelineColorBlendStateCreateInfo colorBlendState;
|
||||
VkPipelineDynamicStateCreateInfo dynamicState;
|
||||
VkDynamicState dynamicStates[2];
|
||||
} PipelineCreateState;
|
||||
static VkPipeline VKPipelines_CreatePipelines(VKRenderPassContext* renderPassContext, uint32_t count,
|
||||
const VKPipelineDescriptor* descriptors) {
|
||||
assert(renderPassContext != NULL && renderPassContext->pipelineContext != NULL);
|
||||
assert(count > 0 && descriptors != NULL);
|
||||
VKPipelineContext* pipelineContext = renderPassContext->pipelineContext;
|
||||
VKDevice* device = pipelineContext->device;
|
||||
VKShaders* shaders = pipelineContext->shaders;
|
||||
VKComposites* composites = &VKGE_graphics_environment()->composites;
|
||||
|
||||
typedef struct {
|
||||
VkPipelineShaderStageCreateInfo createInfos[2]; // vert + frag
|
||||
} ShaderStages;
|
||||
|
||||
/**
|
||||
* Init default pipeline state. Some members are left uninitialized:
|
||||
* - pStages (but stageCount is set to 2)
|
||||
* - pVertexInputState
|
||||
* - pInputAssemblyState
|
||||
* - colorBlendState.pAttachments (but attachmentCount is set to 1)
|
||||
* - createInfo.layout
|
||||
* - createInfo.renderPass
|
||||
* - renderingCreateInfo.pColorAttachmentFormats (but colorAttachmentCount is set to 1)
|
||||
*/
|
||||
static void VKPipelines_InitPipelineCreateState(PipelineCreateState* state) {
|
||||
static const VkViewport viewport = {};
|
||||
static const VkRect2D scissor = {};
|
||||
static const VkPipelineViewportStateCreateInfo viewportState = {
|
||||
// Setup pipeline creation structs.
|
||||
static const uint32_t MAX_DYNAMIC_STATES = 2;
|
||||
typedef struct {
|
||||
VkPipelineShaderStageCreateInfo createInfos[2]; // vert + frag
|
||||
} ShaderStages;
|
||||
ShaderStages stages[count];
|
||||
VkPipelineInputAssemblyStateCreateInfo inputAssemblyStates[count];
|
||||
VkPipelineDepthStencilStateCreateInfo depthStencilStates[count];
|
||||
VkPipelineDynamicStateCreateInfo dynamicStates[count];
|
||||
VkDynamicState dynamicStateValues[count][MAX_DYNAMIC_STATES];
|
||||
VkGraphicsPipelineCreateInfo createInfos[count];
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
// Init default pipeline state. Some members are left uninitialized:
|
||||
// - pStages (but stageCount is set to 2)
|
||||
// - pVertexInputState
|
||||
// - createInfo.layout
|
||||
inputAssemblyStates[i] = (VkPipelineInputAssemblyStateCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||||
.topology = descriptors[i].topology
|
||||
};
|
||||
static const VkViewport viewport = {};
|
||||
static const VkRect2D scissor = {};
|
||||
static const VkPipelineViewportStateCreateInfo viewportState = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.viewportCount = 1,
|
||||
.pViewports = &viewport,
|
||||
.scissorCount = 1,
|
||||
.pScissors = &scissor
|
||||
};
|
||||
static const VkPipelineRasterizationStateCreateInfo rasterizationState = {
|
||||
};
|
||||
static const VkPipelineRasterizationStateCreateInfo rasterizationState = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.polygonMode = VK_POLYGON_MODE_FILL,
|
||||
.cullMode = VK_CULL_MODE_NONE,
|
||||
.lineWidth = 1.0f
|
||||
};
|
||||
state->multisampleState = (VkPipelineMultisampleStateCreateInfo) {
|
||||
};
|
||||
static const VkPipelineMultisampleStateCreateInfo multisampleState = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT
|
||||
};
|
||||
state->colorBlendState = (VkPipelineColorBlendStateCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||
.logicOpEnable = VK_FALSE,
|
||||
.logicOp = VK_LOGIC_OP_XOR,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = NULL,
|
||||
};
|
||||
state->dynamicState = (VkPipelineDynamicStateCreateInfo) {
|
||||
};
|
||||
static const VkStencilOpState stencilOpState = {
|
||||
.failOp = VK_STENCIL_OP_KEEP,
|
||||
.passOp = VK_STENCIL_OP_KEEP,
|
||||
.compareOp = VK_COMPARE_OP_NOT_EQUAL,
|
||||
.compareMask = 0xFFFFFFFFU,
|
||||
.writeMask = 0U,
|
||||
.reference = CLIP_STENCIL_EXCLUDE_VALUE
|
||||
};
|
||||
depthStencilStates[i] = (VkPipelineDepthStencilStateCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
||||
.stencilTestEnable = descriptors[i].stencilMode == STENCIL_MODE_ON,
|
||||
.front = stencilOpState,
|
||||
.back = stencilOpState
|
||||
};
|
||||
dynamicStates[i] = (VkPipelineDynamicStateCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
.dynamicStateCount = 2,
|
||||
.pDynamicStates = state->dynamicStates
|
||||
};
|
||||
state->dynamicStates[0] = VK_DYNAMIC_STATE_VIEWPORT;
|
||||
state->dynamicStates[1] = VK_DYNAMIC_STATE_SCISSOR;
|
||||
state->createInfo = (VkGraphicsPipelineCreateInfo) {
|
||||
.pDynamicStates = dynamicStateValues[i]
|
||||
};
|
||||
dynamicStateValues[i][0] = VK_DYNAMIC_STATE_VIEWPORT;
|
||||
dynamicStateValues[i][1] = VK_DYNAMIC_STATE_SCISSOR;
|
||||
createInfos[i] = (VkGraphicsPipelineCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.stageCount = 2,
|
||||
.pStages = stages[i].createInfos,
|
||||
.pInputAssemblyState = &inputAssemblyStates[i],
|
||||
.pViewportState = &viewportState,
|
||||
.pRasterizationState = &rasterizationState,
|
||||
.pMultisampleState = &state->multisampleState,
|
||||
.pColorBlendState = &state->colorBlendState,
|
||||
.pDynamicState = &state->dynamicState,
|
||||
.pMultisampleState = &multisampleState,
|
||||
.pDepthStencilState = &depthStencilStates[i],
|
||||
.pColorBlendState = &VKComposites_GetState(composites, descriptors[i].composite)->blendState,
|
||||
.pDynamicState = &dynamicStates[i],
|
||||
.renderPass = renderPassContext->renderPass[descriptors[i].stencilMode != STENCIL_MODE_NONE],
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = -1
|
||||
};
|
||||
}
|
||||
|
||||
static const VkPipelineInputAssemblyStateCreateInfo INPUT_ASSEMBLY_STATE_TRIANGLE_STRIP = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||||
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP
|
||||
};
|
||||
static const VkPipelineInputAssemblyStateCreateInfo INPUT_ASSEMBLY_STATE_TRIANGLE_LIST = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||||
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
|
||||
};
|
||||
static const VkPipelineInputAssemblyStateCreateInfo INPUT_ASSEMBLY_STATE_LINE_LIST = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||||
.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST
|
||||
};
|
||||
|
||||
// Blend states are hard-coded, but can also be loaded dynamically to implement custom composites.
|
||||
#define DEF_BLEND(NAME, SRC_COLOR, DST_COLOR, SRC_ALPHA, DST_ALPHA) \
|
||||
{ .blendEnable = VK_TRUE, \
|
||||
.srcColorBlendFactor = VK_BLEND_FACTOR_ ## SRC_COLOR, \
|
||||
.dstColorBlendFactor = VK_BLEND_FACTOR_ ## DST_COLOR, \
|
||||
.colorBlendOp = VK_BLEND_OP_ADD, \
|
||||
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ ## SRC_ALPHA, \
|
||||
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ ## DST_ALPHA, \
|
||||
.alphaBlendOp = VK_BLEND_OP_ADD, \
|
||||
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | \
|
||||
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT }
|
||||
|
||||
|
||||
const VkPipelineColorBlendAttachmentState COMPOSITE_BLEND_STATES[COMPOSITE_COUNT] = {
|
||||
{ .blendEnable = VK_FALSE, // LOGIC_XOR
|
||||
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
|
||||
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT },
|
||||
// NAME || SRC_COLOR | DST_COLOR | SRC_ALPHA | DST_ALPHA ||
|
||||
DEF_BLEND(| CLEAR |, ZERO , ZERO , ZERO , ZERO ),
|
||||
DEF_BLEND(| SRC |, ONE , ZERO , ONE , ZERO ),
|
||||
DEF_BLEND(| SRC_OVER |, ONE , ONE_MINUS_SRC_ALPHA , ONE , ONE_MINUS_SRC_ALPHA ),
|
||||
DEF_BLEND(| DST_OVER |, ONE_MINUS_DST_ALPHA , ONE , ONE_MINUS_DST_ALPHA , ONE ),
|
||||
DEF_BLEND(| SRC_IN |, DST_ALPHA , ZERO , DST_ALPHA , ZERO ),
|
||||
DEF_BLEND(| DST_IN |, ZERO , SRC_ALPHA , ZERO , SRC_ALPHA ),
|
||||
DEF_BLEND(| SRC_OUT |, ONE_MINUS_DST_ALPHA , ZERO , ONE_MINUS_DST_ALPHA , ZERO ),
|
||||
DEF_BLEND(| DST_OUT |, ZERO , ONE_MINUS_SRC_ALPHA , ZERO , ONE_MINUS_SRC_ALPHA ),
|
||||
DEF_BLEND(| DST |, ZERO , ONE , ZERO , ONE ),
|
||||
DEF_BLEND(| SRC_ATOP |, DST_ALPHA , ONE_MINUS_SRC_ALPHA , ZERO , ONE ),
|
||||
DEF_BLEND(| DST_ATOP |, ONE_MINUS_DST_ALPHA , SRC_ALPHA , ONE , ZERO ),
|
||||
DEF_BLEND(| XOR |, ONE_MINUS_DST_ALPHA , ONE_MINUS_SRC_ALPHA , ONE_MINUS_DST_ALPHA , ONE_MINUS_SRC_ALPHA ),
|
||||
};
|
||||
|
||||
static const VkVertexInputAttributeDescription INPUT_ATTRIBUTE_POSITION = {
|
||||
.location = 0,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32_SFLOAT,
|
||||
.offset = 0
|
||||
};
|
||||
static const VkVertexInputAttributeDescription INPUT_ATTRIBUTE_COLOR = {
|
||||
.location = 1,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
.offset = sizeof(float) * 2
|
||||
};
|
||||
static const VkVertexInputAttributeDescription INPUT_ATTRIBUTE_TEXCOORD = {
|
||||
.location = 1,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32_SFLOAT,
|
||||
.offset = sizeof(float) * 2
|
||||
};
|
||||
|
||||
MAKE_INPUT_STATE(COLOR_VERTEX, VKColorVertex, INPUT_ATTRIBUTE_POSITION, INPUT_ATTRIBUTE_COLOR);
|
||||
MAKE_INPUT_STATE(TEXCOORD_VERTEX, VKTxVertex, INPUT_ATTRIBUTE_POSITION, INPUT_ATTRIBUTE_TEXCOORD);
|
||||
|
||||
static void VKPipelines_DestroyPipelineSet(VKDevice* device, VKPipelineSet* set) {
|
||||
assert(device != NULL);
|
||||
if (set == NULL) return;
|
||||
for (uint32_t i = 0; i < PIPELINE_COUNT; i++) {
|
||||
device->vkDestroyPipeline(device->handle, set->pipelines[i], NULL);
|
||||
}
|
||||
free(set);
|
||||
}
|
||||
|
||||
static VKPipelineSet* VKPipelines_CreatePipelineSet(VKRenderPassContext* renderPassContext, VKCompositeMode composite) {
|
||||
assert(renderPassContext != NULL && renderPassContext->pipelineContext != NULL);
|
||||
assert(composite < COMPOSITE_COUNT);
|
||||
VKPipelineContext* pipelineContext = renderPassContext->pipelineContext;
|
||||
|
||||
VKPipelineSet* set = calloc(1, sizeof(VKPipelineSet));
|
||||
VK_RUNTIME_ASSERT(set);
|
||||
VKDevice* device = pipelineContext->device;
|
||||
VKShaders* shaders = pipelineContext->shaders;
|
||||
|
||||
// Setup default pipeline parameters.
|
||||
PipelineCreateState base;
|
||||
VKPipelines_InitPipelineCreateState(&base);
|
||||
base.createInfo.layout = pipelineContext->pipelineLayout;
|
||||
base.createInfo.renderPass = renderPassContext->renderPass;
|
||||
base.colorBlendState.pAttachments = &COMPOSITE_BLEND_STATES[composite];
|
||||
if (COMPOSITE_GROUP(composite) == LOGIC_COMPOSITE_GROUP) base.colorBlendState.logicOpEnable = VK_TRUE;
|
||||
assert(base.dynamicState.dynamicStateCount <= SARRAY_COUNT_OF(base.dynamicStates));
|
||||
|
||||
ShaderStages stages[PIPELINE_COUNT];
|
||||
VkGraphicsPipelineCreateInfo createInfos[PIPELINE_COUNT];
|
||||
for (uint32_t i = 0; i < PIPELINE_COUNT; i++) {
|
||||
createInfos[i] = base.createInfo;
|
||||
createInfos[i].pStages = stages[i].createInfos;
|
||||
};
|
||||
}
|
||||
|
||||
{ // Setup plain color pipelines.
|
||||
createInfos[PIPELINE_DRAW_COLOR].pVertexInputState = createInfos[PIPELINE_FILL_COLOR].pVertexInputState = &INPUT_STATE_COLOR_VERTEX;
|
||||
createInfos[PIPELINE_FILL_COLOR].pInputAssemblyState = &INPUT_ASSEMBLY_STATE_TRIANGLE_LIST;
|
||||
createInfos[PIPELINE_DRAW_COLOR].pInputAssemblyState = &INPUT_ASSEMBLY_STATE_LINE_LIST;
|
||||
stages[PIPELINE_DRAW_COLOR] = stages[PIPELINE_FILL_COLOR] = (ShaderStages) {{ shaders->color_vert, shaders->color_frag }};
|
||||
}
|
||||
// Setup input states.
|
||||
MAKE_INPUT_STATE(COLOR, VKColorVertex, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32B32A32_SFLOAT);
|
||||
MAKE_INPUT_STATE(MASK_FILL_COLOR, VKMaskFillColorVertex, VK_FORMAT_R32G32B32A32_SINT, VK_FORMAT_R32G32B32A32_SFLOAT);
|
||||
MAKE_INPUT_STATE(BLIT, VKTxVertex, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32_SFLOAT);
|
||||
MAKE_INPUT_STATE(CLIP, VKIntVertex, VK_FORMAT_R32G32_SINT);
|
||||
|
||||
{ // Setup texture pipeline.
|
||||
createInfos[PIPELINE_BLIT].pVertexInputState = &INPUT_STATE_TEXCOORD_VERTEX;
|
||||
createInfos[PIPELINE_BLIT].pInputAssemblyState = &INPUT_ASSEMBLY_STATE_TRIANGLE_STRIP;
|
||||
createInfos[PIPELINE_BLIT].layout = pipelineContext->texturePipelineLayout;
|
||||
stages[PIPELINE_BLIT] = (ShaderStages) {{ shaders->blit_vert, shaders->blit_frag }};
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
// Setup shader-specific pipeline parameters.
|
||||
switch (descriptors[i].shader) {
|
||||
case SHADER_COLOR:
|
||||
createInfos[i].pVertexInputState = &INPUT_STATE_COLOR;
|
||||
createInfos[i].layout = pipelineContext->colorPipelineLayout;
|
||||
stages[i] = (ShaderStages) {{ shaders->color_vert, shaders->color_frag }};
|
||||
break;
|
||||
case SHADER_MASK_FILL_COLOR:
|
||||
createInfos[i].pVertexInputState = &INPUT_STATE_MASK_FILL_COLOR;
|
||||
createInfos[i].layout = pipelineContext->maskFillPipelineLayout;
|
||||
stages[i] = (ShaderStages) {{ shaders->mask_fill_color_vert, shaders->mask_fill_color_frag }};
|
||||
break;
|
||||
case SHADER_BLIT:
|
||||
createInfos[i].pVertexInputState = &INPUT_STATE_BLIT;
|
||||
createInfos[i].layout = pipelineContext->texturePipelineLayout;
|
||||
stages[i] = (ShaderStages) {{ shaders->blit_vert, shaders->blit_frag }};
|
||||
break;
|
||||
case SHADER_CLIP:
|
||||
createInfos[i].pVertexInputState = &INPUT_STATE_CLIP;
|
||||
static const VkStencilOpState CLIP_STENCIL_OP = {
|
||||
.failOp = VK_STENCIL_OP_REPLACE,
|
||||
.passOp = VK_STENCIL_OP_REPLACE,
|
||||
.compareOp = VK_COMPARE_OP_NEVER,
|
||||
.compareMask = 0U,
|
||||
.writeMask = 0xFFFFFFFFU,
|
||||
.reference = CLIP_STENCIL_INCLUDE_VALUE
|
||||
};
|
||||
static const VkPipelineDepthStencilStateCreateInfo CLIP_STENCIL_STATE = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
||||
.stencilTestEnable = VK_TRUE,
|
||||
.front = CLIP_STENCIL_OP,
|
||||
.back = CLIP_STENCIL_OP
|
||||
};
|
||||
createInfos[i].pDepthStencilState = &CLIP_STENCIL_STATE;
|
||||
createInfos[i].layout = pipelineContext->texturePipelineLayout;
|
||||
createInfos[i].stageCount = 1;
|
||||
stages[i] = (ShaderStages) {{ shaders->clip_vert }};
|
||||
break;
|
||||
default:
|
||||
VK_FATAL_ERROR("Cannot create pipeline, unknown shader requested!");
|
||||
}
|
||||
assert(createInfos[i].pDynamicState->dynamicStateCount <= MAX_DYNAMIC_STATES);
|
||||
J2dRlsTraceLn4(J2D_TRACE_INFO, "VKPipelines_CreatePipelines: stencilMode=%d, composite=%d, shader=%d, topology=%d",
|
||||
descriptors[i].stencilMode, descriptors[i].composite, descriptors[i].shader, descriptors[i].topology);
|
||||
}
|
||||
|
||||
// Create pipelines.
|
||||
// TODO pipeline cache
|
||||
VK_IF_ERROR(device->vkCreateGraphicsPipelines(device->handle, VK_NULL_HANDLE, PIPELINE_COUNT,
|
||||
createInfos, NULL, set->pipelines)) VK_UNHANDLED_ERROR();
|
||||
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKPipelines_CreatePipelineSet: composite=%d", composite);
|
||||
return set;
|
||||
VkPipeline pipelines[count];
|
||||
VK_IF_ERROR(device->vkCreateGraphicsPipelines(device->handle, VK_NULL_HANDLE, count,
|
||||
createInfos, NULL, pipelines)) VK_UNHANDLED_ERROR();
|
||||
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKPipelines_CreatePipelines: created %d pipelines", count);
|
||||
for (uint32_t i = 0; i < count; ++i) MAP_AT(renderPassContext->pipelines, descriptors[i]) = pipelines[i];
|
||||
return pipelines[0];
|
||||
}
|
||||
|
||||
static VkResult VKPipelines_InitRenderPass(VKDevice* device, VKRenderPassContext* renderPassContext) {
|
||||
static VkResult VKPipelines_InitRenderPasses(VKDevice* device, VKRenderPassContext* renderPassContext) {
|
||||
assert(device != NULL && renderPassContext != NULL);
|
||||
VkAttachmentDescription colorAttachment = {
|
||||
VkAttachmentDescription attachments[] = {{
|
||||
.format = renderPassContext->format,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
|
||||
@@ -299,47 +284,61 @@ static VkResult VKPipelines_InitRenderPass(VKDevice* device, VKRenderPassContext
|
||||
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
|
||||
};
|
||||
}, {
|
||||
.format = VK_FORMAT_S8_UINT,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
|
||||
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
|
||||
.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
|
||||
}};
|
||||
VkAttachmentReference colorReference = {
|
||||
.attachment = 0,
|
||||
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
|
||||
};
|
||||
VkAttachmentReference stencilReference = {
|
||||
.attachment = 1,
|
||||
.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
|
||||
};
|
||||
VkSubpassDescription subpassDescription = {
|
||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
.colorAttachmentCount = 1,
|
||||
.pColorAttachments = &colorReference
|
||||
};
|
||||
// TODO this is probably not needed?
|
||||
// // Subpass dependencies for layout transitions
|
||||
// VkSubpassDependency dependency = {
|
||||
// .srcSubpass = VK_SUBPASS_EXTERNAL,
|
||||
// .dstSubpass = 0,
|
||||
// .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
// .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
// .srcAccessMask = 0,
|
||||
// .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
|
||||
// };
|
||||
VkRenderPassCreateInfo createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &colorAttachment,
|
||||
.pAttachments = attachments,
|
||||
.subpassCount = 1,
|
||||
.pSubpasses = &subpassDescription,
|
||||
.dependencyCount = 0,
|
||||
.pDependencies = NULL
|
||||
};
|
||||
return device->vkCreateRenderPass(device->handle, &createInfo, NULL, &renderPassContext->renderPass);
|
||||
for (uint32_t i = 0; i < 2; i++) {
|
||||
if (i == 1) {
|
||||
createInfo.attachmentCount = 2;
|
||||
subpassDescription.pDepthStencilAttachment = &stencilReference;
|
||||
}
|
||||
VkResult result = device->vkCreateRenderPass(device->handle, &createInfo, NULL, &renderPassContext->renderPass[i]);
|
||||
VK_IF_ERROR(result) return result;
|
||||
}
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
static void VKPipelines_DestroyRenderPassContext(VKRenderPassContext* renderPassContext) {
|
||||
if (renderPassContext == NULL) return;
|
||||
VKDevice* device = renderPassContext->pipelineContext->device;
|
||||
assert(device != NULL);
|
||||
for (uint32_t i = 0; i < ARRAY_SIZE(renderPassContext->pipelineSets); i++) {
|
||||
VKPipelines_DestroyPipelineSet(device, renderPassContext->pipelineSets[i]);
|
||||
for (const VKPipelineDescriptor* k = NULL; (k = MAP_NEXT_KEY(renderPassContext->pipelines, k)) != NULL;) {
|
||||
VkPipeline pipeline = *MAP_FIND(renderPassContext->pipelines, *k);
|
||||
device->vkDestroyPipeline(device->handle, pipeline, NULL);
|
||||
}
|
||||
MAP_FREE(renderPassContext->pipelines);
|
||||
for (uint32_t i = 0; i < 2; i++) {
|
||||
device->vkDestroyRenderPass(device->handle, renderPassContext->renderPass[i], NULL);
|
||||
}
|
||||
ARRAY_FREE(renderPassContext->pipelineSets);
|
||||
device->vkDestroyRenderPass(device->handle, renderPassContext->renderPass, NULL);
|
||||
J2dRlsTraceLn2(J2D_TRACE_INFO, "VKPipelines_DestroyRenderPassContext(%p): format=%d",
|
||||
renderPassContext, renderPassContext->format);
|
||||
free(renderPassContext);
|
||||
@@ -349,14 +348,18 @@ static VKRenderPassContext* VKPipelines_CreateRenderPassContext(VKPipelineContex
|
||||
assert(pipelineContext != NULL && pipelineContext->device != NULL);
|
||||
VKRenderPassContext* renderPassContext = calloc(1, sizeof(VKRenderPassContext));
|
||||
VK_RUNTIME_ASSERT(renderPassContext);
|
||||
HASH_MAP_REHASH(renderPassContext->pipelines, linear_probing,
|
||||
&pipelineDescriptorEquals, &pipelineDescriptorHash, 0, 10, 0.75);
|
||||
renderPassContext->pipelineContext = pipelineContext;
|
||||
renderPassContext->format = format;
|
||||
|
||||
VK_IF_ERROR(VKPipelines_InitRenderPass(pipelineContext->device, renderPassContext)) {
|
||||
VK_IF_ERROR(VKPipelines_InitRenderPasses(pipelineContext->device, renderPassContext)) {
|
||||
VKPipelines_DestroyRenderPassContext(renderPassContext);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// TODO create few common pipelines in advance? Like default shaders for SRC_OVER composite.
|
||||
|
||||
J2dRlsTraceLn2(J2D_TRACE_INFO, "VKPipelines_CreateRenderPassContext(%p): format=%d", renderPassContext, format);
|
||||
return renderPassContext;
|
||||
}
|
||||
@@ -377,7 +380,7 @@ static VkResult VKPipelines_InitPipelineLayouts(VKDevice* device, VKPipelineCont
|
||||
.pushConstantRangeCount = 1,
|
||||
.pPushConstantRanges = &pushConstantRange
|
||||
};
|
||||
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->pipelineLayout);
|
||||
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->colorPipelineLayout);
|
||||
VK_IF_ERROR(result) return result;
|
||||
|
||||
VkDescriptorSetLayoutBinding textureLayoutBinding = {
|
||||
@@ -400,6 +403,26 @@ static VkResult VKPipelines_InitPipelineLayouts(VKDevice* device, VKPipelineCont
|
||||
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->texturePipelineLayout);
|
||||
VK_IF_ERROR(result) return result;
|
||||
|
||||
VkDescriptorSetLayoutBinding maskBufferLayoutBinding = {
|
||||
.binding = 0,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
.pImmutableSamplers = NULL
|
||||
};
|
||||
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.bindingCount = 1,
|
||||
.pBindings = &maskBufferLayoutBinding
|
||||
};
|
||||
result = device->vkCreateDescriptorSetLayout(device->handle, &descriptorSetLayoutCreateInfo, NULL, &pipelines->maskFillDescriptorSetLayout);
|
||||
VK_IF_ERROR(result) return result;
|
||||
|
||||
createInfo.setLayoutCount = 1;
|
||||
createInfo.pSetLayouts = &pipelines->maskFillDescriptorSetLayout;
|
||||
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->maskFillPipelineLayout);
|
||||
VK_IF_ERROR(result) return result;
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -451,9 +474,11 @@ void VKPipelines_DestroyContext(VKPipelineContext* pipelineContext) {
|
||||
VKPipelines_DestroyShaders(device, pipelineContext->shaders);
|
||||
device->vkDestroySampler(device->handle, pipelineContext->linearRepeatSampler, NULL);
|
||||
|
||||
device->vkDestroyPipelineLayout(device->handle, pipelineContext->pipelineLayout, NULL);
|
||||
device->vkDestroyPipelineLayout(device->handle, pipelineContext->colorPipelineLayout, NULL);
|
||||
device->vkDestroyPipelineLayout(device->handle, pipelineContext->texturePipelineLayout, NULL);
|
||||
device->vkDestroyDescriptorSetLayout(device->handle, pipelineContext->textureDescriptorSetLayout, NULL);
|
||||
device->vkDestroyPipelineLayout(device->handle, pipelineContext->maskFillPipelineLayout, NULL);
|
||||
device->vkDestroyDescriptorSetLayout(device->handle, pipelineContext->maskFillDescriptorSetLayout, NULL);
|
||||
|
||||
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKPipelines_DestroyContext(%p)", pipelineContext);
|
||||
free(pipelineContext);
|
||||
@@ -468,20 +493,15 @@ VKRenderPassContext* VKPipelines_GetRenderPassContext(VKPipelineContext* pipelin
|
||||
}
|
||||
// Not found, create.
|
||||
VKRenderPassContext* renderPassContext = VKPipelines_CreateRenderPassContext(pipelineContext, format);
|
||||
ARRAY_PUSH_BACK(pipelineContext->renderPassContexts, renderPassContext);
|
||||
ARRAY_PUSH_BACK(pipelineContext->renderPassContexts) = renderPassContext;
|
||||
return renderPassContext;
|
||||
}
|
||||
|
||||
VkPipeline VKPipelines_GetPipeline(VKRenderPassContext* renderPassContext, VKCompositeMode composite, VKPipeline pipeline) {
|
||||
VkPipeline VKPipelines_GetPipeline(VKRenderPassContext* renderPassContext, VKPipelineDescriptor descriptor) {
|
||||
assert(renderPassContext != NULL);
|
||||
assert(composite < COMPOSITE_COUNT); // We could append custom composites after that index.
|
||||
assert(pipeline < PIPELINE_COUNT); // We could append custom pipelines after that index.
|
||||
// Currently, our pipelines map to composite modes 1-to-1, but this may change in future when we'll add more states.
|
||||
uint32_t setIndex = (uint32_t) composite;
|
||||
|
||||
while (ARRAY_SIZE(renderPassContext->pipelineSets) <= setIndex) ARRAY_PUSH_BACK(renderPassContext->pipelineSets, NULL);
|
||||
if (renderPassContext->pipelineSets[setIndex] == NULL) {
|
||||
renderPassContext->pipelineSets[setIndex] = VKPipelines_CreatePipelineSet(renderPassContext, composite);
|
||||
VkPipeline pipeline = MAP_AT(renderPassContext->pipelines, descriptor);
|
||||
if (pipeline == VK_NULL_HANDLE) {
|
||||
pipeline = VKPipelines_CreatePipelines(renderPassContext, 1, &descriptor);
|
||||
}
|
||||
return renderPassContext->pipelineSets[setIndex]->pipelines[pipeline];
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
@@ -24,76 +24,72 @@
|
||||
#ifndef VKPipelines_h_Included
|
||||
#define VKPipelines_h_Included
|
||||
|
||||
#include "java_awt_AlphaComposite.h"
|
||||
#include "VKComposites.h"
|
||||
#include "VKTypes.h"
|
||||
#include "VKUtil.h"
|
||||
|
||||
#define CLIP_STENCIL_INCLUDE_VALUE 0x80U
|
||||
#define CLIP_STENCIL_EXCLUDE_VALUE 0U
|
||||
|
||||
/**
|
||||
* All pipeline types.
|
||||
* Shader programs.
|
||||
*/
|
||||
typedef enum {
|
||||
PIPELINE_FILL_COLOR = 0,
|
||||
PIPELINE_DRAW_COLOR = 1,
|
||||
PIPELINE_BLIT = 2,
|
||||
PIPELINE_COUNT = 3,
|
||||
NO_PIPELINE = 0x7FFFFFFF
|
||||
} VKPipeline;
|
||||
SHADER_COLOR,
|
||||
SHADER_MASK_FILL_COLOR,
|
||||
SHADER_BLIT,
|
||||
SHADER_CLIP,
|
||||
NO_SHADER = 0x7FFFFFFF
|
||||
} VKShader;
|
||||
|
||||
typedef enum {
|
||||
STENCIL_MODE_NONE = 0, // No stencil attachment.
|
||||
STENCIL_MODE_OFF = 1, // Has stencil attachment, stencil test disabled.
|
||||
STENCIL_MODE_ON = 2 // Has stencil attachment, stencil test enabled.
|
||||
} VKStencilMode;
|
||||
|
||||
/**
|
||||
* There are two groups of composite modes:
|
||||
* - Logic composite - using logicOp.
|
||||
* - Alpha compisite - using blending.
|
||||
* All features describing a pipeline.
|
||||
* When adding new fields, update hash and comparison in VKPipelines.c.
|
||||
*/
|
||||
typedef enum {
|
||||
LOGIC_COMPOSITE_XOR = 0,
|
||||
LOGIC_COMPOSITE_GROUP = LOGIC_COMPOSITE_XOR,
|
||||
ALPHA_COMPOSITE_CLEAR = java_awt_AlphaComposite_CLEAR,
|
||||
ALPHA_COMPOSITE_SRC = java_awt_AlphaComposite_SRC,
|
||||
ALPHA_COMPOSITE_DST = java_awt_AlphaComposite_DST,
|
||||
ALPHA_COMPOSITE_SRC_OVER = java_awt_AlphaComposite_SRC_OVER,
|
||||
ALPHA_COMPOSITE_DST_OVER = java_awt_AlphaComposite_DST_OVER,
|
||||
ALPHA_COMPOSITE_SRC_IN = java_awt_AlphaComposite_SRC_IN,
|
||||
ALPHA_COMPOSITE_DST_IN = java_awt_AlphaComposite_DST_IN,
|
||||
ALPHA_COMPOSITE_SRC_OUT = java_awt_AlphaComposite_SRC_OUT,
|
||||
ALPHA_COMPOSITE_DST_OUT = java_awt_AlphaComposite_DST_OUT,
|
||||
ALPHA_COMPOSITE_SRC_ATOP = java_awt_AlphaComposite_SRC_ATOP,
|
||||
ALPHA_COMPOSITE_DST_ATOP = java_awt_AlphaComposite_DST_ATOP,
|
||||
ALPHA_COMPOSITE_XOR = java_awt_AlphaComposite_XOR,
|
||||
ALPHA_COMPOSITE_GROUP = ALPHA_COMPOSITE_XOR,
|
||||
COMPOSITE_COUNT = ALPHA_COMPOSITE_GROUP + 1,
|
||||
NO_COMPOSITE = 0x7FFFFFFF
|
||||
} VKCompositeMode;
|
||||
#define COMPOSITE_GROUP(COMPOSITE) ( \
|
||||
(COMPOSITE) <= LOGIC_COMPOSITE_GROUP ? LOGIC_COMPOSITE_GROUP : \
|
||||
(COMPOSITE) <= ALPHA_COMPOSITE_GROUP ? ALPHA_COMPOSITE_GROUP : \
|
||||
NO_COMPOSITE )
|
||||
|
||||
extern const VkPipelineColorBlendAttachmentState COMPOSITE_BLEND_STATES[COMPOSITE_COUNT];
|
||||
typedef struct {
|
||||
VKStencilMode stencilMode;
|
||||
VKCompositeMode composite;
|
||||
VKShader shader;
|
||||
VkPrimitiveTopology topology;
|
||||
} VKPipelineDescriptor;
|
||||
|
||||
/**
|
||||
* Global pipeline context.
|
||||
*/
|
||||
struct VKPipelineContext {
|
||||
VKDevice* device;
|
||||
VkPipelineLayout pipelineLayout;
|
||||
VkPipelineLayout texturePipelineLayout;
|
||||
VkDescriptorSetLayout textureDescriptorSetLayout;
|
||||
VKDevice* device;
|
||||
VkPipelineLayout colorPipelineLayout;
|
||||
VkDescriptorSetLayout textureDescriptorSetLayout;
|
||||
VkPipelineLayout texturePipelineLayout;
|
||||
VkDescriptorSetLayout maskFillDescriptorSetLayout;
|
||||
VkPipelineLayout maskFillPipelineLayout;
|
||||
|
||||
VkSampler linearRepeatSampler;
|
||||
VkSampler linearRepeatSampler;
|
||||
|
||||
struct VKShaders* shaders;
|
||||
VKRenderPassContext** renderPassContexts;
|
||||
struct VKShaders* shaders;
|
||||
ARRAY(VKRenderPassContext*) renderPassContexts;
|
||||
};
|
||||
|
||||
/**
|
||||
* Per-format context.
|
||||
*/
|
||||
struct VKRenderPassContext {
|
||||
VKPipelineContext* pipelineContext;
|
||||
VkFormat format;
|
||||
VkRenderPass renderPass;
|
||||
struct VKPipelineSet** pipelineSets; // TODO we will need a real hash map for this in the future.
|
||||
VKPipelineContext* pipelineContext;
|
||||
VkFormat format;
|
||||
VkRenderPass renderPass[2]; // Color-only and color+stencil.
|
||||
MAP(VKPipelineDescriptor, VkPipeline) pipelines;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int x, y;
|
||||
} VKIntVertex;
|
||||
|
||||
typedef struct {
|
||||
float x, y;
|
||||
Color color;
|
||||
@@ -104,10 +100,15 @@ typedef struct {
|
||||
float u, v;
|
||||
} VKTxVertex;
|
||||
|
||||
typedef struct {
|
||||
int x, y, maskOffset, maskScanline;
|
||||
Color color;
|
||||
} VKMaskFillColorVertex;
|
||||
|
||||
VKPipelineContext* VKPipelines_CreateContext(VKDevice* device);
|
||||
void VKPipelines_DestroyContext(VKPipelineContext* pipelines);
|
||||
|
||||
VKRenderPassContext* VKPipelines_GetRenderPassContext(VKPipelineContext* pipelineContext, VkFormat format);
|
||||
VkPipeline VKPipelines_GetPipeline(VKRenderPassContext* renderPassContext, VKCompositeMode composite, VKPipeline pipeline);
|
||||
VkPipeline VKPipelines_GetPipeline(VKRenderPassContext* renderPassContext, VKPipelineDescriptor descriptor);
|
||||
|
||||
#endif //VKPipelines_h_Included
|
||||
|
||||
@@ -26,9 +26,11 @@
|
||||
|
||||
#ifndef HEADLESS
|
||||
|
||||
#include "sun_font_StrikeCache.h"
|
||||
#include "sun_java2d_pipe_BufferedOpCodes.h"
|
||||
#include "sun_java2d_pipe_BufferedRenderPipe.h"
|
||||
#include "sun_java2d_pipe_BufferedTextPipe.h"
|
||||
#include "fontscalerdefs.h"
|
||||
#include "Trace.h"
|
||||
#include "jlong.h"
|
||||
#include "VKBase.h"
|
||||
@@ -91,15 +93,19 @@
|
||||
#define OFFSET_XFORM sun_java2d_vulkan_VKBlitLoops_OFFSET_XFORM
|
||||
#define OFFSET_ISOBLIT sun_java2d_vulkan_VKBlitLoops_OFFSET_ISOBLIT
|
||||
|
||||
#define NO_CLIP ((VkRect2D) {{0, 0}, {0x7FFFFFFFU, 0x7FFFFFFFU}})
|
||||
|
||||
// Rendering context is only accessed from VKRenderQueue_flushBuffer,
|
||||
// which is only called from queue flusher thread, no need for synchronization.
|
||||
static VKRenderingContext context = {
|
||||
.surface = NULL,
|
||||
.transform = {1.0, 0.0, 0.0,0.0, 1.0, 0.0},
|
||||
.clipRect = {{0, 0},{INT_MAX, INT_MAX}},
|
||||
.color = {},
|
||||
.composite = ALPHA_COMPOSITE_SRC_OVER,
|
||||
.extraAlpha = 1.0f
|
||||
.extraAlpha = 1.0f,
|
||||
.clipModCount = 1,
|
||||
.clipRect = NO_CLIP,
|
||||
.clipSpanVertices = NULL
|
||||
};
|
||||
// We keep this color separately from context.color,
|
||||
// because we need consistent state when switching between XOR and alpha composite modes.
|
||||
@@ -156,7 +162,7 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
|
||||
J2dRlsTraceLn4(J2D_TRACE_VERBOSE,
|
||||
"VKRenderQueue_flushBuffer: DRAW_RECT(%d, %d, %d, %d)",
|
||||
x, y, w, h);
|
||||
VKRenderer_RenderRect(&context, PIPELINE_DRAW_COLOR, x, y, w, h);
|
||||
VKRenderer_RenderRect(&context, VK_FALSE, x, y, w, h);
|
||||
}
|
||||
break;
|
||||
case sun_java2d_pipe_BufferedOpCodes_DRAW_POLY:
|
||||
@@ -198,7 +204,7 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
|
||||
J2dRlsTraceLn8(J2D_TRACE_VERBOSE,
|
||||
"VKRenderQueue_flushBuffer: DRAW_PARALLELOGRAM(%f, %f, %f, %f, %f, %f, %f, %f)",
|
||||
x11, y11, dx21, dy21, dx12, dy12, lwr21, lwr12);
|
||||
VKRenderer_RenderParallelogram(&context, PIPELINE_DRAW_COLOR, x11, y11, dx21, dy21, dx12, dy12);
|
||||
VKRenderer_RenderParallelogram(&context, VK_FALSE, x11, y11, dx21, dy21, dx12, dy12);
|
||||
}
|
||||
break;
|
||||
case sun_java2d_pipe_BufferedOpCodes_DRAW_AAPARALLELOGRAM:
|
||||
@@ -226,7 +232,7 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
|
||||
jint h = NEXT_INT(b);
|
||||
J2dRlsTraceLn4(J2D_TRACE_VERBOSE,
|
||||
"VKRenderQueue_flushBuffer: FILL_RECT(%d, %d, %d, %d)", x, y, w, h);
|
||||
VKRenderer_RenderRect(&context, PIPELINE_FILL_COLOR, x, y, w, h);
|
||||
VKRenderer_RenderRect(&context, VK_TRUE, x, y, w, h);
|
||||
}
|
||||
break;
|
||||
case sun_java2d_pipe_BufferedOpCodes_FILL_SPANS:
|
||||
@@ -249,7 +255,7 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
|
||||
J2dRlsTraceLn6(J2D_TRACE_VERBOSE,
|
||||
"VKRenderQueue_flushBuffer: FILL_PARALLELOGRAM(%f, %f, %f, %f, %f, %f)",
|
||||
x11, y11, dx21, dy21, dx12, dy12);
|
||||
VKRenderer_RenderParallelogram(&context, PIPELINE_FILL_COLOR, x11, y11, dx21, dy21, dx12, dy12);
|
||||
VKRenderer_RenderParallelogram(&context, VK_TRUE, x11, y11, dx21, dy21, dx12, dy12);
|
||||
}
|
||||
break;
|
||||
case sun_java2d_pipe_BufferedOpCodes_FILL_AAPARALLELOGRAM:
|
||||
@@ -263,6 +269,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
|
||||
J2dRlsTraceLn6(J2D_TRACE_VERBOSE,
|
||||
"VKRenderQueue_flushBuffer: FILL_AAPARALLELOGRAM(%f, %f, %f, %f, %f, %f)",
|
||||
x11, y11, dx21, dy21, dx12, dy12);
|
||||
// TODO this is not AA!
|
||||
VKRenderer_RenderParallelogram(&context, VK_TRUE, x11, y11, dx21, dy21, dx12, dy12);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -292,6 +300,25 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
|
||||
bytesPerGlyph = BYTES_PER_GLYPH_IMAGE;
|
||||
}
|
||||
J2dRlsTraceLn(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: DRAW_GLYPH_LIST");
|
||||
// TODO this is a quick and dirty implementation of greyscale-AA text rendering over MASK_FILL. Need to do better.
|
||||
jfloat glyphx, glyphy;
|
||||
for (int i = 0; i < numGlyphs; i++) {
|
||||
GlyphInfo *ginfo = (GlyphInfo *)jlong_to_ptr(NEXT_LONG(images));
|
||||
if (usePositions) {
|
||||
jfloat posx = NEXT_FLOAT(positions);
|
||||
jfloat posy = NEXT_FLOAT(positions);
|
||||
glyphx = glyphListOrigX + posx + ginfo->topLeftX;
|
||||
glyphy = glyphListOrigY + posy + ginfo->topLeftY;
|
||||
} else {
|
||||
glyphx = glyphListOrigX + ginfo->topLeftX;
|
||||
glyphy = glyphListOrigY + ginfo->topLeftY;
|
||||
glyphListOrigX += ginfo->advanceX;
|
||||
glyphListOrigY += ginfo->advanceY;
|
||||
}
|
||||
if (ginfo->format != sun_font_StrikeCache_PIXEL_FORMAT_GREYSCALE) continue;
|
||||
if (ginfo->height*ginfo->rowBytes == 0) continue;
|
||||
VKRenderer_MaskFill(&context, (int) glyphx, (int) glyphy, ginfo->width, ginfo->height, 0, ginfo->rowBytes, ginfo->height*ginfo->rowBytes, ginfo->image);
|
||||
}
|
||||
SKIP_BYTES(b, numGlyphs * bytesPerGlyph);
|
||||
}
|
||||
break;
|
||||
@@ -348,7 +375,7 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
|
||||
dx1, dy1, dx2, dy2);
|
||||
}
|
||||
context.surface = oldSurface;
|
||||
break;
|
||||
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: BLIT %p -> %p ", pSrc, pDst)
|
||||
J2dRlsTraceLn8(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: BLIT (%d %d %d %d) -> (%f %f %f %f) ",
|
||||
sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2)
|
||||
J2dRlsTraceLn4(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: BLIT texture=%d rtt=%d xform=%d isoblit=%d",
|
||||
@@ -381,7 +408,10 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
|
||||
jint maskscan = NEXT_INT(b);
|
||||
jint masklen = NEXT_INT(b);
|
||||
unsigned char *pMask = (masklen > 0) ? b : NULL;
|
||||
J2dRlsTraceLn(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: MASK_FILL");
|
||||
J2dRlsTraceLn7(J2D_TRACE_VERBOSE,
|
||||
"VKRenderQueue_flushBuffer: MASK_FILL(%d, %d, %dx%d, maskoff=%d, maskscan=%d, masklen=%d)",
|
||||
x, y, w, h, maskoff, maskscan, masklen);
|
||||
VKRenderer_MaskFill(&context, x, y, w, h, maskoff, maskscan, masklen, pMask);
|
||||
SKIP_BYTES(b, masklen);
|
||||
}
|
||||
break;
|
||||
@@ -404,18 +434,21 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
|
||||
jint y1 = NEXT_INT(b);
|
||||
jint x2 = NEXT_INT(b);
|
||||
jint y2 = NEXT_INT(b);
|
||||
context.clipRect = (VkRect2D){
|
||||
{x1, y1},
|
||||
{x2-x1, y2 - y1}};
|
||||
J2dRlsTraceLn4(J2D_TRACE_VERBOSE,
|
||||
"VKRenderQueue_flushBuffer: SET_RECT_CLIP(%d, %d, %d, %d)",
|
||||
x1, y1, x2, y2);
|
||||
ARRAY_RESIZE(context.clipSpanVertices, 0);
|
||||
jint width = x2 - x1, height = y2 - y1;
|
||||
context.clipRect = (VkRect2D) {{x1, y1}, {CARR_MAX(width, 0), CARR_MAX(height, 0)}};
|
||||
context.clipModCount++;
|
||||
}
|
||||
break;
|
||||
case sun_java2d_pipe_BufferedOpCodes_BEGIN_SHAPE_CLIP:
|
||||
{
|
||||
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
|
||||
"VKRenderQueue_flushBuffer: BEGIN_SHAPE_CLIP");
|
||||
ARRAY_RESIZE(context.clipSpanVertices, 0);
|
||||
context.clipModCount++;
|
||||
}
|
||||
break;
|
||||
case sun_java2d_pipe_BufferedOpCodes_SET_SHAPE_CLIP_SPANS:
|
||||
@@ -423,19 +456,38 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
|
||||
jint count = NEXT_INT(b);
|
||||
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
|
||||
"VKRenderQueue_flushBuffer: SET_SHAPE_CLIP_SPANS");
|
||||
SKIP_BYTES(b, count * BYTES_PER_SPAN);
|
||||
size_t offset = ARRAY_SIZE(context.clipSpanVertices);
|
||||
ARRAY_RESIZE(context.clipSpanVertices, offset + count * 6);
|
||||
for (jint i = 0; i < count; i++) {
|
||||
jint x1 = NEXT_INT(b);
|
||||
jint y1 = NEXT_INT(b);
|
||||
jint x2 = NEXT_INT(b);
|
||||
jint y2 = NEXT_INT(b);
|
||||
context.clipSpanVertices[offset + i * 6 + 0] = (VKIntVertex) {x1, y1};
|
||||
context.clipSpanVertices[offset + i * 6 + 1] = (VKIntVertex) {x2, y1};
|
||||
context.clipSpanVertices[offset + i * 6 + 2] = (VKIntVertex) {x2, y2};
|
||||
context.clipSpanVertices[offset + i * 6 + 3] = (VKIntVertex) {x2, y2};
|
||||
context.clipSpanVertices[offset + i * 6 + 4] = (VKIntVertex) {x1, y2};
|
||||
context.clipSpanVertices[offset + i * 6 + 5] = (VKIntVertex) {x1, y1};
|
||||
}
|
||||
context.clipModCount++;
|
||||
}
|
||||
break;
|
||||
case sun_java2d_pipe_BufferedOpCodes_END_SHAPE_CLIP:
|
||||
{
|
||||
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
|
||||
"VKRenderQueue_flushBuffer: END_SHAPE_CLIP");
|
||||
context.clipRect = NO_CLIP;
|
||||
context.clipModCount++;
|
||||
}
|
||||
break;
|
||||
case sun_java2d_pipe_BufferedOpCodes_RESET_CLIP:
|
||||
{
|
||||
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
|
||||
"VKRenderQueue_flushBuffer: RESET_CLIP");
|
||||
ARRAY_RESIZE(context.clipSpanVertices, 0);
|
||||
context.clipRect = NO_CLIP;
|
||||
context.clipModCount++;
|
||||
}
|
||||
break;
|
||||
case sun_java2d_pipe_BufferedOpCodes_SET_ALPHA_COMPOSITE:
|
||||
@@ -504,9 +556,16 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
|
||||
{
|
||||
VKSDOps* src = NEXT_SURFACE(b);
|
||||
VKSDOps* dst = NEXT_SURFACE(b);
|
||||
J2dRlsTraceLn2(J2D_TRACE_VERBOSE,
|
||||
"VKRenderQueue_flushBuffer: SET_SURFACES src=%p dst=%p", src, dst);
|
||||
|
||||
if (context.surface != NULL && context.surface != dst) {
|
||||
// TODO Problematic surface flush on a context switch without explicit presentation request.
|
||||
// Its presence here should not make any difference, but for some reason does.
|
||||
// Related scenarios need an investigation, e.g. J2Demo.
|
||||
VKRenderer_FlushSurface(context.surface);
|
||||
}
|
||||
|
||||
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
|
||||
"VKRenderQueue_flushBuffer: SET_SURFACES");
|
||||
context.surface = dst;
|
||||
}
|
||||
break;
|
||||
@@ -521,8 +580,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
|
||||
case sun_java2d_pipe_BufferedOpCodes_FLUSH_SURFACE:
|
||||
{
|
||||
VKSDOps* surface = NEXT_SURFACE(b);
|
||||
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
|
||||
"VKRenderQueue_flushBuffer: FLUSH_SURFACE");
|
||||
J2dRlsTraceLn1(J2D_TRACE_VERBOSE,
|
||||
"VKRenderQueue_flushBuffer: FLUSH_SURFACE (%p)", surface)
|
||||
}
|
||||
break;
|
||||
case sun_java2d_pipe_BufferedOpCodes_DISPOSE_SURFACE:
|
||||
@@ -559,8 +618,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
|
||||
VKSDOps* surface = NEXT_SURFACE(b);
|
||||
jint width = NEXT_INT(b);
|
||||
jint height = NEXT_INT(b);
|
||||
J2dRlsTraceLn2(J2D_TRACE_VERBOSE,
|
||||
"VKRenderQueue_flushBuffer: CONFIGURE_SURFACE %dx%d", width, height);
|
||||
J2dRlsTraceLn3(J2D_TRACE_VERBOSE,
|
||||
"VKRenderQueue_flushBuffer: CONFIGURE_SURFACE (%p) %dx%d", surface, width, height);
|
||||
VKRenderer_ConfigureSurface(surface, (VkExtent2D) {width, height});
|
||||
}
|
||||
break;
|
||||
@@ -577,8 +636,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
|
||||
case sun_java2d_pipe_BufferedOpCodes_FLUSH_BUFFER:
|
||||
{
|
||||
VKSDOps* surface = NEXT_SURFACE(b);
|
||||
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
|
||||
"VKRenderQueue_flushBuffer: FLUSH_BUFFER");
|
||||
J2dRlsTraceLn1(J2D_TRACE_VERBOSE,
|
||||
"VKRenderQueue_flushBuffer: FLUSH_BUFFER (%p)", surface)
|
||||
|
||||
VKRenderer_FlushSurface(surface);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#ifndef HEADLESS
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "VKUtil.h"
|
||||
#include "VKBase.h"
|
||||
#include "VKAllocator.h"
|
||||
@@ -39,11 +40,11 @@
|
||||
* Pool of resources with associated timestamps, guarding their reuse.
|
||||
* The pool must only be manipulated via POOL_* macros.
|
||||
*/
|
||||
#define POOL(TYPE, NAME) \
|
||||
struct PoolEntry_ ## NAME { \
|
||||
uint64_t timestamp; \
|
||||
TYPE value; \
|
||||
} *NAME
|
||||
#define POOL(TYPE, NAME) \
|
||||
RING_BUFFER(struct PoolEntry_ ## NAME { \
|
||||
uint64_t timestamp; \
|
||||
TYPE value; \
|
||||
}) NAME
|
||||
|
||||
/**
|
||||
* Take an available item from the pool. VAR is left unchanged if there is no available item.
|
||||
@@ -58,14 +59,14 @@ struct PoolEntry_ ## NAME { \
|
||||
* after the next submitted batch of work completes execution on GPU.
|
||||
*/
|
||||
// In debug mode resource reuse will be randomly delayed by 3 timestamps in ~20% cases.
|
||||
#define POOL_RETURN(RENDERER, NAME, VAR) RING_BUFFER_PUSH_BACK((RENDERER)->NAME, \
|
||||
#define POOL_RETURN(RENDERER, NAME, VAR) (RING_BUFFER_PUSH_BACK((RENDERER)->NAME) = \
|
||||
(struct PoolEntry_ ## NAME) { .timestamp = (RENDERER)->writeTimestamp + (VK_DEBUG_RANDOM(20)*3), .value = (VAR) })
|
||||
|
||||
/**
|
||||
* Insert an item into the pool. It is available for POOL_TAKE immediately.
|
||||
* This is usually used for bulk insertion of newly-created resources.
|
||||
*/
|
||||
#define POOL_INSERT(RENDERER, NAME, VAR) RING_BUFFER_PUSH_FRONT((RENDERER)->NAME, \
|
||||
#define POOL_INSERT(RENDERER, NAME, VAR) (RING_BUFFER_PUSH_FRONT((RENDERER)->NAME) = \
|
||||
(struct PoolEntry_ ## NAME) { .timestamp = 0ULL, .value = (VAR) })
|
||||
|
||||
/**
|
||||
@@ -90,11 +91,14 @@ struct VKRenderer {
|
||||
VKDevice* device;
|
||||
VKPipelineContext* pipelineContext;
|
||||
|
||||
POOL(VkCommandBuffer, commandBufferPool);
|
||||
POOL(VkCommandBuffer, secondaryCommandBufferPool);
|
||||
POOL(VkSemaphore, semaphorePool);
|
||||
POOL(VKBuffer, vertexBufferPool);
|
||||
VKMemory* vertexBufferMemoryPages;
|
||||
POOL(VkCommandBuffer, commandBufferPool);
|
||||
POOL(VkCommandBuffer, secondaryCommandBufferPool);
|
||||
POOL(VkSemaphore, semaphorePool);
|
||||
POOL(VKBuffer, vertexBufferPool);
|
||||
POOL(VKTexelBuffer, maskFillBufferPool);
|
||||
POOL(VkFramebuffer, framebufferDestructionQueue);
|
||||
ARRAY(VKMemory) bufferMemoryPages;
|
||||
ARRAY(VkDescriptorPool) descriptorPools;
|
||||
|
||||
/**
|
||||
* Last known timestamp hit by GPU execution. Resources with equal or less timestamp may be safely reused.
|
||||
@@ -110,14 +114,14 @@ struct VKRenderer {
|
||||
VkCommandBuffer commandBuffer;
|
||||
|
||||
struct Wait {
|
||||
VkSemaphore* semaphores;
|
||||
VkPipelineStageFlags* stages;
|
||||
ARRAY(VkSemaphore) semaphores;
|
||||
ARRAY(VkPipelineStageFlags) stages;
|
||||
} wait;
|
||||
|
||||
struct PendingPresentation {
|
||||
VkSwapchainKHR* swapchains;
|
||||
uint32_t* indices;
|
||||
VkResult* results;
|
||||
ARRAY(VkSwapchainKHR) swapchains;
|
||||
ARRAY(uint32_t) indices;
|
||||
ARRAY(VkResult) results;
|
||||
} pendingPresentation;
|
||||
};
|
||||
|
||||
@@ -134,20 +138,23 @@ typedef struct {
|
||||
*/
|
||||
struct VKRenderPass {
|
||||
VKRenderPassContext* context;
|
||||
VKBuffer* vertexBuffers;
|
||||
ARRAY(VKBuffer) vertexBuffers;
|
||||
ARRAY(VKTexelBuffer) maskFillBuffers;
|
||||
VkRenderPass renderPass; // Non-owning.
|
||||
VkFramebuffer framebuffer;
|
||||
VkCommandBuffer commandBuffer;
|
||||
|
||||
uint32_t firstVertex;
|
||||
uint32_t vertexCount;
|
||||
BufferWritingState vertexBufferWriting;
|
||||
BufferWritingState maskFillBufferWriting;
|
||||
|
||||
VKCompositeMode currentComposite;
|
||||
VKPipeline currentPipeline;
|
||||
VkBool32 pendingFlush;
|
||||
VkBool32 pendingCommands;
|
||||
VkBool32 pendingClear;
|
||||
uint64_t lastTimestamp; // When was this surface last used?
|
||||
VKPipelineDescriptor state;
|
||||
uint64_t clipModCount; // Just a tag to detect when clip was changed.
|
||||
VkBool32 pendingFlush;
|
||||
VkBool32 pendingCommands;
|
||||
VkBool32 pendingClear;
|
||||
uint64_t lastTimestamp; // When was this surface last used?
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -167,7 +174,10 @@ inline VkBool32 VKRenderer_CheckPoolEntryAvailable(VKRenderer* renderer, void* e
|
||||
*/
|
||||
static VkBool32 VKRenderer_CheckPoolDrain(void* pool, void* entry) {
|
||||
if (entry != NULL) return VK_TRUE;
|
||||
else if (pool != NULL) RING_BUFFER_FREE(pool);
|
||||
if (pool != NULL) {
|
||||
RING_BUFFER(char) ring_buffer = pool;
|
||||
RING_BUFFER_FREE(ring_buffer);
|
||||
}
|
||||
return VK_FALSE;
|
||||
}
|
||||
|
||||
@@ -188,11 +198,39 @@ static VKBuffer VKRenderer_GetVertexBuffer(VKRenderer* renderer) {
|
||||
VKRenderer_FindVertexBufferMemoryType,
|
||||
VERTEX_BUFFER_SIZE, VERTEX_BUFFER_PAGE_SIZE, &bufferCount, buffers);
|
||||
VK_RUNTIME_ASSERT(page);
|
||||
ARRAY_PUSH_BACK(renderer->vertexBufferMemoryPages, page);
|
||||
ARRAY_PUSH_BACK(renderer->bufferMemoryPages) = page;
|
||||
for (uint32_t i = 1; i < bufferCount; i++) POOL_INSERT(renderer, vertexBufferPool, buffers[i]);
|
||||
return buffers[0];
|
||||
}
|
||||
|
||||
#define MASK_FILL_BUFFER_SIZE (256 * 1024) // 256KiB = 256 typical MASK_FILL tiles
|
||||
#define MASK_FILL_BUFFER_PAGE_SIZE (4 * 1024 * 1024) // 4MiB - fits 16 buffers
|
||||
static void VKRenderer_FindMaskFillBufferMemoryType(VKMemoryRequirements* requirements) {
|
||||
VKAllocator_FindMemoryType(requirements, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
||||
VK_ALL_MEMORY_PROPERTIES);
|
||||
VKAllocator_FindMemoryType(requirements, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_ALL_MEMORY_PROPERTIES);
|
||||
}
|
||||
static VKTexelBuffer VKRenderer_GetMaskFillBuffer(VKRenderer* renderer) {
|
||||
VKTexelBuffer buffer = { .buffer.handle = VK_NULL_HANDLE };
|
||||
POOL_TAKE(renderer, maskFillBufferPool, buffer);
|
||||
if (buffer.buffer.handle != VK_NULL_HANDLE) return buffer;
|
||||
uint32_t bufferCount = MASK_FILL_BUFFER_PAGE_SIZE / MASK_FILL_BUFFER_SIZE;
|
||||
VKBuffer buffers[bufferCount];
|
||||
VKMemory page = VKBuffer_CreateBuffers(renderer->device, VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT,
|
||||
VKRenderer_FindMaskFillBufferMemoryType,
|
||||
MASK_FILL_BUFFER_SIZE, MASK_FILL_BUFFER_PAGE_SIZE, &bufferCount, buffers);
|
||||
VK_RUNTIME_ASSERT(page);
|
||||
VKTexelBuffer texelBuffers[bufferCount];
|
||||
VkDescriptorPool descriptorPool = VKBuffer_CreateTexelBuffers(
|
||||
renderer->device, VK_FORMAT_R8_UNORM, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER,
|
||||
renderer->pipelineContext->maskFillDescriptorSetLayout, bufferCount, buffers, texelBuffers);
|
||||
VK_RUNTIME_ASSERT(descriptorPool);
|
||||
for (uint32_t i = 1; i < bufferCount; i++) POOL_INSERT(renderer, maskFillBufferPool, texelBuffers[i]);
|
||||
ARRAY_PUSH_BACK(renderer->bufferMemoryPages) = page;
|
||||
ARRAY_PUSH_BACK(renderer->descriptorPools) = descriptorPool;
|
||||
return texelBuffers[0];
|
||||
}
|
||||
|
||||
static VkSemaphore VKRenderer_AddPendingSemaphore(VKRenderer* renderer) {
|
||||
VKDevice* device = renderer->device;
|
||||
VkSemaphore semaphore = VK_NULL_HANDLE;
|
||||
@@ -294,14 +332,26 @@ void VKRenderer_Destroy(VKRenderer* renderer) {
|
||||
device->vkDestroySemaphore(device->handle, entry->value, NULL);
|
||||
}
|
||||
|
||||
// Destroy vertex buffer pool.
|
||||
// Destroy buffer pools.
|
||||
POOL_DRAIN_FOR(renderer, vertexBufferPool, entry) {
|
||||
device->vkDestroyBuffer(device->handle, entry->value.handle, NULL);
|
||||
}
|
||||
for (uint32_t i = 0; i < ARRAY_SIZE(renderer->vertexBufferMemoryPages); i++) {
|
||||
VKAllocator_Free(device->allocator, renderer->vertexBufferMemoryPages[i]);
|
||||
POOL_DRAIN_FOR(renderer, maskFillBufferPool, entry) {
|
||||
// No need to destroy descriptor sets one by one, we will destroy the pool anyway.
|
||||
device->vkDestroyBufferView(device->handle, entry->value.view, NULL);
|
||||
device->vkDestroyBuffer(device->handle, entry->value.buffer.handle, NULL);
|
||||
}
|
||||
ARRAY_FREE(renderer->vertexBufferMemoryPages);
|
||||
POOL_DRAIN_FOR(renderer, framebufferDestructionQueue, entry) {
|
||||
device->vkDestroyFramebuffer(device->handle, entry->value, NULL);
|
||||
}
|
||||
for (uint32_t i = 0; i < ARRAY_SIZE(renderer->bufferMemoryPages); i++) {
|
||||
VKAllocator_Free(device->allocator, renderer->bufferMemoryPages[i]);
|
||||
}
|
||||
ARRAY_FREE(renderer->bufferMemoryPages);
|
||||
for (uint32_t i = 0; i < ARRAY_SIZE(renderer->descriptorPools); i++) {
|
||||
device->vkDestroyDescriptorPool(device->handle, renderer->descriptorPools[i], NULL);
|
||||
}
|
||||
ARRAY_FREE(renderer->descriptorPools);
|
||||
|
||||
device->vkDestroySemaphore(device->handle, renderer->timelineSemaphore, NULL);
|
||||
device->vkDestroyCommandPool(device->handle, renderer->commandPool, NULL);
|
||||
@@ -314,6 +364,17 @@ void VKRenderer_Destroy(VKRenderer* renderer) {
|
||||
free(renderer);
|
||||
}
|
||||
|
||||
static void VKRenderer_CleanupPendingResources(VKRenderer* renderer) {
|
||||
VKDevice* device = renderer->device;
|
||||
for (;;) {
|
||||
VkFramebuffer framebuffer = VK_NULL_HANDLE;
|
||||
POOL_TAKE(renderer, framebufferDestructionQueue, framebuffer);
|
||||
if (framebuffer == VK_NULL_HANDLE) break;
|
||||
device->vkDestroyFramebuffer(device->handle, framebuffer, NULL);
|
||||
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "VKRenderer_CleanupPendingResources(%p): framebuffer destroyed", renderer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record commands into primary command buffer (outside of a render pass).
|
||||
* Recorded commands will be sent for execution via VKRenderer_Flush.
|
||||
@@ -351,6 +412,7 @@ VkCommandBuffer VKRenderer_Record(VKRenderer* renderer) {
|
||||
|
||||
void VKRenderer_Flush(VKRenderer* renderer) {
|
||||
if (renderer == NULL) return;
|
||||
VKRenderer_CleanupPendingResources(renderer);
|
||||
VKDevice* device = renderer->device;
|
||||
size_t pendingPresentations = ARRAY_SIZE(renderer->pendingPresentation.swapchains);
|
||||
|
||||
@@ -436,7 +498,7 @@ void VKRenderer_AddImageBarrier(VkImageMemoryBarrier* barriers, VKBarrierBatch*
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = image->handle,
|
||||
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
|
||||
.subresourceRange = { VKImage_GetAspect(image), 0, 1, 0, 1 }
|
||||
};
|
||||
batch->barrierCount++;
|
||||
batch->srcStages |= image->lastStage;
|
||||
@@ -465,20 +527,28 @@ inline void VKRenderer_FlushDraw(VKSDOps* surface) {
|
||||
*/
|
||||
static void VKRenderer_ResetDrawing(VKSDOps* surface) {
|
||||
assert(surface != NULL && surface->renderPass != NULL);
|
||||
surface->renderPass->currentComposite = NO_COMPOSITE;
|
||||
surface->renderPass->currentPipeline = NO_PIPELINE;
|
||||
surface->renderPass->state.composite = NO_COMPOSITE;
|
||||
surface->renderPass->state.shader = NO_SHADER;
|
||||
surface->renderPass->firstVertex = 0;
|
||||
surface->renderPass->vertexCount = 0;
|
||||
surface->renderPass->vertexBufferWriting = (BufferWritingState) {NULL, 0, VK_FALSE};
|
||||
surface->renderPass->maskFillBufferWriting = (BufferWritingState) {NULL, 0, VK_FALSE};
|
||||
size_t vertexBufferCount = ARRAY_SIZE(surface->renderPass->vertexBuffers);
|
||||
if (vertexBufferCount == 0) return;
|
||||
VkMappedMemoryRange memoryRanges[vertexBufferCount];
|
||||
size_t maskFillBufferCount = ARRAY_SIZE(surface->renderPass->maskFillBuffers);
|
||||
if (vertexBufferCount == 0 && maskFillBufferCount == 0) return;
|
||||
VkMappedMemoryRange memoryRanges[vertexBufferCount + maskFillBufferCount];
|
||||
for (uint32_t i = 0; i < vertexBufferCount; i++) {
|
||||
memoryRanges[i] = surface->renderPass->vertexBuffers[i].range;
|
||||
POOL_RETURN(surface->device->renderer, vertexBufferPool, surface->renderPass->vertexBuffers[i]);
|
||||
}
|
||||
for (uint32_t i = 0; i < maskFillBufferCount; i++) {
|
||||
memoryRanges[vertexBufferCount + i] = surface->renderPass->maskFillBuffers[i].buffer.range;
|
||||
POOL_RETURN(surface->device->renderer, maskFillBufferPool, surface->renderPass->maskFillBuffers[i]);
|
||||
}
|
||||
ARRAY_RESIZE(surface->renderPass->vertexBuffers, 0);
|
||||
VK_IF_ERROR(surface->device->vkFlushMappedMemoryRanges(surface->device->handle, vertexBufferCount, memoryRanges)) {}
|
||||
ARRAY_RESIZE(surface->renderPass->maskFillBuffers, 0);
|
||||
VK_IF_ERROR(surface->device->vkFlushMappedMemoryRanges(surface->device->handle,
|
||||
vertexBufferCount + maskFillBufferCount, memoryRanges)) {}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -502,6 +572,7 @@ void VKRenderer_DestroyRenderPass(VKSDOps* surface) {
|
||||
if (device != NULL && device->renderer != NULL) {
|
||||
// Wait while surface resources are being used by the device.
|
||||
VKRenderer_Wait(device->renderer, surface->renderPass->lastTimestamp);
|
||||
VKRenderer_CleanupPendingResources(device->renderer);
|
||||
VKRenderer_DiscardRenderPass(surface);
|
||||
// Release resources.
|
||||
device->vkDestroyFramebuffer(device->handle, surface->renderPass->framebuffer, NULL);
|
||||
@@ -509,6 +580,7 @@ void VKRenderer_DestroyRenderPass(VKSDOps* surface) {
|
||||
POOL_RETURN(device->renderer, secondaryCommandBufferPool, surface->renderPass->commandBuffer);
|
||||
}
|
||||
ARRAY_FREE(surface->renderPass->vertexBuffers);
|
||||
ARRAY_FREE(surface->renderPass->maskFillBuffers);
|
||||
}
|
||||
free(surface->renderPass);
|
||||
surface->renderPass = NULL;
|
||||
@@ -533,10 +605,15 @@ static VkBool32 VKRenderer_InitRenderPass(VKSDOps* surface) {
|
||||
VKRenderPass* renderPass = surface->renderPass = malloc(sizeof(VKRenderPass));
|
||||
VK_RUNTIME_ASSERT(renderPass);
|
||||
(*renderPass) = (VKRenderPass) {
|
||||
.state = {
|
||||
.stencilMode = STENCIL_MODE_NONE,
|
||||
.composite = NO_COMPOSITE,
|
||||
.shader = NO_SHADER
|
||||
},
|
||||
.clipModCount = 0,
|
||||
.pendingFlush = VK_FALSE,
|
||||
.pendingCommands = VK_FALSE,
|
||||
.pendingClear = VK_TRUE, // Clear the surface by default
|
||||
.currentComposite = NO_COMPOSITE,
|
||||
.currentPipeline = NO_PIPELINE,
|
||||
.lastTimestamp = 0
|
||||
};
|
||||
|
||||
@@ -545,23 +622,48 @@ static VkBool32 VKRenderer_InitRenderPass(VKSDOps* surface) {
|
||||
renderPass->context = VKPipelines_GetRenderPassContext(renderer->pipelineContext, surface->image->format);
|
||||
}
|
||||
|
||||
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "VKRenderer_InitRenderPass(%p)", surface);
|
||||
return VK_TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize surface framebuffer.
|
||||
* This function can be called between render passes of a single frame, unlike VKRenderer_InitRenderPass.
|
||||
*/
|
||||
static void VKRenderer_InitFramebuffer(VKSDOps* surface) {
|
||||
assert(surface != NULL && surface->device != NULL && surface->renderPass != NULL);
|
||||
VKDevice* device = surface->device;
|
||||
VKRenderPass* renderPass = surface->renderPass;
|
||||
|
||||
if (renderPass->state.stencilMode == STENCIL_MODE_NONE && surface->stencil != NULL) {
|
||||
// Queue outdated color-only framebuffer for destruction.
|
||||
POOL_RETURN(device->renderer, framebufferDestructionQueue, renderPass->framebuffer);
|
||||
renderPass->framebuffer = VK_NULL_HANDLE;
|
||||
renderPass->state.stencilMode = STENCIL_MODE_OFF;
|
||||
}
|
||||
|
||||
// Initialize framebuffer.
|
||||
if (renderPass->framebuffer == VK_NULL_HANDLE) {
|
||||
renderPass->renderPass = renderPass->context->renderPass[surface->stencil != NULL];
|
||||
VkImageView views[] = { surface->image->view, VK_NULL_HANDLE };
|
||||
VkFramebufferCreateInfo framebufferCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||
.renderPass = renderPass->context->renderPass,
|
||||
.renderPass = renderPass->renderPass,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &surface->image->view,
|
||||
.pAttachments = views,
|
||||
.width = surface->image->extent.width,
|
||||
.height = surface->image->extent.height,
|
||||
.layers = 1
|
||||
};
|
||||
if (surface->stencil != NULL) {
|
||||
framebufferCreateInfo.attachmentCount = 2;
|
||||
views[1] = surface->stencil->view;
|
||||
}
|
||||
VK_IF_ERROR(device->vkCreateFramebuffer(device->handle, &framebufferCreateInfo, NULL,
|
||||
&renderPass->framebuffer)) VK_UNHANDLED_ERROR();
|
||||
}
|
||||
|
||||
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "VKRenderer_InitRenderPass(%p)", surface);
|
||||
return VK_TRUE;
|
||||
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "VKRenderer_InitFramebuffer(%p)", surface);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -569,6 +671,7 @@ static VkBool32 VKRenderer_InitRenderPass(VKSDOps* surface) {
|
||||
*/
|
||||
static void VKRenderer_BeginRenderPass(VKSDOps* surface) {
|
||||
assert(surface != NULL && surface->renderPass != NULL && !surface->renderPass->pendingCommands);
|
||||
VKRenderer_InitFramebuffer(surface);
|
||||
// We may have a pending flush, which is already obsolete.
|
||||
surface->renderPass->pendingFlush = VK_FALSE;
|
||||
VKDevice* device = surface->device;
|
||||
@@ -595,7 +698,7 @@ static void VKRenderer_BeginRenderPass(VKSDOps* surface) {
|
||||
// Begin recording render pass commands.
|
||||
VkCommandBufferInheritanceInfo inheritanceInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO,
|
||||
.renderPass = surface->renderPass->context->renderPass,
|
||||
.renderPass = surface->renderPass->renderPass,
|
||||
.subpass = 0,
|
||||
.framebuffer = surface->renderPass->framebuffer
|
||||
};
|
||||
@@ -624,7 +727,7 @@ static void VKRenderer_BeginRenderPass(VKSDOps* surface) {
|
||||
surface->renderPass->pendingClear = VK_FALSE;
|
||||
}
|
||||
|
||||
// Set viewport and scissor.
|
||||
// Set viewport.
|
||||
VkViewport viewport = {
|
||||
.x = 0.0f,
|
||||
.y = 0.0f,
|
||||
@@ -633,15 +736,13 @@ static void VKRenderer_BeginRenderPass(VKSDOps* surface) {
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 1.0f
|
||||
};
|
||||
VkRect2D scissor = {{0, 0}, surface->image->extent};
|
||||
device->vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
|
||||
device->vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
|
||||
// Calculate inverse viewport for vertex shader.
|
||||
viewport.width = 2.0f / viewport.width;
|
||||
viewport.height = 2.0f / viewport.height;
|
||||
device->vkCmdPushConstants(
|
||||
commandBuffer,
|
||||
renderer->pipelineContext->pipelineLayout,
|
||||
renderer->pipelineContext->colorPipelineLayout, // TODO what if our pipeline layout differs?
|
||||
VK_SHADER_STAGE_VERTEX_BIT,
|
||||
0,
|
||||
sizeof(float) * 2,
|
||||
@@ -666,30 +767,36 @@ void VKRenderer_FlushRenderPass(VKSDOps* surface) {
|
||||
surface->renderPass->lastTimestamp = renderer->writeTimestamp;
|
||||
VkCommandBuffer cb = VKRenderer_Record(renderer);
|
||||
|
||||
// Insert barrier to prepare surface for rendering.
|
||||
VkImageMemoryBarrier barriers[1];
|
||||
// Insert barriers to prepare surface for rendering.
|
||||
VkImageMemoryBarrier barriers[2];
|
||||
VKBarrierBatch barrierBatch = {};
|
||||
VKRenderer_AddImageBarrier(barriers, &barrierBatch, surface->image,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
if (surface->stencil != NULL) {
|
||||
VKRenderer_AddImageBarrier(barriers, &barrierBatch, surface->stencil,
|
||||
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
||||
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
|
||||
}
|
||||
if (barrierBatch.barrierCount > 0) {
|
||||
device->vkCmdPipelineBarrier(cb, barrierBatch.srcStages, barrierBatch.dstStages,
|
||||
0, 0, NULL, 0, NULL, barrierBatch.barrierCount, barriers);
|
||||
}
|
||||
|
||||
// If there is a pending clear, record it into render pass.
|
||||
if (clear) VKRenderer_BeginRenderPass(surface);
|
||||
// Begin render pass.
|
||||
VkRenderPassBeginInfo renderPassInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
.renderPass = surface->renderPass->context->renderPass,
|
||||
.renderPass = surface->renderPass->renderPass,
|
||||
.framebuffer = surface->renderPass->framebuffer,
|
||||
.renderArea = (VkRect2D) {{0, 0}, surface->image->extent},
|
||||
.clearValueCount = 0,
|
||||
.pClearValues = NULL
|
||||
};
|
||||
device->vkCmdBeginRenderPass(cb, &renderPassInfo, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
|
||||
// If there is a pending clear, record it into render pass.
|
||||
if (clear) VKRenderer_BeginRenderPass(surface);
|
||||
|
||||
// Execute render pass commands.
|
||||
if (surface->renderPass->pendingCommands) {
|
||||
@@ -740,8 +847,8 @@ void VKRenderer_FlushSurface(VKSDOps* surface) {
|
||||
|
||||
// Acquire swapchain image.
|
||||
VkSemaphore acquireSemaphore = VKRenderer_AddPendingSemaphore(renderer);
|
||||
ARRAY_PUSH_BACK(renderer->wait.semaphores, acquireSemaphore);
|
||||
ARRAY_PUSH_BACK(renderer->wait.stages, VK_PIPELINE_STAGE_TRANSFER_BIT); // Acquire image before blitting content onto swapchain
|
||||
ARRAY_PUSH_BACK(renderer->wait.semaphores) = acquireSemaphore;
|
||||
ARRAY_PUSH_BACK(renderer->wait.stages) = VK_PIPELINE_STAGE_TRANSFER_BIT; // Acquire image before blitting content onto swapchain
|
||||
|
||||
uint32_t imageIndex;
|
||||
VkResult acquireImageResult = device->vkAcquireNextImageKHR(device->handle, win->swapchain, UINT64_MAX,
|
||||
@@ -802,8 +909,8 @@ void VKRenderer_FlushSurface(VKSDOps* surface) {
|
||||
}
|
||||
|
||||
// Add pending presentation request.
|
||||
ARRAY_PUSH_BACK(renderer->pendingPresentation.swapchains, win->swapchain);
|
||||
ARRAY_PUSH_BACK(renderer->pendingPresentation.indices, imageIndex);
|
||||
ARRAY_PUSH_BACK(renderer->pendingPresentation.swapchains) = win->swapchain;
|
||||
ARRAY_PUSH_BACK(renderer->pendingPresentation.indices) = imageIndex;
|
||||
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "VKRenderer_FlushSurface(%p): queued for presentation", surface);
|
||||
}
|
||||
}
|
||||
@@ -854,8 +961,10 @@ inline BufferWritingState VKRenderer_AllocateBufferData(VKSDOps* surface, Buffer
|
||||
* This function must not be used directly, use VK_DRAW macro instead.
|
||||
* It is responsibility of the caller to pass correct vertexSize, matching current pipeline.
|
||||
* This function cannot draw more vertices than fits into single vertex buffer at once.
|
||||
* This function must be called after all dynamic allocation functions,
|
||||
* which can invalidate drawing state, e.g. VKRenderer_AllocateMaskFillBytes.
|
||||
*/
|
||||
static void* VKRenderer_AllocateVertices(VKRenderingContext* context, uint32_t vertices, size_t vertexSize) {
|
||||
static void* VKRenderer_AllocateVertices(const VKRenderingContext* context, uint32_t vertices, size_t vertexSize) {
|
||||
assert(vertices > 0 && vertexSize > 0);
|
||||
assert(vertexSize * vertices <= VERTEX_BUFFER_SIZE);
|
||||
VKSDOps* surface = context->surface;
|
||||
@@ -864,7 +973,7 @@ static void* VKRenderer_AllocateVertices(VKRenderingContext* context, uint32_t v
|
||||
if (!state.bound) {
|
||||
if (state.data == NULL) {
|
||||
VKBuffer buffer = VKRenderer_GetVertexBuffer(surface->device->renderer);
|
||||
ARRAY_PUSH_BACK(surface->renderPass->vertexBuffers, buffer);
|
||||
ARRAY_PUSH_BACK(surface->renderPass->vertexBuffers) = buffer;
|
||||
surface->renderPass->vertexBufferWriting.data = state.data = buffer.data;
|
||||
}
|
||||
assert(ARRAY_SIZE(surface->renderPass->vertexBuffers) > 0);
|
||||
@@ -878,14 +987,100 @@ static void* VKRenderer_AllocateVertices(VKRenderingContext* context, uint32_t v
|
||||
|
||||
/**
|
||||
* Allocate vertices from vertex buffer, providing pointer for writing.
|
||||
* VKRenderer_Validate must have been called before.
|
||||
* This function cannot draw more vertices than fits into single vertex buffer at once.
|
||||
* This function must be called after all dynamic allocation functions,
|
||||
* which can invalidate drawing state, e.g. VKRenderer_AllocateMaskFillBytes.
|
||||
*/
|
||||
#define VK_DRAW(VERTICES, CONTEXT, VERTEX_COUNT) \
|
||||
(VERTICES) = VKRenderer_AllocateVertices((CONTEXT), (VERTEX_COUNT), sizeof((VERTICES)[0]))
|
||||
|
||||
/**
|
||||
* Allocate bytes from mask fill buffer. VKRenderer_Validate must have been called before.
|
||||
* This function cannot take more bytes than fits into single mask fill buffer at once.
|
||||
* Caller must write data at the returned pointer DrawingBufferWritingState.data
|
||||
* and take into account DrawingBufferWritingState.offset from the beginning of the bound buffer.
|
||||
* This function can invalidate drawing state, always call it before VK_DRAW.
|
||||
*/
|
||||
static BufferWritingState VKRenderer_AllocateMaskFillBytes(const VKRenderingContext* context, uint32_t size) {
|
||||
assert(size > 0);
|
||||
assert(size <= MASK_FILL_BUFFER_SIZE);
|
||||
VKSDOps* surface = context->surface;
|
||||
BufferWritingState state = VKRenderer_AllocateBufferData(
|
||||
surface, &surface->renderPass->maskFillBufferWriting, size, MASK_FILL_BUFFER_SIZE);
|
||||
if (!state.bound) {
|
||||
if (state.data == NULL) {
|
||||
VKTexelBuffer buffer = VKRenderer_GetMaskFillBuffer(surface->device->renderer);
|
||||
ARRAY_PUSH_BACK(surface->renderPass->maskFillBuffers) = buffer;
|
||||
surface->renderPass->maskFillBufferWriting.data = state.data = buffer.buffer.data;
|
||||
}
|
||||
assert(ARRAY_SIZE(surface->renderPass->maskFillBuffers) > 0);
|
||||
surface->device->vkCmdBindDescriptorSets(context->surface->renderPass->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
surface->device->renderer->pipelineContext->maskFillPipelineLayout,
|
||||
0, 1, &ARRAY_LAST(surface->renderPass->maskFillBuffers).descriptorSet, 0, NULL);
|
||||
}
|
||||
state.data = (void*) ((uint8_t*) state.data + state.offset);
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup stencil attachment according to the context clip state.
|
||||
* If there is a clip shape, attachment is cleared with "fail" value and then
|
||||
* pixels inside the clip shape are set to "pass".
|
||||
* If there is no clip shape, whole attachment is cleared with "pass" value.
|
||||
*/
|
||||
static void VKRenderer_SetupStencil(const VKRenderingContext* context) {
|
||||
assert(context != NULL && context->surface != NULL && context->surface->renderPass != NULL);
|
||||
VKSDOps* surface = context->surface;
|
||||
VKRenderPass* renderPass = surface->renderPass;
|
||||
VkCommandBuffer cb = renderPass->commandBuffer;
|
||||
VKRenderer_FlushDraw(surface);
|
||||
|
||||
// Clear stencil attachment.
|
||||
VkClearAttachment clearAttachment = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT,
|
||||
.clearValue.depthStencil.stencil = ARRAY_SIZE(context->clipSpanVertices) > 0 ?
|
||||
CLIP_STENCIL_EXCLUDE_VALUE : CLIP_STENCIL_INCLUDE_VALUE
|
||||
};
|
||||
VkClearRect clearRect = {
|
||||
.rect = {{0, 0}, surface->stencil->extent},
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1
|
||||
};
|
||||
surface->device->vkCmdClearAttachments(cb, 1, &clearAttachment, 1, &clearRect);
|
||||
|
||||
// Bind the clip pipeline.
|
||||
surface->device->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
VKPipelines_GetPipeline(surface->renderPass->context, (VKPipelineDescriptor) {
|
||||
.stencilMode = STENCIL_MODE_ON,
|
||||
.composite = NO_COMPOSITE,
|
||||
.shader = SHADER_CLIP,
|
||||
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
|
||||
}));
|
||||
// Reset vertex buffer binding.
|
||||
renderPass->vertexBufferWriting.bound = VK_FALSE;
|
||||
|
||||
// Rasterize clip spans.
|
||||
const uint32_t MAX_VERTICES_PER_DRAW = (VERTEX_BUFFER_SIZE / sizeof(VKIntVertex) / 3) * 3;
|
||||
VKIntVertex* vs;
|
||||
for (uint32_t drawn = 0;;) {
|
||||
uint32_t currentDraw = ARRAY_SIZE(context->clipSpanVertices) - drawn;
|
||||
if (currentDraw > MAX_VERTICES_PER_DRAW) currentDraw = MAX_VERTICES_PER_DRAW;
|
||||
else if (currentDraw == 0) break;
|
||||
VK_DRAW(vs, context, currentDraw);
|
||||
memcpy(vs, context->clipSpanVertices + drawn, currentDraw * sizeof(VKIntVertex));
|
||||
drawn += currentDraw;
|
||||
}
|
||||
VKRenderer_FlushDraw(surface);
|
||||
|
||||
// Reset pipeline state.
|
||||
renderPass->state.shader = NO_SHADER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup pipeline for drawing. Returns FALSE if surface is not yet ready for drawing.
|
||||
*/
|
||||
VkBool32 VKRenderer_Validate(VKRenderingContext* context, VKPipeline pipeline) {
|
||||
VkBool32 VKRenderer_Validate(const VKRenderingContext* context, VKShader shader, VkPrimitiveTopology topology) {
|
||||
assert(context != NULL && context->surface != NULL);
|
||||
VKSDOps* surface = context->surface;
|
||||
|
||||
@@ -898,46 +1093,72 @@ VkBool32 VKRenderer_Validate(VKRenderingContext* context, VKPipeline pipeline) {
|
||||
VKRenderPass* renderPass = surface->renderPass;
|
||||
|
||||
// Validate render pass state.
|
||||
if (renderPass->currentComposite != context->composite) {
|
||||
if (renderPass->state.composite != context->composite ||
|
||||
renderPass->clipModCount != context->clipModCount) {
|
||||
// ALPHA_COMPOSITE_DST keeps destination intact, so don't even bother to change the state.
|
||||
if (context->composite == ALPHA_COMPOSITE_DST) return VK_FALSE;
|
||||
VKCompositeMode oldComposite = renderPass->currentComposite;
|
||||
VKCompositeMode oldComposite = renderPass->state.composite;
|
||||
VkBool32 clipChanged = renderPass->clipModCount != context->clipModCount;
|
||||
// Init stencil attachment, if needed.
|
||||
if (clipChanged && ARRAY_SIZE(context->clipSpanVertices) > 0 && surface->stencil == NULL) {
|
||||
if (surface->renderPass->pendingCommands) VKRenderer_FlushRenderPass(surface);
|
||||
if (!VKSD_ConfigureImageSurfaceStencil(surface)) return VK_FALSE;
|
||||
}
|
||||
// Update state.
|
||||
VKRenderer_FlushDraw(surface);
|
||||
renderPass->currentComposite = context->composite;
|
||||
renderPass->state.composite = context->composite;
|
||||
renderPass->clipModCount = context->clipModCount;
|
||||
// Begin render pass.
|
||||
if (!renderPass->pendingCommands) VKRenderer_BeginRenderPass(surface);
|
||||
VkBool32 renderPassJustStarted = !renderPass->pendingCommands;
|
||||
if (renderPassJustStarted) VKRenderer_BeginRenderPass(surface);
|
||||
// Validate current clip.
|
||||
if (clipChanged || renderPassJustStarted) {
|
||||
J2dTraceLn(J2D_TRACE_VERBOSE, "VKRenderer_Validate: updating clip");
|
||||
surface->device->vkCmdSetScissor(renderPass->commandBuffer, 0, 1, &context->clipRect);
|
||||
if (clipChanged) {
|
||||
if (ARRAY_SIZE(context->clipSpanVertices) > 0) {
|
||||
VKRenderer_SetupStencil(context);
|
||||
renderPass->state.stencilMode = STENCIL_MODE_ON;
|
||||
} else renderPass->state.stencilMode = surface->stencil != NULL ? STENCIL_MODE_OFF : STENCIL_MODE_NONE;
|
||||
}
|
||||
}
|
||||
// Validate current composite.
|
||||
J2dTraceLn2(J2D_TRACE_VERBOSE, "VKRenderer_Validate: updating composite, old=%d, new=%d", oldComposite, context->composite);
|
||||
// Reset the pipeline.
|
||||
renderPass->currentPipeline = NO_PIPELINE;
|
||||
if (oldComposite != context->composite) {
|
||||
J2dTraceLn2(J2D_TRACE_VERBOSE, "VKRenderer_Validate: updating composite, old=%d, new=%d", oldComposite, context->composite);
|
||||
// Reset the pipeline.
|
||||
renderPass->state.shader = NO_SHADER;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate current pipeline.
|
||||
if (renderPass->currentPipeline != pipeline) {
|
||||
if (renderPass->state.shader != shader || renderPass->state.topology != topology) {
|
||||
J2dTraceLn2(J2D_TRACE_VERBOSE, "VKRenderer_Validate: updating pipeline, old=%d, new=%d",
|
||||
renderPass->currentPipeline, pipeline);
|
||||
renderPass->state.shader, shader);
|
||||
VKRenderer_FlushDraw(surface);
|
||||
VkCommandBuffer cb = renderPass->commandBuffer;
|
||||
renderPass->currentPipeline = pipeline;
|
||||
renderPass->state.shader = shader;
|
||||
renderPass->state.topology = topology;
|
||||
surface->device->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
VKPipelines_GetPipeline(renderPass->context, context->composite, pipeline));
|
||||
VKPipelines_GetPipeline(renderPass->context, renderPass->state));
|
||||
renderPass->vertexBufferWriting.bound = VK_FALSE;
|
||||
renderPass->maskFillBufferWriting.bound = VK_FALSE;
|
||||
}
|
||||
return VK_TRUE;
|
||||
}
|
||||
|
||||
// Drawing operations.
|
||||
|
||||
void VKRenderer_RenderRect(VKRenderingContext* context, VKPipeline pipeline, jint x, jint y, jint w, jint h) {
|
||||
VKRenderer_RenderParallelogram(context, pipeline, (float) x, (float) y, (float) w, 0, 0, (float) h);
|
||||
void VKRenderer_RenderRect(const VKRenderingContext* context, VkBool32 fill,
|
||||
jint x, jint y, jint w, jint h) {
|
||||
VKRenderer_RenderParallelogram(context, fill, (float) x, (float) y, (float) w, 0, 0, (float) h);
|
||||
}
|
||||
|
||||
void VKRenderer_RenderParallelogram(VKRenderingContext* context, VKPipeline pipeline,
|
||||
void VKRenderer_RenderParallelogram(const VKRenderingContext* context, VkBool32 fill,
|
||||
jfloat x11, jfloat y11,
|
||||
jfloat dx21, jfloat dy21,
|
||||
jfloat dx12, jfloat dy12) {
|
||||
if (!VKRenderer_Validate(context, pipeline)) return; // Not ready.
|
||||
if (!VKRenderer_Validate(context, SHADER_COLOR,
|
||||
fill ? VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST : VK_PRIMITIVE_TOPOLOGY_LINE_LIST)) return; // Not ready.
|
||||
Color c = context->color;
|
||||
/* dx21
|
||||
* (p1)---------(p2) | (p1)------
|
||||
@@ -955,23 +1176,23 @@ void VKRenderer_RenderParallelogram(VKRenderingContext* context, VKPipeline pipe
|
||||
VKColorVertex p4 = {x11 + dx12, y11 + dy12, c};
|
||||
|
||||
VKColorVertex* vs;
|
||||
VK_DRAW(vs, context, pipeline == PIPELINE_DRAW_COLOR ? 8 : 6);
|
||||
VK_DRAW(vs, context, fill ? 6 : 8);
|
||||
uint32_t i = 0;
|
||||
vs[i++] = p1;
|
||||
vs[i++] = p2;
|
||||
vs[i++] = p3;
|
||||
vs[i++] = p4;
|
||||
vs[i++] = p1;
|
||||
if (pipeline == PIPELINE_DRAW_COLOR) {
|
||||
if (!fill) {
|
||||
vs[i++] = p4;
|
||||
vs[i++] = p2;
|
||||
}
|
||||
vs[i++] = p3;
|
||||
}
|
||||
|
||||
void VKRenderer_FillSpans(VKRenderingContext* context, jint spanCount, jint *spans) {
|
||||
void VKRenderer_FillSpans(const VKRenderingContext* context, jint spanCount, jint *spans) {
|
||||
if (spanCount == 0) return;
|
||||
if (!VKRenderer_Validate(context, PIPELINE_FILL_COLOR)) return; // Not ready.
|
||||
if (!VKRenderer_Validate(context, SHADER_COLOR, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)) return; // Not ready.
|
||||
Color c = context->color;
|
||||
|
||||
jfloat x1 = (float)*(spans++);
|
||||
@@ -998,9 +1219,9 @@ void VKRenderer_FillSpans(VKRenderingContext* context, jint spanCount, jint *spa
|
||||
}
|
||||
}
|
||||
|
||||
void VKRenderer_TextureRender(VKRenderingContext* context, VKImage *destImage, VKImage *srcImage,
|
||||
void VKRenderer_TextureRender(const VKRenderingContext* context, VKImage *destImage, VKImage *srcImage,
|
||||
VkBuffer vertexBuffer, uint32_t vertexNum) {
|
||||
if (!VKRenderer_Validate(context, PIPELINE_BLIT)) return; // Not ready.
|
||||
if (!VKRenderer_Validate(context, SHADER_BLIT, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP)) return; // Not ready.
|
||||
VKSDOps* surface = (VKSDOps*)context->surface;
|
||||
VKRenderPass* renderPass = surface->renderPass;
|
||||
VkCommandBuffer cb = renderPass->commandBuffer;
|
||||
@@ -1060,4 +1281,37 @@ void VKRenderer_TextureRender(VKRenderingContext* context, VKImage *destImage, V
|
||||
device->vkCmdDraw(cb, vertexNum, 1, 0, 0);
|
||||
}
|
||||
|
||||
void VKRenderer_MaskFill(const VKRenderingContext* context, jint x, jint y, jint w, jint h,
|
||||
jint maskoff, jint maskscan, jint masklen, uint8_t* mask) {
|
||||
if (!VKRenderer_Validate(context, SHADER_MASK_FILL_COLOR, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)) return; // Not ready.
|
||||
// maskoff is the offset from the beginning of mask,
|
||||
// it's the same as x and y offset within a tile (maskoff % maskscan, maskoff / maskscan).
|
||||
// maskscan is the number of bytes in a row/
|
||||
// masklen is the size of the whole mask tile, it may be way bigger, than number of actually needed bytes.
|
||||
uint32_t byteCount = maskscan * h;
|
||||
if (mask == NULL) {
|
||||
maskscan = 0;
|
||||
byteCount = 1;
|
||||
}
|
||||
BufferWritingState maskState = VKRenderer_AllocateMaskFillBytes(context, byteCount);
|
||||
if (mask != NULL) {
|
||||
memcpy(maskState.data, mask + maskoff, byteCount);
|
||||
} else {
|
||||
// Special case, fully opaque mask
|
||||
*((char *)maskState.data) = 0xFF;
|
||||
}
|
||||
|
||||
VKMaskFillColorVertex* vs;
|
||||
VK_DRAW(vs, context, 6);
|
||||
Color c = context->color;
|
||||
int offset = (int) maskState.offset;
|
||||
VKMaskFillColorVertex p1 = {x, y, offset, maskscan, c};
|
||||
VKMaskFillColorVertex p2 = {x + w, y, offset, maskscan, c};
|
||||
VKMaskFillColorVertex p3 = {x + w, y + h, offset, maskscan, c};
|
||||
VKMaskFillColorVertex p4 = {x, y + h, offset, maskscan, c};
|
||||
// Always keep p1 as provoking vertex for correct origin calculation in vertex shader.
|
||||
vs[0] = p1; vs[1] = p3; vs[2] = p2;
|
||||
vs[3] = p1; vs[4] = p3; vs[5] = p4;
|
||||
}
|
||||
|
||||
#endif /* !HEADLESS */
|
||||
|
||||
@@ -33,12 +33,14 @@
|
||||
struct VKRenderingContext {
|
||||
VKSDOps* surface;
|
||||
VKTransform transform;
|
||||
VkRect2D clipRect;
|
||||
Color color;
|
||||
VKCompositeMode composite;
|
||||
// Extra alpha is not used when painting with plain color,
|
||||
// in this case color.a already includes it.
|
||||
float extraAlpha;
|
||||
uint64_t clipModCount; // Used to track changes to the clip.
|
||||
VkRect2D clipRect;
|
||||
ARRAY(VKIntVertex) clipSpanVertices;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
@@ -52,7 +54,7 @@ VKRenderer* VKRenderer_Create(VKDevice* device);
|
||||
/**
|
||||
* Setup pipeline for drawing. Returns FALSE if surface is not yet ready for drawing.
|
||||
*/
|
||||
VkBool32 VKRenderer_Validate(VKRenderingContext* context, VKPipeline pipeline);
|
||||
VkBool32 VKRenderer_Validate(const VKRenderingContext* context, VKShader shader, VkPrimitiveTopology topology);
|
||||
|
||||
/**
|
||||
* Record commands into primary command buffer (outside of a render pass).
|
||||
@@ -102,19 +104,23 @@ void VKRenderer_FlushRenderPass(VKSDOps* surface);
|
||||
|
||||
// Blit operations.
|
||||
|
||||
void VKRenderer_TextureRender(VKRenderingContext* context,
|
||||
void VKRenderer_TextureRender(const VKRenderingContext* context,
|
||||
VKImage *destImage, VKImage *srcImage,
|
||||
VkBuffer vertexBuffer, uint32_t vertexNum);
|
||||
|
||||
// Drawing operations.
|
||||
|
||||
void VKRenderer_RenderRect(VKRenderingContext* context, VKPipeline pipeline, jint x, jint y, jint w, jint h);
|
||||
void VKRenderer_RenderRect(const VKRenderingContext* context, VkBool32 fill,
|
||||
jint x, jint y, jint w, jint h);
|
||||
|
||||
void VKRenderer_RenderParallelogram(VKRenderingContext* context, VKPipeline pipeline,
|
||||
void VKRenderer_RenderParallelogram(const VKRenderingContext* context, VkBool32 fill,
|
||||
jfloat x11, jfloat y11,
|
||||
jfloat dx21, jfloat dy21,
|
||||
jfloat dx12, jfloat dy12);
|
||||
|
||||
void VKRenderer_FillSpans(VKRenderingContext* context, jint spanCount, jint *spans);
|
||||
void VKRenderer_FillSpans(const VKRenderingContext* context, jint spanCount, jint *spans);
|
||||
|
||||
void VKRenderer_MaskFill(const VKRenderingContext* context,
|
||||
jint x, jint y, jint w, jint h, jint maskoff, jint maskscan, jint masklen, uint8_t* mask);
|
||||
|
||||
#endif //VKRenderer_h_Included
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@@ -40,9 +40,10 @@ static void VKSD_ResetImageSurface(VKSDOps* vksdo) {
|
||||
// DestroyRenderPass also waits while the surface resources are being used by device.
|
||||
VKRenderer_DestroyRenderPass(vksdo);
|
||||
|
||||
if (vksdo->device != NULL && vksdo->image != NULL) {
|
||||
if (vksdo->device != NULL) {
|
||||
VKImage_Destroy(vksdo->device, vksdo->stencil);
|
||||
VKImage_Destroy(vksdo->device, vksdo->image);
|
||||
vksdo->image = NULL;
|
||||
vksdo->image = vksdo->stencil = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +91,8 @@ VkBool32 VKSD_ConfigureImageSurface(VKSDOps* vksdo) {
|
||||
|
||||
VKImage* image = VKImage_Create(device, vksdo->requestedExtent.width, vksdo->requestedExtent.height,
|
||||
0, format, VK_IMAGE_TILING_OPTIMAL,
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
VK_SAMPLE_COUNT_1_BIT, VKSD_FindImageSurfaceMemoryType);
|
||||
VK_RUNTIME_ASSERT(image);
|
||||
VKSD_ResetImageSurface(vksdo);
|
||||
@@ -100,6 +102,25 @@ VkBool32 VKSD_ConfigureImageSurface(VKSDOps* vksdo) {
|
||||
return vksdo->image != NULL;
|
||||
}
|
||||
|
||||
VkBool32 VKSD_ConfigureImageSurfaceStencil(VKSDOps* vksdo) {
|
||||
// Check that image is ready.
|
||||
if (vksdo->image == NULL) {
|
||||
J2dRlsTraceLn1(J2D_TRACE_WARNING, "VKSD_ConfigureImageSurfaceStencil(%p): image is not ready", vksdo);
|
||||
return VK_FALSE;
|
||||
}
|
||||
// Initialize stencil image.
|
||||
if (vksdo->stencil == NULL) {
|
||||
vksdo->stencil = VKImage_Create(vksdo->device, vksdo->image->extent.width, vksdo->image->extent.height,
|
||||
0, VK_FORMAT_S8_UINT, VK_IMAGE_TILING_OPTIMAL,
|
||||
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
|
||||
VK_SAMPLE_COUNT_1_BIT, VKSD_FindImageSurfaceMemoryType);
|
||||
VK_RUNTIME_ASSERT(vksdo->stencil);
|
||||
J2dRlsTraceLn3(J2D_TRACE_INFO, "VKSD_ConfigureImageSurfaceStencil(%p): stencil image updated %dx%d",
|
||||
vksdo, vksdo->stencil->extent.width, vksdo->stencil->extent.height);
|
||||
}
|
||||
return vksdo->stencil != NULL;
|
||||
}
|
||||
|
||||
VkBool32 VKSD_ConfigureWindowSurface(VKWinSDOps* vkwinsdo) {
|
||||
// Check that image is ready.
|
||||
if (vkwinsdo->vksdOps.image == NULL) {
|
||||
@@ -273,4 +294,23 @@ VkBool32 VKSD_ConfigureWindowSurface(VKWinSDOps* vkwinsdo) {
|
||||
return VK_TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_java2d_vulkan_VKOffScreenSurfaceData
|
||||
* Method: initOps
|
||||
* Signature: (II)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKOffScreenSurfaceData_initOps
|
||||
(JNIEnv *env, jobject vksd, jint width, jint height) {
|
||||
VKSDOps * sd = (VKSDOps*)SurfaceData_InitOps(env, vksd, sizeof(VKSDOps));
|
||||
J2dTraceLn1(J2D_TRACE_VERBOSE, "VKOffScreenSurfaceData_initOps(%p)", sd);
|
||||
if (sd == NULL) {
|
||||
JNU_ThrowOutOfMemoryError(env, "Initialization of SurfaceData failed.");
|
||||
return;
|
||||
}
|
||||
sd->drawableType = VKSD_RT_TEXTURE;
|
||||
sd->background = VKUtil_DecodeJavaColor(0);
|
||||
VKSD_ResetSurface(sd);
|
||||
VKRenderer_ConfigureSurface(sd, (VkExtent2D){width, height});
|
||||
}
|
||||
|
||||
#endif /* !HEADLESS */
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include "SurfaceData.h"
|
||||
#include "sun_java2d_pipe_hw_AccelSurface.h"
|
||||
#include "VKUtil.h"
|
||||
#include "VKTypes.h"
|
||||
#include "VKRenderer.h"
|
||||
|
||||
@@ -50,6 +51,7 @@ struct VKSDOps {
|
||||
jint drawableType;
|
||||
VKDevice* device;
|
||||
VKImage* image;
|
||||
VKImage* stencil;
|
||||
|
||||
Color background;
|
||||
VkExtent2D requestedExtent;
|
||||
@@ -66,7 +68,7 @@ struct VKWinSDOps {
|
||||
VKSDOps vksdOps;
|
||||
VkSurfaceKHR surface;
|
||||
VkSwapchainKHR swapchain;
|
||||
VkImage* swapchainImages;
|
||||
ARRAY(VkImage) swapchainImages;
|
||||
VKDevice* swapchainDevice;
|
||||
VkExtent2D swapchainExtent;
|
||||
VKWinSD_SurfaceResizeCallback resizeCallback;
|
||||
@@ -84,6 +86,12 @@ void VKSD_ResetSurface(VKSDOps* vksdo);
|
||||
*/
|
||||
VkBool32 VKSD_ConfigureImageSurface(VKSDOps* vksdo);
|
||||
|
||||
/**
|
||||
* [Re]configure stencil attachment of the image surface.
|
||||
* VKSD_ConfigureImageSurface must have been called before.
|
||||
*/
|
||||
VkBool32 VKSD_ConfigureImageSurfaceStencil(VKSDOps* vksdo);
|
||||
|
||||
/**
|
||||
* Configure window surface. This [re]initializes the swapchain.
|
||||
* VKSD_ConfigureImageSurface must have been called before.
|
||||
|
||||
@@ -56,6 +56,7 @@ typedef struct VKRenderPass VKRenderPass;
|
||||
typedef struct VKRenderingContext VKRenderingContext;
|
||||
typedef struct VKPipelineContext VKPipelineContext;
|
||||
typedef struct VKRenderPassContext VKRenderPassContext;
|
||||
typedef struct VKTexelBuffer VKTexelBuffer;
|
||||
typedef struct VKBuffer VKBuffer;
|
||||
typedef struct VKImage VKImage;
|
||||
typedef struct VKSDOps VKSDOps;
|
||||
|
||||
@@ -30,9 +30,6 @@
|
||||
#include "jni_util.h"
|
||||
#include "VKTypes.h"
|
||||
|
||||
#define C_ARRAY_UTIL_ALLOCATION_FAILED() VK_FATAL_ERROR("CArrayUtil allocation failed")
|
||||
#include "CArrayUtil.h"
|
||||
|
||||
// VK_DEBUG_RANDOM may be used to randomly tune some parameters and turn off some features,
|
||||
// which would allow to cover wider range of scenarios and catch configuration-specific errors early.
|
||||
// In debug builds it returns 1 with approximately CHANCE_PERCENT chance, on release builds it is always 0.
|
||||
@@ -69,6 +66,9 @@ inline VkBool32 VKUtil_CheckError(VkResult result, const char* errorMessage) {
|
||||
#define VK_UNHANDLED_ERROR() VK_FATAL_ERROR("Unhandled Vulkan error")
|
||||
#define VK_RUNTIME_ASSERT(...) if (!(__VA_ARGS__)) VK_FATAL_ERROR("Vulkan assertion failed: " #__VA_ARGS__)
|
||||
|
||||
#define C_ARRAY_UTIL_ALLOCATION_FAILED() VK_FATAL_ERROR("CArrayUtil allocation failed")
|
||||
#include "CArrayUtil.h"
|
||||
|
||||
typedef enum {
|
||||
FORMAT_ALIAS_ORIGINAL = 0,
|
||||
FORMAT_ALIAS_UNORM = 1,
|
||||
|
||||
196
src/java.desktop/unix/classes/sun/awt/wl/GtkFileDialogPeer.java
Normal file
196
src/java.desktop/unix/classes/sun/awt/wl/GtkFileDialogPeer.java
Normal file
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.awt.wl;
|
||||
|
||||
import jdk.internal.misc.InnocuousThread;
|
||||
import java.awt.FileDialog;
|
||||
import java.awt.peer.FileDialogPeer;
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import sun.awt.AWTAccessor;
|
||||
import sun.awt.SunToolkit;
|
||||
|
||||
|
||||
final class GtkFileDialogPeer extends WLDialogPeer implements FileDialogPeer {
|
||||
|
||||
// A pointer to the native GTK FileChooser widget
|
||||
private volatile long widget;
|
||||
private volatile boolean quit;
|
||||
|
||||
GtkFileDialogPeer(FileDialog fd) {
|
||||
super(fd);
|
||||
}
|
||||
|
||||
private static native void initIDs();
|
||||
private native void run(String title, int mode, String dir, String file,
|
||||
FilenameFilter filter, boolean isMultipleMode);
|
||||
private native void quit();
|
||||
private native void toFrontImpl(long timestamp);
|
||||
|
||||
static {
|
||||
initIDs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toFront() {
|
||||
long timestamp = WLToolkit.getInputState().getTimestamp();
|
||||
toFrontImpl(timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public native void setBounds(int x, int y, int width, int height, int op);
|
||||
|
||||
/**
|
||||
* Called exclusively by the native C code.
|
||||
*/
|
||||
private void setFileInternal(String directory, String[] filenames) {
|
||||
AWTAccessor.FileDialogAccessor accessor = AWTAccessor.getFileDialogAccessor();
|
||||
FileDialog fd = (FileDialog) target;
|
||||
if (filenames == null) {
|
||||
accessor.setDirectory(fd, null);
|
||||
accessor.setFile(fd, null);
|
||||
accessor.setFiles(fd, null);
|
||||
} else {
|
||||
// Fix 6987233: add the trailing slash if it's absent
|
||||
String withSeparator = directory;
|
||||
if (directory != null) {
|
||||
withSeparator = directory.endsWith(File.separator) ?
|
||||
directory : (directory + File.separator);
|
||||
}
|
||||
accessor.setDirectory(fd, withSeparator);
|
||||
accessor.setFile(fd, filenames[0]);
|
||||
|
||||
File[] files = new File[filenames.length];
|
||||
for (int i = 0; i < filenames.length; i++) {
|
||||
files[i] = new File(directory, filenames[i]);
|
||||
}
|
||||
accessor.setFiles(fd, files);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called exclusively by the native C code.
|
||||
*/
|
||||
private boolean filenameFilterCallback(String fullname) {
|
||||
FileDialog fd = (FileDialog) target;
|
||||
|
||||
if (fd.getFilenameFilter() == null) {
|
||||
// no filter, accept all.
|
||||
return true;
|
||||
}
|
||||
|
||||
File file = new File(fullname);
|
||||
return fd.getFilenameFilter().accept(new File(file.getParent()), file.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean b) {
|
||||
SunToolkit.awtLock();
|
||||
try {
|
||||
quit = !b;
|
||||
if (b) {
|
||||
InnocuousThread.newThread("ShowGtkFileDialog", this::showNativeDialog).start();
|
||||
} else {
|
||||
quit();
|
||||
FileDialog fd = (FileDialog) target;
|
||||
fd.setVisible(false);
|
||||
}
|
||||
} finally {
|
||||
SunToolkit.awtUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
SunToolkit.awtLock();
|
||||
try {
|
||||
quit = true;
|
||||
quit();
|
||||
}
|
||||
finally {
|
||||
SunToolkit.awtUnlock();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDirectory(String dir) {
|
||||
// We do not implement this method because we
|
||||
// have delegated to FileDialog#setDirectory
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFile(String file) {
|
||||
// We do not implement this method because we
|
||||
// have delegated to FileDialog#setFile
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFilenameFilter(FilenameFilter filter) {
|
||||
// We do not implement this method because we
|
||||
// have delegated to FileDialog#setFilenameFilter
|
||||
}
|
||||
|
||||
private void showNativeDialog() {
|
||||
FileDialog fd = (FileDialog) target;
|
||||
String dirname = fd.getDirectory();
|
||||
// File path has a priority over the directory path.
|
||||
String filename = fd.getFile();
|
||||
if (filename != null) {
|
||||
final File file = new File(filename);
|
||||
if (fd.getMode() == FileDialog.LOAD
|
||||
&& dirname != null
|
||||
&& file.getParent() == null) {
|
||||
filename = dirname + (dirname.endsWith(File.separator) ? "" :
|
||||
File.separator) + filename;
|
||||
}
|
||||
if (fd.getMode() == FileDialog.SAVE && file.getParent() != null) {
|
||||
filename = file.getName();
|
||||
dirname = file.getParent();
|
||||
}
|
||||
}
|
||||
if (!quit) {
|
||||
run(fd.getTitle(), fd.getMode(), dirname, filename,
|
||||
fd.getFilenameFilter(), fd.isMultipleMode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by native code when GTK dialog is created.
|
||||
*/
|
||||
boolean setWindow() {
|
||||
return !quit && widget != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by native code when GTK dialog is closing.
|
||||
*/
|
||||
private void onClose() {
|
||||
widget = 0;
|
||||
FileDialog fd = (FileDialog) target;
|
||||
fd.setVisible(false);
|
||||
}
|
||||
}
|
||||
@@ -61,19 +61,20 @@ public final class WLClipboard extends SunClipboard {
|
||||
// false otherwise (the regular clipboard).
|
||||
private final boolean isPrimary; // used by native
|
||||
|
||||
private final Object dataLock = new Object();
|
||||
// A handle to the native clipboard representation, 0 if not available.
|
||||
private long clipboardNativePtr; // guarded by 'this'
|
||||
private long ourOfferNativePtr; // guarded by 'this'
|
||||
private long clipboardNativePtr; // guarded by dataLock
|
||||
private long ourOfferNativePtr; // guarded by dataLock
|
||||
|
||||
// The list of numeric format IDs the current clipboard is available in;
|
||||
// could be null or empty.
|
||||
private List<Long> clipboardFormats; // guarded by 'this'
|
||||
private List<Long> clipboardFormats; // guarded by dataLock
|
||||
|
||||
// The "current" list formats for the new clipboard contents that is about
|
||||
// to be received from Wayland. Could be empty, but never null.
|
||||
private List<Long> newClipboardFormats = new ArrayList<>(INITIAL_MIME_FORMATS_COUNT); // guarded by 'this'
|
||||
private List<Long> newClipboardFormats = new ArrayList<>(INITIAL_MIME_FORMATS_COUNT); // guarded by dataLock
|
||||
|
||||
private static Thread clipboardDispatcherThread;
|
||||
private static final Thread clipboardDispatcherThread;
|
||||
static {
|
||||
initIDs();
|
||||
dataOfferQueuePtr = createDataOfferQueue();
|
||||
@@ -175,11 +176,15 @@ public final class WLClipboard extends SunClipboard {
|
||||
log.fine("Clipboard: Offering new contents (" + contents + ") in these MIME formats: " + Arrays.toString(mime));
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
synchronized (dataLock) {
|
||||
if (ourOfferNativePtr != 0) {
|
||||
cancelOffer(ourOfferNativePtr);
|
||||
ourOfferNativePtr = 0;
|
||||
}
|
||||
ourOfferNativePtr = offerData(eventSerial, mime, contents, dataOfferQueuePtr);
|
||||
}
|
||||
long newOfferPtr = offerData(eventSerial, mime, contents, dataOfferQueuePtr);
|
||||
synchronized (dataLock) {
|
||||
ourOfferNativePtr = newOfferPtr;
|
||||
}
|
||||
|
||||
// Once we have offered the data, someone may come back and ask to provide them.
|
||||
@@ -252,7 +257,7 @@ public final class WLClipboard extends SunClipboard {
|
||||
*/
|
||||
@Override
|
||||
protected long[] getClipboardFormats() {
|
||||
synchronized (this) {
|
||||
synchronized (dataLock) {
|
||||
if (clipboardFormats != null && !clipboardFormats.isEmpty()) {
|
||||
long[] res = new long[clipboardFormats.size()];
|
||||
for (int i = 0; i < res.length; i++) {
|
||||
@@ -274,7 +279,25 @@ public final class WLClipboard extends SunClipboard {
|
||||
*/
|
||||
@Override
|
||||
protected byte[] getClipboardData(long format) throws IOException {
|
||||
synchronized (this) {
|
||||
int fd = getClipboardFDIn(format);
|
||||
if (fd >= 0) {
|
||||
FileDescriptor javaFD = new FileDescriptor();
|
||||
jdk.internal.access.SharedSecrets.getJavaIOFileDescriptorAccess().set(javaFD, fd);
|
||||
try (var in = new FileInputStream(javaFD)) {
|
||||
byte[] bytes = readAllBytesFrom(in);
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("Clipboard: read data from " + fd + ": "
|
||||
+ (bytes != null ? bytes.length : 0) + " bytes");
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private int getClipboardFDIn(long format) {
|
||||
synchronized (dataLock) {
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("Clipboard: requested content of clipboard with handle "
|
||||
+ clipboardNativePtr + " in format " + format);
|
||||
@@ -286,21 +309,10 @@ public final class WLClipboard extends SunClipboard {
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("Clipboard: will read data from " + fd + " in format " + mime);
|
||||
}
|
||||
if (fd >= 0) {
|
||||
FileDescriptor javaFD = new FileDescriptor();
|
||||
jdk.internal.access.SharedSecrets.getJavaIOFileDescriptorAccess().set(javaFD, fd);
|
||||
try (var in = new FileInputStream(javaFD)) {
|
||||
byte[] bytes = readAllBytesFrom(in);
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("Clipboard: read data from " + fd + ": "
|
||||
+ (bytes != null ? bytes.length : 0) + " bytes");
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -321,7 +333,7 @@ public final class WLClipboard extends SunClipboard {
|
||||
log.fine("Clipboard: new format is available for " + nativePtr + ": " + mime);
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
synchronized (dataLock) {
|
||||
newClipboardFormats.add(format);
|
||||
}
|
||||
}
|
||||
@@ -342,7 +354,7 @@ public final class WLClipboard extends SunClipboard {
|
||||
log.fine("Clipboard: new clipboard is available: " + newClipboardNativePtr);
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
synchronized (dataLock) {
|
||||
long oldClipboardNativePtr = clipboardNativePtr;
|
||||
if (oldClipboardNativePtr != 0) {
|
||||
// "The client must destroy the previous selection data_offer, if any, upon receiving this event."
|
||||
@@ -352,13 +364,13 @@ public final class WLClipboard extends SunClipboard {
|
||||
clipboardNativePtr = newClipboardNativePtr; // Could be NULL
|
||||
|
||||
newClipboardFormats = new ArrayList<>(INITIAL_MIME_FORMATS_COUNT);
|
||||
|
||||
notifyOfNewFormats(getClipboardFormats());
|
||||
}
|
||||
|
||||
notifyOfNewFormats(getClipboardFormats());
|
||||
}
|
||||
|
||||
private void handleOfferCancelled(long offerNativePtr) {
|
||||
synchronized (this) {
|
||||
synchronized (dataLock) {
|
||||
assert offerNativePtr == ourOfferNativePtr;
|
||||
ourOfferNativePtr = 0;
|
||||
}
|
||||
|
||||
@@ -509,7 +509,7 @@ public class WLComponentPeer implements ComponentPeer {
|
||||
SurfaceData.convertTo(WLSurfaceDataExt.class, surfaceData).commit();
|
||||
}
|
||||
});
|
||||
Toolkit.getDefaultToolkit().sync();
|
||||
((WLToolkit) Toolkit.getDefaultToolkit()).flush();
|
||||
}
|
||||
|
||||
private boolean canPaintRoundedCorners() {
|
||||
|
||||
124
src/java.desktop/unix/classes/sun/awt/wl/WLDesktopPeer.java
Normal file
124
src/java.desktop/unix/classes/sun/awt/wl/WLDesktopPeer.java
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.awt.wl;
|
||||
|
||||
import sun.awt.SunToolkit;
|
||||
import sun.awt.UNIXToolkit;
|
||||
|
||||
import java.awt.Desktop;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
|
||||
import java.awt.Desktop.Action;
|
||||
import java.awt.peer.DesktopPeer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class WLDesktopPeer implements DesktopPeer {
|
||||
|
||||
// supportedActions may be changed from native within the init() call
|
||||
private static final List<Desktop.Action> supportedActions
|
||||
= new ArrayList<>(Arrays.asList(Action.OPEN, Action.MAIL, Action.BROWSE));
|
||||
|
||||
private static boolean nativeLibraryLoaded = false;
|
||||
private static boolean initExecuted = false;
|
||||
|
||||
private static void initWithLock(){
|
||||
SunToolkit.awtLock();
|
||||
try {
|
||||
if (!initExecuted) {
|
||||
nativeLibraryLoaded = init(
|
||||
UNIXToolkit.getEnabledGtkVersion().getNumber(),
|
||||
UNIXToolkit.isGtkVerbose());
|
||||
}
|
||||
} finally {
|
||||
initExecuted = true;
|
||||
SunToolkit.awtUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
WLDesktopPeer(){
|
||||
initWithLock();
|
||||
}
|
||||
|
||||
static boolean isDesktopSupported() {
|
||||
initWithLock();
|
||||
return nativeLibraryLoaded && !supportedActions.isEmpty();
|
||||
}
|
||||
|
||||
public boolean isSupported(Action type) {
|
||||
return supportedActions.contains(type);
|
||||
}
|
||||
|
||||
public void open(File file) throws IOException {
|
||||
try {
|
||||
launch(file.toURI());
|
||||
} catch (MalformedURLException e) {
|
||||
throw new IOException(file.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public void edit(File file) throws IOException {
|
||||
throw new UnsupportedOperationException("The current platform " +
|
||||
"doesn't support the EDIT action.");
|
||||
}
|
||||
|
||||
public void print(File file) throws IOException {
|
||||
throw new UnsupportedOperationException("The current platform " +
|
||||
"doesn't support the PRINT action.");
|
||||
}
|
||||
|
||||
public void mail(URI uri) throws IOException {
|
||||
launch(uri);
|
||||
}
|
||||
|
||||
public void browse(URI uri) throws IOException {
|
||||
launch(uri);
|
||||
}
|
||||
|
||||
private void launch(URI uri) throws IOException {
|
||||
byte[] uriByteArray = ( uri.toString() + '\0' ).getBytes();
|
||||
boolean result = false;
|
||||
SunToolkit.awtLock();
|
||||
try {
|
||||
if (!nativeLibraryLoaded) {
|
||||
throw new IOException("Failed to load native libraries.");
|
||||
}
|
||||
result = gnome_url_show(uriByteArray);
|
||||
} finally {
|
||||
SunToolkit.awtUnlock();
|
||||
}
|
||||
if (!result) {
|
||||
throw new IOException("Failed to show URI:" + uri);
|
||||
}
|
||||
}
|
||||
|
||||
private native boolean gnome_url_show(byte[] url);
|
||||
private static native boolean init(int gtkVersion, boolean verbose);
|
||||
}
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
package sun.awt.wl;
|
||||
|
||||
import java.awt.event.InputEvent;
|
||||
|
||||
/**
|
||||
* MouseEvent objects cannot be created directly from WLPointerEvent because they require
|
||||
* the information of certain events from the past like keyboard modifiers keys getting
|
||||
@@ -347,4 +349,8 @@ record WLInputState(WLPointerEvent eventWithSurface,
|
||||
public int getModifiers() {
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
public int getNonKeyboardModifiers() {
|
||||
return modifiers & ~(InputEvent.SHIFT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK | InputEvent.META_DOWN_MASK | InputEvent.ALT_DOWN_MASK);
|
||||
}
|
||||
}
|
||||
@@ -112,49 +112,15 @@ class WLKeyboard {
|
||||
}
|
||||
}
|
||||
|
||||
public static final int XKB_SHIFT_MASK = 1 << 0;
|
||||
public static final int XKB_CAPS_LOCK_MASK = 1 << 1;
|
||||
public static final int XKB_CTRL_MASK = 1 << 2;
|
||||
public static final int XKB_ALT_MASK = 1 << 3;
|
||||
public static final int XKB_NUM_LOCK_MASK = 1 << 4;
|
||||
public static final int XKB_MOD3_MASK = 1 << 5;
|
||||
public static final int XKB_META_MASK = 1 << 6;
|
||||
public static final int XKB_MOD5_MASK = 1 << 7;
|
||||
|
||||
private final KeyRepeatManager keyRepeatManager;
|
||||
|
||||
private native void initialize(KeyRepeatManager keyRepeatManager);
|
||||
|
||||
public int getModifiers() {
|
||||
int result = 0;
|
||||
int mask = getXKBModifiersMask();
|
||||
public native int getModifiers();
|
||||
|
||||
if ((mask & XKB_SHIFT_MASK) != 0) {
|
||||
result |= InputEvent.SHIFT_DOWN_MASK;
|
||||
}
|
||||
public native boolean isCapsLockPressed();
|
||||
|
||||
if ((mask & XKB_CTRL_MASK) != 0) {
|
||||
result |= InputEvent.CTRL_DOWN_MASK;
|
||||
}
|
||||
|
||||
if ((mask & XKB_ALT_MASK) != 0) {
|
||||
result |= InputEvent.ALT_DOWN_MASK;
|
||||
}
|
||||
|
||||
if ((mask & XKB_META_MASK) != 0) {
|
||||
result |= InputEvent.META_DOWN_MASK;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isCapsLockPressed() {
|
||||
return (getXKBModifiersMask() & XKB_CAPS_LOCK_MASK) != 0;
|
||||
}
|
||||
|
||||
public boolean isNumLockPressed() {
|
||||
return (getXKBModifiersMask() & XKB_NUM_LOCK_MASK) != 0;
|
||||
}
|
||||
public native boolean isNumLockPressed();
|
||||
|
||||
public void onLostFocus() {
|
||||
assert EventQueue.isDispatchThread();
|
||||
|
||||
@@ -37,6 +37,7 @@ import sun.awt.SunToolkit;
|
||||
import sun.awt.UNIXToolkit;
|
||||
import sun.awt.datatransfer.DataTransferer;
|
||||
import sun.java2d.vulkan.VKInstance;
|
||||
import sun.java2d.vulkan.VKRenderQueue;
|
||||
import sun.util.logging.PlatformLogger;
|
||||
|
||||
import java.awt.*;
|
||||
@@ -160,6 +161,8 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
|
||||
|
||||
private static Cursor currentCursor;
|
||||
|
||||
private static Boolean sunAwtDisableGtkFileDialogs = null;
|
||||
|
||||
private static native void initIDs(long displayPtr);
|
||||
|
||||
static {
|
||||
@@ -202,6 +205,13 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized boolean getSunAwtDisableGtkFileDialogs() {
|
||||
if (sunAwtDisableGtkFileDialogs == null) {
|
||||
sunAwtDisableGtkFileDialogs = Boolean.getBoolean("sun.awt.disableGtkFileDialogs");
|
||||
}
|
||||
return sunAwtDisableGtkFileDialogs;
|
||||
}
|
||||
|
||||
private static void initSystemProperties() {
|
||||
final String extraButtons = "sun.awt.enableExtraMouseButtons";
|
||||
areExtraMouseButtonsEnabled =
|
||||
@@ -343,7 +353,8 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
|
||||
int keyLocation,
|
||||
int rawCode,
|
||||
int extendedKeyCode,
|
||||
char keyChar) {
|
||||
char keyChar,
|
||||
int modifiers) {
|
||||
// Invoked from the native code
|
||||
assert EventQueue.isDispatchThread();
|
||||
|
||||
@@ -375,7 +386,8 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
|
||||
keyChar,
|
||||
keyLocation,
|
||||
rawCode,
|
||||
extendedKeyCode
|
||||
extendedKeyCode,
|
||||
modifiers
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -391,9 +403,11 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
|
||||
char keyChar,
|
||||
int keyLocation,
|
||||
long rawCode,
|
||||
int extendedKeyCode) {
|
||||
int extendedKeyCode,
|
||||
int modifiers) {
|
||||
int mergedModifiers = inputState.getNonKeyboardModifiers() | modifiers;
|
||||
final KeyEvent keyEvent = new KeyEvent(source, id, timestamp,
|
||||
inputState.getModifiers(), keyCode, keyChar, keyLocation);
|
||||
mergedModifiers, keyCode, keyChar, keyLocation);
|
||||
|
||||
AWTAccessor.KeyEventAccessor kea = AWTAccessor.getKeyEventAccessor();
|
||||
kea.setRawCode(keyEvent, rawCode);
|
||||
@@ -676,10 +690,17 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
|
||||
|
||||
@Override
|
||||
public FileDialogPeer createFileDialog(FileDialog target) {
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("Not implemented: WLToolkit.createFileDialog()");
|
||||
FileDialogPeer peer = null;
|
||||
if (!getSunAwtDisableGtkFileDialogs() && checkGtkVersion(3, 0, 0)) {
|
||||
peer = new GtkFileDialogPeer(target);
|
||||
targetCreatedPeer(target, peer);
|
||||
return peer;
|
||||
} else {
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("Not implemented: WLToolkit.createFileDialog()");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -980,18 +1001,12 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
|
||||
*/
|
||||
@Override
|
||||
public boolean isDesktopSupported() {
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("Not implemented: WLToolkit.isDesktopSupported()");
|
||||
}
|
||||
return false;
|
||||
return WLDesktopPeer.isDesktopSupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DesktopPeer createDesktopPeer(Desktop target) {
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("Not implemented: WLToolkit.createDesktopPeer()");
|
||||
}
|
||||
return null;
|
||||
return new WLDesktopPeer();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1040,12 +1055,14 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNativeGTKAvailable() {
|
||||
return false;
|
||||
public void sync() {
|
||||
if(VKInstance.isVulkanEnabled()) {
|
||||
VKRenderQueue.sync();
|
||||
}
|
||||
flushImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sync() {
|
||||
public void flush() {
|
||||
flushImpl();
|
||||
}
|
||||
|
||||
|
||||
@@ -219,7 +219,7 @@ public final class WLVKGraphicsConfig extends WLGraphicsConfig
|
||||
|
||||
@Override
|
||||
public SurfaceData createSurfaceData(WLComponentPeer peer) {
|
||||
return WLVKSurfaceData.createData(peer);
|
||||
return new WLVKWindowSurfaceData(peer);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,8 +47,9 @@ public class WLVKVolatileSurfaceManager extends VolatileSurfaceManager {
|
||||
* if the image is not bitmask
|
||||
*/
|
||||
int transparency = vImg.getTransparency();
|
||||
// TODO: enable acceleration
|
||||
accelerationEnabled = false; // transparency != Transparency.BITMASK;
|
||||
|
||||
accelerationEnabled = VKInstance.isSurfaceDataAccelerated() &&
|
||||
transparency != Transparency.BITMASK;
|
||||
}
|
||||
|
||||
protected boolean isAccelerationEnabled() {
|
||||
@@ -60,7 +61,6 @@ public class WLVKVolatileSurfaceManager extends VolatileSurfaceManager {
|
||||
* of an existing window if this is a double buffered GraphicsConfig)
|
||||
*/
|
||||
protected SurfaceData initAcceleratedSurface() {
|
||||
/* TODO
|
||||
try {
|
||||
WLVKGraphicsConfig gc =
|
||||
(WLVKGraphicsConfig)vImg.getGraphicsConfig();
|
||||
@@ -71,15 +71,11 @@ public class WLVKVolatileSurfaceManager extends VolatileSurfaceManager {
|
||||
if (type == AccelSurface.UNDEFINED) {
|
||||
type = AccelSurface.RT_TEXTURE;
|
||||
}
|
||||
return WLVKSurfaceData.createData(gc,
|
||||
vImg.getWidth(),
|
||||
vImg.getHeight(),
|
||||
cm, vImg, type);
|
||||
return new VKOffScreenSurfaceData(
|
||||
gc, vImg, cm, type, vImg.getWidth(), vImg.getHeight());
|
||||
} catch (NullPointerException | OutOfMemoryError ignored) {
|
||||
return null;
|
||||
}
|
||||
*/
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, JetBrains s.r.o.. All rights reserved.
|
||||
* Copyright 2025 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@@ -28,39 +27,31 @@ package sun.java2d.vulkan;
|
||||
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.GraphicsConfiguration;
|
||||
import java.awt.GraphicsDevice;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
import sun.awt.image.BufImgSurfaceData;
|
||||
import sun.awt.wl.WLComponentPeer;
|
||||
import sun.java2d.SurfaceData;
|
||||
import sun.java2d.loops.Blit;
|
||||
import sun.java2d.loops.CompositeType;
|
||||
import sun.java2d.loops.SurfaceType;
|
||||
import sun.java2d.pipe.BufferedContext;
|
||||
import static sun.java2d.pipe.BufferedOpCodes.FLUSH_BUFFER;
|
||||
import sun.java2d.pipe.RenderBuffer;
|
||||
import sun.java2d.wl.WLPixelGrabberExt;
|
||||
import sun.java2d.wl.WLSurfaceDataExt;
|
||||
import sun.util.logging.PlatformLogger;
|
||||
|
||||
import static sun.java2d.pipe.BufferedOpCodes.FLUSH_BUFFER;
|
||||
import static sun.java2d.pipe.BufferedOpCodes.CONFIGURE_SURFACE;
|
||||
|
||||
public abstract class WLVKSurfaceData extends VKSurfaceData implements WLSurfaceDataExt, WLPixelGrabberExt {
|
||||
private static final PlatformLogger log = PlatformLogger.getLogger("sun.java2d.vulkan.WLVKSurfaceData");
|
||||
|
||||
public class WLVKWindowSurfaceData extends VKSurfaceData
|
||||
implements WLPixelGrabberExt, WLSurfaceDataExt
|
||||
{
|
||||
protected WLComponentPeer peer;
|
||||
|
||||
private native void initOps(int backgroundRGB);
|
||||
|
||||
private native void assignWlSurface(long surfacePtr);
|
||||
|
||||
protected WLVKSurfaceData(WLComponentPeer peer, WLVKGraphicsConfig gc,
|
||||
SurfaceType sType, ColorModel cm, int type)
|
||||
public WLVKWindowSurfaceData(WLComponentPeer peer)
|
||||
{
|
||||
super(gc, cm, type, 0, 0);
|
||||
super((VKGraphicsConfig) peer.getGraphicsConfiguration(), peer.getColorModel(), WINDOW, 0, 0);
|
||||
this.peer = peer;
|
||||
final int backgroundRGB = peer.getBackground() != null
|
||||
? peer.getBackground().getRGB()
|
||||
@@ -68,10 +59,46 @@ public abstract class WLVKSurfaceData extends VKSurfaceData implements WLSurface
|
||||
initOps(backgroundRGB);
|
||||
}
|
||||
|
||||
private void bufferAttached() {
|
||||
// Called from the native code when a buffer has just been attached to this surface
|
||||
// but the surface has not been committed yet.
|
||||
peer.updateSurfaceSize();
|
||||
public SurfaceData getReplacement() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNativeResource(int resType) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Rectangle getBounds() {
|
||||
Rectangle r = peer.getBufferBounds();
|
||||
r.x = r.y = 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns destination Component associated with this SurfaceData.
|
||||
*/
|
||||
public Object getDestination() {
|
||||
return peer.getTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDefaultScaleX() {
|
||||
return scale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDefaultScaleY() {
|
||||
return scale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedContext getContext() {
|
||||
return ((WLVKGraphicsConfig) getDeviceConfiguration()).getContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnScreen() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,6 +106,7 @@ public abstract class WLVKSurfaceData extends VKSurfaceData implements WLSurface
|
||||
assignWlSurface(surfacePtr);
|
||||
if (surfacePtr != 0) configure();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revalidate(int width, int height, int scale) {
|
||||
this.width = width;
|
||||
@@ -87,23 +115,6 @@ public abstract class WLVKSurfaceData extends VKSurfaceData implements WLSurface
|
||||
configure();
|
||||
}
|
||||
|
||||
private synchronized void configure() {
|
||||
VKRenderQueue rq = VKRenderQueue.getInstance();
|
||||
rq.lock();
|
||||
try {
|
||||
RenderBuffer buf = rq.getBuffer();
|
||||
rq.ensureCapacityAndAlignment(20, 4);
|
||||
buf.putInt(CONFIGURE_SURFACE);
|
||||
buf.putLong(getNativeOps());
|
||||
buf.putInt(width);
|
||||
buf.putInt(height);
|
||||
|
||||
rq.flushNow();
|
||||
} finally {
|
||||
rq.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void commit() {
|
||||
VKRenderQueue rq = VKRenderQueue.getInstance();
|
||||
@@ -119,38 +130,16 @@ public abstract class WLVKSurfaceData extends VKSurfaceData implements WLSurface
|
||||
rq.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnScreen() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphicsConfiguration getDeviceConfiguration() {
|
||||
return peer.getGraphicsConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a SurfaceData object representing surface of on-screen Window.
|
||||
*/
|
||||
public static WLVKWindowSurfaceData createData(WLComponentPeer peer) {
|
||||
WLVKGraphicsConfig gc = getGC(peer);
|
||||
return new WLVKWindowSurfaceData(peer, gc);
|
||||
private void bufferAttached() {
|
||||
// Called from the native code when a buffer has just been attached to this surface
|
||||
// but the surface has not been committed yet.
|
||||
peer.updateSurfaceSize();
|
||||
}
|
||||
|
||||
public static WLVKGraphicsConfig getGC(WLComponentPeer peer) {
|
||||
if (peer != null) {
|
||||
return (WLVKGraphicsConfig) peer.getGraphicsConfiguration();
|
||||
} else {
|
||||
// REMIND: this should rarely (never?) happen, but what if
|
||||
// default config is not WLVK?
|
||||
GraphicsEnvironment env =
|
||||
GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
GraphicsDevice gd = env.getDefaultScreenDevice();
|
||||
return (WLVKGraphicsConfig)gd.getDefaultConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
public int getRGBPixelAt(int x, int y) {
|
||||
Rectangle r = peer.getBufferBounds();
|
||||
if (x < r.x || x >= r.x + r.width || y < r.y || y >= r.y + r.height) {
|
||||
@@ -163,7 +152,7 @@ public abstract class WLVKSurfaceData extends VKSurfaceData implements WLSurface
|
||||
Blit blit = Blit.getFromCache(getSurfaceType(), CompositeType.SrcNoEa,
|
||||
resData.getSurfaceType());
|
||||
blit.Blit(this, resData, AlphaComposite.Src, null,
|
||||
x, y, 0, 0, 1, 1);
|
||||
x, y, 0, 0, 1, 1);
|
||||
|
||||
return resImg.getRGB(0, 0);
|
||||
}
|
||||
@@ -194,54 +183,4 @@ public abstract class WLVKSurfaceData extends VKSurfaceData implements WLSurface
|
||||
resImg.getRGB(0, 0, b.width, b.height, pixels, 0, b.width);
|
||||
return pixels;
|
||||
}
|
||||
|
||||
|
||||
public static class WLVKWindowSurfaceData extends WLVKSurfaceData {
|
||||
public WLVKWindowSurfaceData(WLComponentPeer peer, WLVKGraphicsConfig gc)
|
||||
{
|
||||
super(peer, gc, gc.getSurfaceType(), peer.getColorModel(), WINDOW);
|
||||
}
|
||||
|
||||
public SurfaceData getReplacement() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNativeResource(int resType) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Rectangle getBounds() {
|
||||
Rectangle r = peer.getBufferBounds();
|
||||
r.x = r.y = 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns destination Component associated with this SurfaceData.
|
||||
*/
|
||||
public Object getDestination() {
|
||||
return peer.getTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDefaultScaleX() {
|
||||
return scale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDefaultScaleY() {
|
||||
return scale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedContext getContext() {
|
||||
return ((WLVKGraphicsConfig) getDeviceConfiguration()).getContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnScreen() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@@ -35,8 +35,15 @@ static void WLVKSurfaceData_OnResize(VKWinSDOps* surface, VkExtent2D extent) {
|
||||
JNU_CallMethodByName(env, NULL, surface->vksdOps.sdOps.sdObject, "bufferAttached", "()V");
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_WLVKSurfaceData_initOps(JNIEnv *env, jobject vksd, jint backgroundRGB) {
|
||||
J2dTraceLn1(J2D_TRACE_VERBOSE, "WLVKSurfaceData_initOps(%p)", vksd);
|
||||
/*
|
||||
* Class: sun_java2d_vulkan_WLVKSurfaceData_WLVKWindowSurfaceData
|
||||
* Method: initOps
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_WLVKWindowSurfaceData_initOps(
|
||||
JNIEnv *env, jobject vksd, jint backgroundRGB)
|
||||
{
|
||||
J2dTraceLn1(J2D_TRACE_VERBOSE, "WLVKWindowsSurfaceData_initOps(%p)", vksd);
|
||||
VKWinSDOps* sd = (VKWinSDOps*)SurfaceData_InitOps(env, vksd, sizeof(VKWinSDOps));
|
||||
if (sd == NULL) {
|
||||
JNU_ThrowOutOfMemoryError(env, "Initialization of SurfaceData failed.");
|
||||
@@ -48,17 +55,28 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_WLVKSurfaceData_initOps(JNIEnv *en
|
||||
VKSD_ResetSurface(&sd->vksdOps);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_WLVKSurfaceData_assignWlSurface(JNIEnv *env, jobject vksd, jlong wlSurfacePtr) {
|
||||
J2dRlsTraceLn2(J2D_TRACE_INFO, "WLVKSurfaceData_assignWlSurface(%p): wl_surface=%p", (void*)vksd, wlSurfacePtr);
|
||||
/*
|
||||
* Class: sun_java2d_vulkan_WLVKSurfaceData_WLVKWindowSurfaceData
|
||||
* Method: assignWlSurface
|
||||
* Signature: (J)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_WLVKWindowSurfaceData_assignWlSurface(
|
||||
JNIEnv *env, jobject vksd, jlong wlSurfacePtr)
|
||||
{
|
||||
VKWinSDOps* sd = (VKWinSDOps*)SurfaceData_GetOps(env, vksd);
|
||||
J2dRlsTraceLn2(J2D_TRACE_INFO, "WLVKWindowsSurfaceData_assignWlSurface(%p): wl_surface=%p",
|
||||
(void*)sd, wlSurfacePtr);
|
||||
|
||||
if (sd == NULL) {
|
||||
J2dRlsTraceLn1(J2D_TRACE_ERROR, "WLVKSurfaceData_assignWlSurface(%p): VKWinSDOps is NULL", vksd);
|
||||
J2dRlsTraceLn1(J2D_TRACE_ERROR,
|
||||
"WLVKWindowSurfaceData_assignWlSurface(%p): VKWinSDOps is NULL", vksd);
|
||||
VK_UNHANDLED_ERROR();
|
||||
}
|
||||
|
||||
if (sd->surface != VK_NULL_HANDLE) {
|
||||
VKSD_ResetSurface(&sd->vksdOps);
|
||||
J2dRlsTraceLn1(J2D_TRACE_INFO, "WLVKSurfaceData_assignWlSurface(%p): surface reset", vksd);
|
||||
J2dRlsTraceLn1(J2D_TRACE_INFO,
|
||||
"WLVKWindowSurfaceData_assignWlSurface(%p): surface reset", vksd);
|
||||
}
|
||||
|
||||
struct wl_surface* wl_surface = (struct wl_surface*)jlong_to_ptr(wlSurfacePtr);
|
||||
@@ -73,7 +91,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_WLVKSurfaceData_assignWlSurface(JN
|
||||
VK_IF_ERROR(ge->vkCreateWaylandSurfaceKHR(ge->vkInstance, &surfaceCreateInfo, NULL, &sd->surface)) {
|
||||
VK_UNHANDLED_ERROR();
|
||||
}
|
||||
J2dRlsTraceLn1(J2D_TRACE_INFO, "WLVKSurfaceData_assignWlSurface(%p): surface created", vksd);
|
||||
J2dRlsTraceLn1(J2D_TRACE_INFO,
|
||||
"WLVKWindowSurfaceData_assignWlSurface(%p): surface created", vksd);
|
||||
// Swapchain will be created later after CONFIGURE_SURFACE.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 JetBrains s.r.o.
|
||||
// Copyright 2023-2025 JetBrains s.r.o.
|
||||
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
//
|
||||
// This code is free software; you can redistribute it and/or modify it
|
||||
@@ -27,6 +27,8 @@
|
||||
|
||||
#include "WLKeyboard.h"
|
||||
#include "WLToolkit.h"
|
||||
#include "java_awt_event_InputEvent.h"
|
||||
|
||||
#include <sun_awt_wl_WLKeyboard.h>
|
||||
|
||||
#include <stdint.h>
|
||||
@@ -48,6 +50,15 @@ extern JNIEnv *getEnv();
|
||||
#define XKB_MOD_NAME_NUM "Mod2"
|
||||
#define XKB_MOD_NAME_LOGO "Mod4"
|
||||
|
||||
#define XKB_SHIFT_MASK (1 << 0)
|
||||
#define XKB_CAPS_LOCK_MASK (1 << 1)
|
||||
#define XKB_CTRL_MASK (1 << 2)
|
||||
#define XKB_ALT_MASK (1 << 3)
|
||||
#define XKB_NUM_LOCK_MASK (1 << 4)
|
||||
#define XKB_MOD3_MASK (1 << 5)
|
||||
#define XKB_META_MASK (1 << 6)
|
||||
#define XKB_MOD5_MASK (1 << 7)
|
||||
|
||||
#define XKB_LED_NAME_CAPS "Caps Lock"
|
||||
#define XKB_LED_NAME_NUM "Num Lock"
|
||||
#define XKB_LED_NAME_SCROLL "Scroll Lock"
|
||||
@@ -201,7 +212,7 @@ static struct WLKeyboardState {
|
||||
|
||||
// Report KEY_PRESS/KEY_RELEASE events on non-ASCII capable layouts
|
||||
// as if they happen on the QWERTY layout
|
||||
bool useNationalLayouts;
|
||||
bool reportNonAsciiAsQwerty;
|
||||
|
||||
// Report dead keys not as KeyEvent.VK_DEAD_something, but as the corresponding 'normal' Java keycode
|
||||
bool reportDeadKeysAsNormal;
|
||||
@@ -1026,6 +1037,7 @@ convertDeadKey(xkb_keysym_t keysym, enum ConvertDeadKeyType type) {
|
||||
}
|
||||
|
||||
enum TranslateKeycodeType {
|
||||
TRANSLATE_USING_ACTIVE_STATE,
|
||||
TRANSLATE_USING_ACTIVE_LAYOUT,
|
||||
TRANSLATE_USING_QWERTY,
|
||||
};
|
||||
@@ -1043,22 +1055,65 @@ translateKeycodeToKeysym(uint32_t keycode, enum TranslateKeycodeType type) {
|
||||
const uint32_t xkbKeycode = keycode + 8;
|
||||
|
||||
struct xkb_state *state;
|
||||
xkb_layout_index_t group;
|
||||
|
||||
if (keyboard.qwertyKeymap && type == TRANSLATE_USING_QWERTY) {
|
||||
state = keyboard.tmpQwertyState;
|
||||
group = 0;
|
||||
if (type == TRANSLATE_USING_ACTIVE_STATE) {
|
||||
state = keyboard.state;
|
||||
} else {
|
||||
state = keyboard.tmpState;
|
||||
group = getKeyboardLayoutIndex();
|
||||
xkb_layout_index_t group;
|
||||
bool numLock;
|
||||
|
||||
if (keyboard.qwertyKeymap && type == TRANSLATE_USING_QWERTY) {
|
||||
state = keyboard.tmpQwertyState;
|
||||
group = 0;
|
||||
numLock = true;
|
||||
} else {
|
||||
state = keyboard.tmpState;
|
||||
group = getKeyboardLayoutIndex();
|
||||
numLock = xkb.state_mod_name_is_active(keyboard.state, XKB_MOD_NAME_NUM, XKB_STATE_MODS_EFFECTIVE) == 1;
|
||||
}
|
||||
|
||||
xkb.state_update_mask(state, 0, 0, numLock ? XKB_NUM_LOCK_MASK : 0, 0, 0, group);
|
||||
}
|
||||
|
||||
bool numLock = xkb.state_mod_name_is_active(keyboard.state, XKB_MOD_NAME_NUM, XKB_STATE_MODS_EFFECTIVE) == 1;
|
||||
xkb.state_update_mask(state, 0, 0, numLock ? sun_awt_wl_WLKeyboard_XKB_NUM_LOCK_MASK : 0, 0, 0, group);
|
||||
|
||||
return xkb.state_key_get_one_sym(state, xkbKeycode);
|
||||
}
|
||||
|
||||
static bool
|
||||
isFunctionKeysym(xkb_keysym_t keysym) {
|
||||
// https://www.x.org/releases/current/doc/xproto/x11protocol.html#keysym_encoding
|
||||
// https://www.x.org/releases/X11R7.7/doc/kbproto/xkbproto.html#New_KeySyms
|
||||
// I think it's enough to check if the keysym belongs to the "Keyboard" set of function keys.
|
||||
return keysym >= 0xff00 && keysym <= 0xffff;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
getXKBModifiers(void) {
|
||||
return xkb.state_serialize_mods(keyboard.state, XKB_STATE_MODS_EFFECTIVE);
|
||||
}
|
||||
|
||||
static int
|
||||
convertXKBModifiersToJavaModifiers(xkb_mod_mask_t mask) {
|
||||
int result = 0;
|
||||
|
||||
if ((mask & XKB_SHIFT_MASK) != 0) {
|
||||
result |= java_awt_event_InputEvent_SHIFT_DOWN_MASK;
|
||||
}
|
||||
|
||||
if ((mask & XKB_CTRL_MASK) != 0) {
|
||||
result |= java_awt_event_InputEvent_CTRL_DOWN_MASK;
|
||||
}
|
||||
|
||||
if ((mask & XKB_ALT_MASK) != 0) {
|
||||
result |= java_awt_event_InputEvent_ALT_DOWN_MASK;
|
||||
}
|
||||
|
||||
if ((mask & XKB_META_MASK) != 0) {
|
||||
result |= java_awt_event_InputEvent_META_DOWN_MASK;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
convertKeysymToJavaCode(xkb_keysym_t keysym, int *javaKeyCode, int *javaKeyLocation) {
|
||||
if (javaKeyCode) {
|
||||
@@ -1269,19 +1324,21 @@ handleKey(long serial, long timestamp, uint32_t keycode, bool isPressed, bool is
|
||||
|
||||
xkb_keycode_t xkbKeycode = keycode + 8;
|
||||
bool keyRepeats = xkb.keymap_key_repeats(keyboard.keymap, xkbKeycode);
|
||||
xkb_keysym_t keysym = translateKeycodeToKeysym(keycode, TRANSLATE_USING_ACTIVE_LAYOUT);
|
||||
xkb_mod_mask_t consumedModifiers = xkb.state_key_get_consumed_mods2(keyboard.state, xkbKeycode, XKB_CONSUMED_MODE_GTK);
|
||||
xkb_keysym_t actualKeysym = translateKeycodeToKeysym(keycode, TRANSLATE_USING_ACTIVE_STATE);
|
||||
xkb_keysym_t noModsKeysym = translateKeycodeToKeysym(keycode, TRANSLATE_USING_ACTIVE_LAYOUT);
|
||||
xkb_keysym_t qwertyKeysym = translateKeycodeToKeysym(keycode, TRANSLATE_USING_QWERTY);
|
||||
|
||||
#ifdef WL_KEYBOARD_DEBUG
|
||||
char buf[256];
|
||||
xkb.keysym_get_name(keysym, buf, sizeof buf);
|
||||
fprintf(stderr, "handleKey: keysym = %d (%s)\n", keysym, buf);
|
||||
xkb.keysym_get_name(actualKeysym, buf, sizeof buf);
|
||||
fprintf(stderr, "handleKey: actualKeysym = %d (%s)\n", actualKeysym, buf);
|
||||
xkb.keysym_get_name(noModsKeysym, buf, sizeof buf);
|
||||
fprintf(stderr, "handleKey: noModsKeysym = %d (%s)\n", noModsKeysym, buf);
|
||||
xkb.keysym_get_name(qwertyKeysym, buf, sizeof buf);
|
||||
fprintf(stderr, "handleKey: qwertyKeysym = %d (%s)\n", qwertyKeysym, buf);
|
||||
#endif
|
||||
|
||||
int javaKeyCode, javaExtendedKeyCode, javaKeyLocation;
|
||||
|
||||
// If the national layouts support is enabled, and the current keyboard is not ascii-capable,
|
||||
// we need to set the extended key code properly.
|
||||
//
|
||||
@@ -1292,19 +1349,33 @@ handleKey(long serial, long timestamp, uint32_t keycode, bool isPressed, bool is
|
||||
// swap will be lost when attempting to translate what they're typing on the non-ascii-capable
|
||||
// layout to the QWERTY key map. Hence, the 'qwertyKeysym <= 0x7f' check.
|
||||
|
||||
if (keyboard.useNationalLayouts && !keyboard.asciiCapable && qwertyKeysym <= 0x7f) {
|
||||
int javaKeyCode = java_awt_event_KeyEvent_VK_UNDEFINED;
|
||||
int javaExtendedKeyCode = java_awt_event_KeyEvent_VK_UNDEFINED;
|
||||
int javaKeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_STANDARD;
|
||||
xkb_keysym_t reportedKeysym = noModsKeysym;
|
||||
xkb_mod_mask_t modifiers = getXKBModifiers();
|
||||
|
||||
bool reportQwerty = keyboard.reportNonAsciiAsQwerty && !keyboard.asciiCapable && qwertyKeysym <= 0x7f;
|
||||
|
||||
if (isFunctionKeysym(actualKeysym) && actualKeysym != noModsKeysym) {
|
||||
// Emulating pressing a function key, for example AltGr+F being mapped to Right on German Neo 2
|
||||
modifiers &= ~consumedModifiers;
|
||||
reportedKeysym = actualKeysym;
|
||||
reportQwerty = false;
|
||||
}
|
||||
|
||||
if (reportQwerty) {
|
||||
convertKeysymToJavaCode(qwertyKeysym, &javaKeyCode, &javaKeyLocation);
|
||||
javaExtendedKeyCode = javaKeyCode;
|
||||
} else {
|
||||
xkb_keysym_t report = keysym;
|
||||
if (keyboard.reportDeadKeysAsNormal) {
|
||||
xkb_keysym_t converted = convertDeadKey(keysym, CONVERT_TO_NON_COMBINING);
|
||||
xkb_keysym_t converted = convertDeadKey(reportedKeysym, CONVERT_TO_NON_COMBINING);
|
||||
if (converted != 0) {
|
||||
report = converted;
|
||||
reportedKeysym = converted;
|
||||
}
|
||||
}
|
||||
|
||||
convertKeysymToJavaCode(report, &javaExtendedKeyCode, &javaKeyLocation);
|
||||
convertKeysymToJavaCode(reportedKeysym, &javaExtendedKeyCode, &javaKeyLocation);
|
||||
if (javaExtendedKeyCode >= 0x1000000 && !keyboard.reportJavaKeyCodeForActiveLayout) {
|
||||
convertKeysymToJavaCode(qwertyKeysym, &javaKeyCode, NULL);
|
||||
} else {
|
||||
@@ -1312,8 +1383,11 @@ handleKey(long serial, long timestamp, uint32_t keycode, bool isPressed, bool is
|
||||
}
|
||||
}
|
||||
|
||||
int javaModifiers = convertXKBModifiersToJavaModifiers(modifiers);
|
||||
|
||||
#ifdef WL_KEYBOARD_DEBUG
|
||||
fprintf(stderr, "handleKey: javaKeyCode = %d\n", javaKeyCode);
|
||||
fprintf(stderr, "handleKey: javaExtendedKeyCode = %d\n", javaExtendedKeyCode);
|
||||
#endif
|
||||
|
||||
struct WLKeyEvent event = {
|
||||
@@ -1325,6 +1399,7 @@ handleKey(long serial, long timestamp, uint32_t keycode, bool isPressed, bool is
|
||||
.rawCode = (int)xkbKeycode,
|
||||
.extendedKeyCode = javaExtendedKeyCode,
|
||||
.keyChar = getJavaKeyCharForKeycode(xkbKeycode),
|
||||
.modifiers = javaModifiers,
|
||||
};
|
||||
|
||||
wlPostKeyEvent(&event);
|
||||
@@ -1433,7 +1508,7 @@ Java_sun_awt_wl_WLKeyboard_initialize(JNIEnv *env, jobject instance, jobject key
|
||||
return;
|
||||
}
|
||||
|
||||
keyboard.useNationalLayouts = true;
|
||||
keyboard.reportNonAsciiAsQwerty = true;
|
||||
keyboard.remapExtraKeycodes = true;
|
||||
keyboard.reportDeadKeysAsNormal = false;
|
||||
keyboard.reportJavaKeyCodeForActiveLayout = true;
|
||||
@@ -1462,9 +1537,18 @@ Java_sun_awt_wl_WLKeyboard_cancelCompose(JNIEnv *env, jobject instance) {
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_awt_wl_WLKeyboard_getXKBModifiersMask(JNIEnv *env, jobject instance) {
|
||||
xkb_mod_mask_t mods = xkb.state_serialize_mods(keyboard.state, XKB_STATE_MODS_EFFECTIVE);
|
||||
return (jint) mods;
|
||||
Java_sun_awt_wl_WLKeyboard_getModifiers(JNIEnv *env, jobject instance) {
|
||||
return convertXKBModifiersToJavaModifiers(getXKBModifiers());
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_sun_awt_wl_WLKeyboard_isCapsLockPressed(JNIEnv *env, jobject instance) {
|
||||
return (getXKBModifiers() & XKB_CAPS_LOCK_MASK) != 0;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_sun_awt_wl_WLKeyboard_isNumLockPressed(JNIEnv *env, jobject instance) {
|
||||
return (getXKBModifiers() & XKB_NUM_LOCK_MASK) != 0;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 JetBrains s.r.o.
|
||||
// Copyright 2023-2025 JetBrains s.r.o.
|
||||
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
//
|
||||
// This code is free software; you can redistribute it and/or modify it
|
||||
@@ -40,6 +40,7 @@ struct WLKeyEvent {
|
||||
int rawCode;
|
||||
int extendedKeyCode;
|
||||
uint16_t keyChar;
|
||||
int modifiers;
|
||||
};
|
||||
|
||||
void wlSetKeymap(const char* serializedKeymap);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2022-2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022-2024, JetBrains s.r.o.. All rights reserved.
|
||||
* Copyright (c) 2022-2025, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@@ -443,7 +443,8 @@ wlPostKeyEvent(const struct WLKeyEvent* event)
|
||||
event->keyLocation,
|
||||
event->rawCode,
|
||||
event->extendedKeyCode,
|
||||
event->keyChar
|
||||
event->keyChar,
|
||||
event->modifiers
|
||||
);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
}
|
||||
@@ -691,7 +692,7 @@ initJavaRefs(JNIEnv *env, jclass clazz)
|
||||
JNI_FALSE);
|
||||
CHECK_NULL_RETURN(dispatchKeyboardKeyEventMID = (*env)->GetStaticMethodID(env, tkClass,
|
||||
"dispatchKeyboardKeyEvent",
|
||||
"(JJIIIIIC)V"),
|
||||
"(JJIIIIICI)V"),
|
||||
JNI_FALSE);
|
||||
CHECK_NULL_RETURN(dispatchKeyboardModifiersEventMID = (*env)->GetStaticMethodID(env, tkClass,
|
||||
"dispatchKeyboardModifiersEvent",
|
||||
@@ -1130,23 +1131,6 @@ Java_java_awt_Event_initIDs(JNIEnv *env, jclass cls)
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_SunToolkit_closeSplashScreen(JNIEnv *env, jclass cls)
|
||||
{
|
||||
typedef void (*SplashClose_t)();
|
||||
SplashClose_t splashClose;
|
||||
void* hSplashLib = dlopen(0, RTLD_LAZY);
|
||||
if (!hSplashLib) {
|
||||
return;
|
||||
}
|
||||
splashClose = (SplashClose_t)dlsym(hSplashLib,
|
||||
"SplashClose");
|
||||
if (splashClose) {
|
||||
splashClose();
|
||||
}
|
||||
dlclose(hSplashLib);
|
||||
}
|
||||
|
||||
void awt_output_flush()
|
||||
{
|
||||
wlFlushToServer(getEnv());
|
||||
|
||||
132
src/java.desktop/unix/native/libawt_wlawt/awt_Desktop.c
Normal file
132
src/java.desktop/unix/native/libawt_wlawt/awt_Desktop.c
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#ifdef HEADLESS
|
||||
#error This file should not be included in headless library
|
||||
#endif
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <jni.h>
|
||||
#include <jvm_md.h>
|
||||
|
||||
#include "Trace.h"
|
||||
#include "jni_util.h"
|
||||
#include "gtk_interface.h"
|
||||
|
||||
typedef gboolean (GNOME_URL_SHOW_TYPE)(const char *, void **);
|
||||
typedef gboolean (GNOME_VFS_INIT_TYPE)(void);
|
||||
|
||||
static GNOME_URL_SHOW_TYPE *gnome_url_show = NULL;
|
||||
|
||||
static gboolean gnome_load(void) {
|
||||
void *vfs_handle;
|
||||
void *gnome_handle;
|
||||
const char *errmsg;
|
||||
GNOME_VFS_INIT_TYPE *gnome_vfs_init;
|
||||
|
||||
vfs_handle = dlopen(VERSIONED_JNI_LIB_NAME("gnomevfs-2", "0"), RTLD_LAZY);
|
||||
if (vfs_handle == NULL) {
|
||||
vfs_handle = dlopen(JNI_LIB_NAME("gnomevfs-2"), RTLD_LAZY);
|
||||
if (vfs_handle == NULL) {
|
||||
J2dTraceLn(J2D_TRACE_ERROR, "can not load libgnomevfs-2.so");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
dlerror(); /* Clear errors */
|
||||
gnome_vfs_init = dlsym(vfs_handle, "gnome_vfs_init");
|
||||
if (gnome_vfs_init == NULL){
|
||||
J2dTraceLn(J2D_TRACE_ERROR, "dlsym( gnome_vfs_init) returned NULL");
|
||||
return FALSE;
|
||||
}
|
||||
if ((errmsg = dlerror()) != NULL) {
|
||||
J2dTraceLn1(J2D_TRACE_ERROR, "can not find symbol gnome_vfs_init %s", errmsg);
|
||||
return FALSE;
|
||||
}
|
||||
// call gonme_vfs_init()
|
||||
(*gnome_vfs_init)();
|
||||
|
||||
gnome_handle = dlopen(VERSIONED_JNI_LIB_NAME("gnome-2", "0"), RTLD_LAZY);
|
||||
if (gnome_handle == NULL) {
|
||||
gnome_handle = dlopen(JNI_LIB_NAME("gnome-2"), RTLD_LAZY);
|
||||
if (gnome_handle == NULL) {
|
||||
J2dTraceLn(J2D_TRACE_ERROR, "can not load libgnome-2.so");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
dlerror(); /* Clear errors */
|
||||
gnome_url_show = dlsym(gnome_handle, "gnome_url_show");
|
||||
if ((errmsg = dlerror()) != NULL) {
|
||||
J2dTraceLn(J2D_TRACE_ERROR, "can not find symble gnome_url_show");
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean gtk_has_been_loaded = FALSE;
|
||||
static gboolean gnome_has_been_loaded = FALSE;
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_sun_awt_wl_WLDesktopPeer_init
|
||||
(JNIEnv *env, jclass cls, jint version, jboolean verbose)
|
||||
{
|
||||
if (gtk_has_been_loaded || gnome_has_been_loaded) {
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
if (gtk_load(env, version, verbose) && gtk->show_uri_load(env)) {
|
||||
gtk_has_been_loaded = TRUE;
|
||||
return JNI_TRUE;
|
||||
} else if (gnome_load()) {
|
||||
gnome_has_been_loaded = TRUE;
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_sun_awt_wl_WLDesktopPeer_gnome_1url_1show
|
||||
(JNIEnv *env, jobject obj, jbyteArray url_j)
|
||||
{
|
||||
gboolean success = FALSE;
|
||||
const gchar* url_c = (gchar*)(*env)->GetByteArrayElements(env, url_j, NULL);
|
||||
if (url_c == NULL) {
|
||||
JNU_ThrowOutOfMemoryError(env, 0);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
if (gtk_has_been_loaded) {
|
||||
gtk->gdk_threads_enter();
|
||||
success = gtk->gtk_show_uri_on_window(NULL, url_c, GDK_CURRENT_TIME, NULL);
|
||||
gtk->gdk_threads_leave();
|
||||
} else if (gnome_has_been_loaded) {
|
||||
success = (*gnome_url_show)(url_c, NULL);
|
||||
}
|
||||
|
||||
(*env)->ReleaseByteArrayElements(env, url_j, (jbyte*)url_c, 0);
|
||||
|
||||
return success ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#ifdef HEADLESS
|
||||
#error This file should not be included in headless library
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <jni.h>
|
||||
#include <jni_util.h>
|
||||
|
||||
#include "gtk_interface.h"
|
||||
#include "sun_awt_wl_GtkFileDialogPeer.h"
|
||||
#include "java_awt_FileDialog.h"
|
||||
#include "debug_assert.h"
|
||||
|
||||
typedef void GtkWidget;
|
||||
static JavaVM *jvm;
|
||||
|
||||
static jmethodID filenameFilterCallbackMethodID = NULL;
|
||||
static jmethodID setFileInternalMethodID = NULL;
|
||||
static jfieldID widgetFieldID = NULL;
|
||||
static jmethodID setWindowMethodID = NULL;
|
||||
static jmethodID onCloseMethodID = NULL;
|
||||
|
||||
JNIEXPORT void JNICALL Java_sun_awt_wl_GtkFileDialogPeer_initIDs
|
||||
(JNIEnv *env, jclass cx)
|
||||
{
|
||||
filenameFilterCallbackMethodID = (*env)->GetMethodID(env, cx,
|
||||
"filenameFilterCallback", "(Ljava/lang/String;)Z");
|
||||
DASSERT(filenameFilterCallbackMethodID != NULL);
|
||||
CHECK_NULL(filenameFilterCallbackMethodID);
|
||||
|
||||
setFileInternalMethodID = (*env)->GetMethodID(env, cx,
|
||||
"setFileInternal", "(Ljava/lang/String;[Ljava/lang/String;)V");
|
||||
DASSERT(setFileInternalMethodID != NULL);
|
||||
CHECK_NULL(setFileInternalMethodID);
|
||||
|
||||
widgetFieldID = (*env)->GetFieldID(env, cx, "widget", "J");
|
||||
DASSERT(widgetFieldID != NULL);
|
||||
CHECK_NULL(widgetFieldID);
|
||||
|
||||
setWindowMethodID = (*env)->GetMethodID(env, cx, "setWindow", "()Z");
|
||||
DASSERT(setWindowMethodID != NULL);
|
||||
|
||||
onCloseMethodID = (*env)->GetMethodID(env, cx, "onClose", "()V");
|
||||
DASSERT(onCloseMethodID != NULL);
|
||||
}
|
||||
|
||||
static gboolean filenameFilterCallback(const GtkFileFilterInfo * filter_info, gpointer obj)
|
||||
{
|
||||
JNIEnv *env;
|
||||
jstring filename;
|
||||
|
||||
env = (JNIEnv *) JNU_GetEnv(jvm, JNI_VERSION_1_2);
|
||||
|
||||
filename = (*env)->NewStringUTF(env, filter_info->filename);
|
||||
JNU_CHECK_EXCEPTION_RETURN(env, FALSE);
|
||||
|
||||
return (*env)->CallBooleanMethod(env, obj, filenameFilterCallbackMethodID, filename);
|
||||
}
|
||||
|
||||
static void quit(JNIEnv * env, jobject jpeer, gboolean isSignalHandler)
|
||||
{
|
||||
jthrowable pendingException;
|
||||
if ((pendingException = (*env)->ExceptionOccurred(env)) != NULL) {
|
||||
(*env)->ExceptionClear(env);
|
||||
}
|
||||
|
||||
GtkWidget * dialog = (GtkWidget*)jlong_to_ptr(
|
||||
(*env)->GetLongField(env, jpeer, widgetFieldID));
|
||||
|
||||
(*env)->CallVoidMethod(env, jpeer, onCloseMethodID);
|
||||
|
||||
if (dialog != NULL) {
|
||||
// Callbacks from GTK signals are made within the GTK lock
|
||||
// So, within a signal handler there is no need to call
|
||||
// gdk_threads_enter() / gtk->gdk_threads_leave()
|
||||
if (!isSignalHandler) {
|
||||
gtk->gdk_threads_enter();
|
||||
}
|
||||
|
||||
gtk->gtk_widget_hide (dialog);
|
||||
gtk->gtk_widget_destroy (dialog);
|
||||
|
||||
gtk->gtk_main_quit ();
|
||||
|
||||
if (!isSignalHandler) {
|
||||
gtk->gdk_threads_leave();
|
||||
}
|
||||
}
|
||||
|
||||
if (pendingException) {
|
||||
(*env)->Throw(env, pendingException);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_sun_awt_wl_GtkFileDialogPeer_quit
|
||||
(JNIEnv * env, jobject jpeer)
|
||||
{
|
||||
quit(env, jpeer, FALSE);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_sun_awt_wl_GtkFileDialogPeer_toFrontImpl
|
||||
(JNIEnv * env, jobject jpeer, jlong timestamp)
|
||||
{
|
||||
gtk->gdk_threads_enter();
|
||||
|
||||
GtkWidget * dialog = jlong_to_ptr((*env)->GetLongField(env, jpeer, widgetFieldID));
|
||||
if (dialog != NULL) {
|
||||
gtk->gtk_window_present_with_time((GtkWindow*)dialog, (guint32)timestamp);
|
||||
}
|
||||
|
||||
gtk->gdk_threads_leave();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_sun_awt_wl_GtkFileDialogPeer_setBounds
|
||||
(JNIEnv * env, jobject jpeer, jint x, jint y, jint width, jint height, jint op)
|
||||
{
|
||||
gtk->gdk_threads_enter();
|
||||
|
||||
GtkWindow* dialog = jlong_to_ptr((*env)->GetLongField(env, jpeer, widgetFieldID));
|
||||
if (dialog != NULL) {
|
||||
// Note: gtk_window_move() will not work in Wayland anyway, so the coordinates
|
||||
// are ignored intentionally here.
|
||||
if (width > 0 && height > 0) {
|
||||
gtk->gtk_window_resize(dialog, (gint)width, (gint)height);
|
||||
}
|
||||
}
|
||||
|
||||
gtk->gdk_threads_leave();
|
||||
}
|
||||
|
||||
/*
|
||||
* baseDir should be freed by user.
|
||||
*/
|
||||
static gboolean isFromSameDirectory(GSList* list, gchar** baseDir) {
|
||||
|
||||
GSList *it = list;
|
||||
gchar* prevDir = NULL;
|
||||
gboolean isAllDirsSame = TRUE;
|
||||
|
||||
while (it) {
|
||||
gchar* dir = gtk->g_path_get_dirname((gchar*) it->data);
|
||||
|
||||
if (prevDir && strcmp(prevDir, dir) != 0) {
|
||||
isAllDirsSame = FALSE;
|
||||
gtk->g_free(dir);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!prevDir) {
|
||||
prevDir = strdup(dir);
|
||||
}
|
||||
gtk->g_free(dir);
|
||||
|
||||
it = it->next;
|
||||
}
|
||||
|
||||
if (isAllDirsSame) {
|
||||
*baseDir = prevDir;
|
||||
} else {
|
||||
free(prevDir);
|
||||
*baseDir = strdup("/");
|
||||
}
|
||||
|
||||
return isAllDirsSame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a GSList to an array of filenames
|
||||
*/
|
||||
static jobjectArray toFilenamesArray(JNIEnv *env, GSList* list, jstring* jcurrent_folder)
|
||||
{
|
||||
jstring str;
|
||||
jclass stringCls;
|
||||
GSList *iterator;
|
||||
jobjectArray array;
|
||||
int i;
|
||||
gchar* entry;
|
||||
gchar * baseDir;
|
||||
gboolean isFromSameDir;
|
||||
|
||||
if (list == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stringCls = (*env)->FindClass(env, "java/lang/String");
|
||||
if (stringCls == NULL) {
|
||||
(*env)->ExceptionClear(env);
|
||||
JNU_ThrowInternalError(env, "Could not get java.lang.String class");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
array = (*env)->NewObjectArray(env, gtk->gtk_g_slist_length(list), stringCls, NULL);
|
||||
if (array == NULL) {
|
||||
(*env)->ExceptionClear(env);
|
||||
JNU_ThrowInternalError(env, "Could not instantiate array files array");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
isFromSameDir = isFromSameDirectory(list, &baseDir);
|
||||
|
||||
*jcurrent_folder = (*env)->NewStringUTF(env, baseDir);
|
||||
if (*jcurrent_folder == NULL) {
|
||||
free(baseDir);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (iterator = list, i=0; iterator; iterator = iterator->next, i++) {
|
||||
entry = (gchar*) iterator->data;
|
||||
if (isFromSameDir) {
|
||||
entry = strrchr(entry, '/') + 1;
|
||||
} else if (entry[0] == '/') {
|
||||
entry++;
|
||||
}
|
||||
|
||||
str = (*env)->NewStringUTF(env, entry);
|
||||
if((*env)->ExceptionCheck(env)){
|
||||
break;
|
||||
}
|
||||
if (str) {
|
||||
(*env)->SetObjectArrayElement(env, array, i, str);
|
||||
if((*env)->ExceptionCheck(env)){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(baseDir);
|
||||
return array;
|
||||
}
|
||||
|
||||
static void handle_response(GtkWidget* aDialog, gint responseId, gpointer obj)
|
||||
{
|
||||
JNIEnv *env;
|
||||
GSList *filenames;
|
||||
jstring jcurrent_folder = NULL;
|
||||
jobjectArray jfilenames;
|
||||
|
||||
env = (JNIEnv *) JNU_GetEnv(jvm, JNI_VERSION_1_2);
|
||||
filenames = NULL;
|
||||
|
||||
if (responseId == GTK_RESPONSE_ACCEPT) {
|
||||
filenames = gtk->gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(aDialog));
|
||||
}
|
||||
|
||||
jfilenames = toFilenamesArray(env, filenames, &jcurrent_folder);
|
||||
|
||||
if (!(*env)->ExceptionCheck(env)) {
|
||||
(*env)->CallVoidMethod(env, obj, setFileInternalMethodID, jcurrent_folder, jfilenames);
|
||||
}
|
||||
|
||||
quit(env, (jobject)obj, TRUE);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_GtkFileDialogPeer_run(JNIEnv * env, jobject jpeer,
|
||||
jstring jtitle, jint mode, jstring jdir, jstring jfile,
|
||||
jobject jfilter, jboolean multiple)
|
||||
{
|
||||
GtkWidget *dialog = NULL;
|
||||
GtkFileFilter *filter;
|
||||
|
||||
if (jvm == NULL) {
|
||||
(*env)->GetJavaVM(env, &jvm);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
}
|
||||
|
||||
gtk->gdk_threads_enter();
|
||||
|
||||
const char *title = jtitle == NULL? "": (*env)->GetStringUTFChars(env, jtitle, 0);
|
||||
if (title == NULL) {
|
||||
(*env)->ExceptionClear(env);
|
||||
JNU_ThrowOutOfMemoryError(env, "Could not get title");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode == java_awt_FileDialog_SAVE) {
|
||||
dialog = gtk->gtk_file_chooser_dialog_new(title, NULL,
|
||||
GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL,
|
||||
GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
|
||||
}
|
||||
else {
|
||||
dialog = gtk->gtk_file_chooser_dialog_new(title, NULL,
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL,
|
||||
GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
|
||||
|
||||
/* Set multiple selection mode, that is allowed only in OPEN action */
|
||||
if (multiple) {
|
||||
gtk->gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), multiple);
|
||||
}
|
||||
}
|
||||
|
||||
if (jtitle != NULL) {
|
||||
(*env)->ReleaseStringUTFChars(env, jtitle, title);
|
||||
}
|
||||
|
||||
/* Set the directory */
|
||||
if (jdir != NULL) {
|
||||
const char *dir = (*env)->GetStringUTFChars(env, jdir, 0);
|
||||
if (dir == NULL) {
|
||||
(*env)->ExceptionClear(env);
|
||||
JNU_ThrowOutOfMemoryError(env, "Could not get dir");
|
||||
return;
|
||||
}
|
||||
gtk->gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), dir);
|
||||
(*env)->ReleaseStringUTFChars(env, jdir, dir);
|
||||
}
|
||||
|
||||
/* Set the filename */
|
||||
if (jfile != NULL) {
|
||||
const char *filename = (*env)->GetStringUTFChars(env, jfile, 0);
|
||||
if (filename == NULL) {
|
||||
(*env)->ExceptionClear(env);
|
||||
JNU_ThrowOutOfMemoryError(env, "Could not get filename");
|
||||
return;
|
||||
}
|
||||
if (mode == java_awt_FileDialog_SAVE) {
|
||||
gtk->gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), filename);
|
||||
} else {
|
||||
gtk->gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), filename);
|
||||
}
|
||||
(*env)->ReleaseStringUTFChars(env, jfile, filename);
|
||||
}
|
||||
|
||||
/* Set the file filter */
|
||||
if (jfilter != NULL) {
|
||||
filter = gtk->gtk_file_filter_new();
|
||||
gtk->gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME,
|
||||
filenameFilterCallback, jpeer, NULL);
|
||||
gtk->gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
|
||||
}
|
||||
|
||||
/* Other Properties */
|
||||
gtk->gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
|
||||
|
||||
// Note: the initial location will be ignored by the Wayland server anyway, so
|
||||
// we are not doing gtk_window_move() here
|
||||
|
||||
gtk->g_signal_connect_data(dialog, "response", G_CALLBACK( handle_response), jpeer, 0, 0);
|
||||
|
||||
(*env)->SetLongField(env, jpeer, widgetFieldID, ptr_to_jlong(dialog));
|
||||
|
||||
gtk->gtk_widget_show(dialog);
|
||||
|
||||
if ((*env)->CallBooleanMethod(env, jpeer, setWindowMethodID, 0)) {
|
||||
gtk->gtk_main();
|
||||
}
|
||||
|
||||
gtk->gdk_threads_leave();
|
||||
}
|
||||
|
||||
214
src/java.desktop/unix/native/libawt_wlawt/awt_UNIXToolkit.c
Normal file
214
src/java.desktop/unix/native/libawt_wlawt/awt_UNIXToolkit.c
Normal file
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#ifdef HEADLESS
|
||||
#error This file should not be included in headless library
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <jni.h>
|
||||
#include <sizecalc.h>
|
||||
#include "sun_awt_UNIXToolkit.h"
|
||||
|
||||
#include "awt.h"
|
||||
#include "gtk_interface.h"
|
||||
|
||||
static jclass toolkitClass = NULL;
|
||||
static jmethodID loadIconMID = NULL;
|
||||
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_sun_awt_UNIXToolkit_check_1gtk(JNIEnv *env, jclass klass, jint version)
|
||||
{
|
||||
return (jboolean)gtk_check_version(version);
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_sun_awt_UNIXToolkit_load_1gtk(JNIEnv *env, jclass klass, jint version, jboolean verbose)
|
||||
{
|
||||
return (jboolean)gtk_load(env, version, verbose);
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_sun_awt_UNIXToolkit_unload_1gtk(JNIEnv *env, jclass klass)
|
||||
{
|
||||
return (jboolean)gtk->unload();
|
||||
}
|
||||
|
||||
static jboolean init_method(JNIEnv *env, jobject this)
|
||||
{
|
||||
if (toolkitClass == NULL) {
|
||||
toolkitClass = (*env)->NewGlobalRef(env,
|
||||
(*env)->GetObjectClass(env, this));
|
||||
CHECK_NULL_RETURN(toolkitClass, JNI_FALSE);
|
||||
loadIconMID = (*env)->GetMethodID(env, toolkitClass,
|
||||
"loadIconCallback", "([BIIIIIZ)V");
|
||||
CHECK_NULL_RETURN(loadIconMID, JNI_FALSE);
|
||||
}
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_awt_UNIXToolkit
|
||||
* Method: load_gtk_icon
|
||||
* Signature: (Ljava/lang/String)Z
|
||||
*
|
||||
* This method assumes that GTK libs are present.
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_sun_awt_UNIXToolkit_load_1gtk_1icon(JNIEnv *env, jobject this,
|
||||
jstring filename)
|
||||
{
|
||||
int len;
|
||||
jsize jlen;
|
||||
char *filename_str = NULL;
|
||||
GError **error = NULL;
|
||||
|
||||
if (filename == NULL)
|
||||
{
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
len = (*env)->GetStringUTFLength(env, filename);
|
||||
jlen = (*env)->GetStringLength(env, filename);
|
||||
filename_str = (char *)SAFE_SIZE_ARRAY_ALLOC(malloc,
|
||||
sizeof(char), len + 1);
|
||||
if (filename_str == NULL) {
|
||||
JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
|
||||
return JNI_FALSE;
|
||||
}
|
||||
if (!init_method(env, this) ) {
|
||||
free(filename_str);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
(*env)->GetStringUTFRegion(env, filename, 0, jlen, filename_str);
|
||||
jboolean result = gtk->get_file_icon_data(env, filename_str, error,
|
||||
loadIconMID, this);
|
||||
|
||||
/* Release the strings we've allocated. */
|
||||
free(filename_str);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_awt_UNIXToolkit
|
||||
* Method: load_stock_icon
|
||||
* Signature: (ILjava/lang/String;IILjava/lang/String;)Z
|
||||
*
|
||||
* This method assumes that GTK libs are present.
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_sun_awt_UNIXToolkit_load_1stock_1icon(JNIEnv *env, jobject this,
|
||||
jint widget_type, jstring stock_id, jint icon_size,
|
||||
jint text_direction, jstring detail)
|
||||
{
|
||||
int len;
|
||||
jsize jlen;
|
||||
char *stock_id_str = NULL;
|
||||
char *detail_str = NULL;
|
||||
jboolean result = JNI_FALSE;
|
||||
|
||||
if (stock_id == NULL)
|
||||
{
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
len = (*env)->GetStringUTFLength(env, stock_id);
|
||||
jlen = (*env)->GetStringLength(env, stock_id);
|
||||
stock_id_str = (char *)SAFE_SIZE_ARRAY_ALLOC(malloc, sizeof(char), len + 1);
|
||||
if (stock_id_str == NULL) {
|
||||
JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
|
||||
return JNI_FALSE;
|
||||
}
|
||||
(*env)->GetStringUTFRegion(env, stock_id, 0, jlen, stock_id_str);
|
||||
|
||||
/* Detail isn't required so check for NULL. */
|
||||
if (detail != NULL)
|
||||
{
|
||||
len = (*env)->GetStringUTFLength(env, detail);
|
||||
jlen = (*env)->GetStringLength(env, detail);
|
||||
detail_str = (char *)SAFE_SIZE_ARRAY_ALLOC(malloc, sizeof(char), len + 1);
|
||||
if (detail_str == NULL) {
|
||||
free(stock_id_str);
|
||||
JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
|
||||
return JNI_FALSE;
|
||||
}
|
||||
(*env)->GetStringUTFRegion(env, detail, 0, jlen, detail_str);
|
||||
}
|
||||
|
||||
if (init_method(env, this)) {
|
||||
result = gtk->get_icon_data(env, widget_type, stock_id_str,
|
||||
icon_size, text_direction, detail_str,
|
||||
loadIconMID, this);
|
||||
}
|
||||
|
||||
free(stock_id_str);
|
||||
free(detail_str);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_SunToolkit_closeSplashScreen(JNIEnv *env, jclass cls)
|
||||
{
|
||||
typedef void (*SplashClose_t)();
|
||||
SplashClose_t splashClose;
|
||||
void* hSplashLib = dlopen(0, RTLD_LAZY);
|
||||
if (!hSplashLib) {
|
||||
return;
|
||||
}
|
||||
splashClose = (SplashClose_t)dlsym(hSplashLib,
|
||||
"SplashClose");
|
||||
if (splashClose) {
|
||||
splashClose();
|
||||
}
|
||||
dlclose(hSplashLib);
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_sun_awt_UNIXToolkit_gtkCheckVersionImpl(JNIEnv *env, jobject this, jint major, jint minor, jint micro)
|
||||
{
|
||||
char *ret;
|
||||
|
||||
ret = gtk->gtk_check_version(major, minor, micro);
|
||||
if (ret == NULL) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_awt_UNIXToolkit_get_1gtk_1version(JNIEnv *env, jclass klass)
|
||||
{
|
||||
return gtk ? gtk->version : GTK_ANY;
|
||||
}
|
||||
|
||||
2844
src/java.desktop/unix/native/libawt_wlawt/gtk3_interface.c
Normal file
2844
src/java.desktop/unix/native/libawt_wlawt/gtk3_interface.c
Normal file
File diff suppressed because it is too large
Load Diff
755
src/java.desktop/unix/native/libawt_wlawt/gtk3_interface.h
Normal file
755
src/java.desktop/unix/native/libawt_wlawt/gtk3_interface.h
Normal file
@@ -0,0 +1,755 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#ifdef HEADLESS
|
||||
#error This file should not be included in headless library
|
||||
#endif
|
||||
|
||||
#ifndef _GTK3_INTERFACE_H
|
||||
#define _GTK3_INTERFACE_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <jni.h>
|
||||
#include "gtk_interface.h"
|
||||
|
||||
#define LIGHTNESS_MULT 1.3
|
||||
#define DARKNESS_MULT 0.7
|
||||
|
||||
#define G_PI 3.1415926535897932384626433832795028841971693993751
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GTK_STATE_FLAG_NORMAL = 0,
|
||||
GTK_STATE_FLAG_ACTIVE = 1 << 0,
|
||||
GTK_STATE_FLAG_PRELIGHT = 1 << 1,
|
||||
GTK_STATE_FLAG_SELECTED = 1 << 2,
|
||||
GTK_STATE_FLAG_INSENSITIVE = 1 << 3,
|
||||
GTK_STATE_FLAG_INCONSISTENT = 1 << 4,
|
||||
GTK_STATE_FLAG_FOCUSED = 1 << 5,
|
||||
GTK_STATE_FLAG_BACKDROP = 1 << 6,
|
||||
GTK_STATE_FLAG_DIR_LTR = 1 << 7,
|
||||
GTK_STATE_FLAG_DIR_RTL = 1 << 8,
|
||||
GTK_STATE_FLAG_LINK = 1 << 9,
|
||||
GTK_STATE_FLAG_VISITED = 1 << 10,
|
||||
GTK_STATE_FLAG_CHECKED = 1 << 11
|
||||
} GtkStateFlags;
|
||||
|
||||
typedef enum {
|
||||
GTK_JUNCTION_NONE = 0,
|
||||
GTK_JUNCTION_CORNER_TOPLEFT = 1 << 0,
|
||||
GTK_JUNCTION_CORNER_TOPRIGHT = 1 << 1,
|
||||
GTK_JUNCTION_CORNER_BOTTOMLEFT = 1 << 2,
|
||||
GTK_JUNCTION_CORNER_BOTTOMRIGHT = 1 << 3,
|
||||
GTK_JUNCTION_TOP =
|
||||
(GTK_JUNCTION_CORNER_TOPLEFT | GTK_JUNCTION_CORNER_TOPRIGHT),
|
||||
GTK_JUNCTION_BOTTOM =
|
||||
(GTK_JUNCTION_CORNER_BOTTOMLEFT | GTK_JUNCTION_CORNER_BOTTOMRIGHT),
|
||||
GTK_JUNCTION_LEFT =
|
||||
(GTK_JUNCTION_CORNER_TOPLEFT | GTK_JUNCTION_CORNER_BOTTOMLEFT),
|
||||
GTK_JUNCTION_RIGHT =
|
||||
(GTK_JUNCTION_CORNER_TOPRIGHT | GTK_JUNCTION_CORNER_BOTTOMRIGHT)
|
||||
} GtkJunctionSides;
|
||||
|
||||
typedef enum {
|
||||
GTK_REGION_EVEN = 1 << 0,
|
||||
GTK_REGION_ODD = 1 << 1,
|
||||
GTK_REGION_FIRST = 1 << 2,
|
||||
GTK_REGION_LAST = 1 << 3,
|
||||
GTK_REGION_ONLY = 1 << 4,
|
||||
GTK_REGION_SORTED = 1 << 5
|
||||
} GtkRegionFlags;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GTK_WINDOW_TOPLEVEL,
|
||||
GTK_WINDOW_POPUP
|
||||
} GtkWindowType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
G_PARAM_READABLE = 1 << 0,
|
||||
G_PARAM_WRITABLE = 1 << 1,
|
||||
G_PARAM_CONSTRUCT = 1 << 2,
|
||||
G_PARAM_CONSTRUCT_ONLY = 1 << 3,
|
||||
G_PARAM_LAX_VALIDATION = 1 << 4,
|
||||
G_PARAM_STATIC_NAME = 1 << 5
|
||||
} GParamFlags;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GTK_ICON_LOOKUP_NO_SVG = 1 << 0,
|
||||
GTK_ICON_LOOKUP_FORCE_SVG = 1 << 1,
|
||||
GTK_ICON_LOOKUP_USE_BUILTIN = 1 << 2,
|
||||
GTK_ICON_LOOKUP_GENERIC_FALLBACK = 1 << 3,
|
||||
GTK_ICON_LOOKUP_FORCE_SIZE = 1 << 4
|
||||
} GtkIconLookupFlags;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GTK_UPDATE_CONTINUOUS,
|
||||
GTK_UPDATE_DISCONTINUOUS,
|
||||
GTK_UPDATE_DELAYED
|
||||
} GtkUpdateType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GTK_PROGRESS_CONTINUOUS,
|
||||
GTK_PROGRESS_DISCRETE
|
||||
} GtkProgressBarStyle;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GTK_PROGRESS_LEFT_TO_RIGHT,
|
||||
GTK_PROGRESS_RIGHT_TO_LEFT,
|
||||
GTK_PROGRESS_BOTTOM_TO_TOP,
|
||||
GTK_PROGRESS_TOP_TO_BOTTOM
|
||||
} GtkProgressBarOrientation;
|
||||
|
||||
typedef enum {
|
||||
CAIRO_FORMAT_INVALID = -1,
|
||||
CAIRO_FORMAT_ARGB32 = 0,
|
||||
CAIRO_FORMAT_RGB24 = 1,
|
||||
CAIRO_FORMAT_A8 = 2,
|
||||
CAIRO_FORMAT_A1 = 3,
|
||||
CAIRO_FORMAT_RGB16_565 = 4
|
||||
} cairo_format_t;
|
||||
|
||||
typedef enum _cairo_status {
|
||||
CAIRO_STATUS_SUCCESS = 0,
|
||||
|
||||
CAIRO_STATUS_NO_MEMORY,
|
||||
CAIRO_STATUS_INVALID_RESTORE,
|
||||
CAIRO_STATUS_INVALID_POP_GROUP,
|
||||
CAIRO_STATUS_NO_CURRENT_POINT,
|
||||
CAIRO_STATUS_INVALID_MATRIX,
|
||||
CAIRO_STATUS_INVALID_STATUS,
|
||||
CAIRO_STATUS_NULL_POINTER,
|
||||
CAIRO_STATUS_INVALID_STRING,
|
||||
CAIRO_STATUS_INVALID_PATH_DATA,
|
||||
CAIRO_STATUS_READ_ERROR,
|
||||
CAIRO_STATUS_WRITE_ERROR,
|
||||
CAIRO_STATUS_SURFACE_FINISHED,
|
||||
CAIRO_STATUS_SURFACE_TYPE_MISMATCH,
|
||||
CAIRO_STATUS_PATTERN_TYPE_MISMATCH,
|
||||
CAIRO_STATUS_INVALID_CONTENT,
|
||||
CAIRO_STATUS_INVALID_FORMAT,
|
||||
CAIRO_STATUS_INVALID_VISUAL,
|
||||
CAIRO_STATUS_FILE_NOT_FOUND,
|
||||
CAIRO_STATUS_INVALID_DASH,
|
||||
CAIRO_STATUS_INVALID_DSC_COMMENT,
|
||||
CAIRO_STATUS_INVALID_INDEX,
|
||||
CAIRO_STATUS_CLIP_NOT_REPRESENTABLE,
|
||||
CAIRO_STATUS_TEMP_FILE_ERROR,
|
||||
CAIRO_STATUS_INVALID_STRIDE,
|
||||
CAIRO_STATUS_FONT_TYPE_MISMATCH,
|
||||
CAIRO_STATUS_USER_FONT_IMMUTABLE,
|
||||
CAIRO_STATUS_USER_FONT_ERROR,
|
||||
CAIRO_STATUS_NEGATIVE_COUNT,
|
||||
CAIRO_STATUS_INVALID_CLUSTERS,
|
||||
CAIRO_STATUS_INVALID_SLANT,
|
||||
CAIRO_STATUS_INVALID_WEIGHT,
|
||||
CAIRO_STATUS_INVALID_SIZE,
|
||||
CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED,
|
||||
CAIRO_STATUS_DEVICE_TYPE_MISMATCH,
|
||||
CAIRO_STATUS_DEVICE_ERROR,
|
||||
|
||||
CAIRO_STATUS_LAST_STATUS
|
||||
} cairo_status_t;
|
||||
|
||||
/* We define all structure pointers to be void* */
|
||||
typedef void GVfs;
|
||||
|
||||
typedef void GdkColormap;
|
||||
typedef void GdkDrawable;
|
||||
typedef void GdkGC;
|
||||
typedef void GdkPixmap;
|
||||
typedef void GtkStyleContext;
|
||||
typedef void GtkFixed;
|
||||
typedef void GtkMenuItem;
|
||||
typedef void GtkMenuShell;
|
||||
typedef void GtkWidgetClass;
|
||||
typedef void PangoFontDescription;
|
||||
typedef void GtkSettings;
|
||||
typedef void GtkStyleProvider;
|
||||
typedef void cairo_pattern_t;
|
||||
typedef void cairo_t;
|
||||
typedef void cairo_surface_t;
|
||||
typedef void GtkScrolledWindow;
|
||||
typedef void GtkIconTheme;
|
||||
typedef void GtkWidget;
|
||||
typedef void GtkMisc;
|
||||
typedef void GtkContainer;
|
||||
typedef void GtkBin;
|
||||
typedef void GtkAdjustment;
|
||||
typedef void GtkRange;
|
||||
typedef void GtkProgressBar;
|
||||
typedef void GtkProgress;
|
||||
typedef void GtkWidgetPath;
|
||||
typedef void GtkPaned;
|
||||
|
||||
/* Some real structures */
|
||||
typedef struct
|
||||
{
|
||||
guint32 pixel;
|
||||
guint16 red;
|
||||
guint16 green;
|
||||
guint16 blue;
|
||||
} GdkColor;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gdouble red;
|
||||
gdouble green;
|
||||
gdouble blue;
|
||||
gdouble alpha;
|
||||
} GdkRGBA;
|
||||
|
||||
typedef struct {
|
||||
gint fd;
|
||||
gushort events;
|
||||
gushort revents;
|
||||
} GPollFD;
|
||||
|
||||
typedef struct {
|
||||
int x, y;
|
||||
int width, height;
|
||||
} GtkAllocation;
|
||||
|
||||
typedef struct {
|
||||
gint width;
|
||||
gint height;
|
||||
} GtkRequisition;
|
||||
|
||||
typedef struct {
|
||||
GtkWidgetClass *g_class;
|
||||
} GTypeInstance;
|
||||
|
||||
typedef struct {
|
||||
gint16 left;
|
||||
gint16 right;
|
||||
gint16 top;
|
||||
gint16 bottom;
|
||||
} GtkBorder;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GType g_type;
|
||||
union {
|
||||
gint v_int;
|
||||
guint v_uint;
|
||||
glong v_long;
|
||||
gulong v_ulong;
|
||||
gint64 v_int64;
|
||||
guint64 v_uint64;
|
||||
gfloat v_float;
|
||||
gdouble v_double;
|
||||
gpointer v_pointer;
|
||||
} data[2];
|
||||
} GValue;
|
||||
|
||||
typedef struct {
|
||||
GTypeInstance g_type_instance;
|
||||
const gchar *name;
|
||||
GParamFlags flags;
|
||||
GType value_type;
|
||||
GType owner_type;
|
||||
} GParamSpec;
|
||||
|
||||
static gchar* (*fp_glib_check_version)(guint required_major,
|
||||
guint required_minor, guint required_micro);
|
||||
|
||||
/**
|
||||
* Returns :
|
||||
* NULL if the GTK+ library is compatible with the given version, or a string
|
||||
* describing the version mismatch.
|
||||
*/
|
||||
static gchar* (*fp_gtk_check_version)(guint required_major, guint
|
||||
required_minor, guint required_micro);
|
||||
|
||||
static void (*fp_g_free)(gpointer mem);
|
||||
static void (*fp_g_object_unref)(gpointer object);
|
||||
|
||||
static int (*fp_gdk_pixbuf_get_bits_per_sample)(const GdkPixbuf *pixbuf);
|
||||
static guchar *(*fp_gdk_pixbuf_get_pixels)(const GdkPixbuf *pixbuf);
|
||||
static gboolean (*fp_gdk_pixbuf_get_has_alpha)(const GdkPixbuf *pixbuf);
|
||||
static int (*fp_gdk_pixbuf_get_height)(const GdkPixbuf *pixbuf);
|
||||
static int (*fp_gdk_pixbuf_get_n_channels)(const GdkPixbuf *pixbuf);
|
||||
static int (*fp_gdk_pixbuf_get_rowstride)(const GdkPixbuf *pixbuf);
|
||||
static int (*fp_gdk_pixbuf_get_width)(const GdkPixbuf *pixbuf);
|
||||
static GdkPixbuf *(*fp_gdk_pixbuf_new_from_file)(const char *filename,
|
||||
GError **error);
|
||||
static GdkColorspace (*fp_gdk_pixbuf_get_colorspace)(const GdkPixbuf *pixbuf);
|
||||
|
||||
static GdkPixbuf *(*fp_gdk_pixbuf_get_from_drawable)(GdkWindow *window,
|
||||
int src_x, int src_y, int width, int height);
|
||||
static GdkPixbuf *(*fp_gdk_pixbuf_scale_simple)(GdkPixbuf *src,
|
||||
int dest_width, int dest_heigh, GdkInterpType interp_type);
|
||||
|
||||
|
||||
static void (*fp_gtk_widget_destroy)(void *widget);
|
||||
static void (*fp_gtk_window_present)(GtkWindow *window);
|
||||
static void (*fp_gtk_window_present_with_time)(GtkWindow *window, guint32 timestamp);
|
||||
static void (*fp_gtk_window_move)(GtkWindow *window, gint x, gint y);
|
||||
static void (*fp_gtk_window_resize)(GtkWindow *window, gint width, gint height);
|
||||
|
||||
/**
|
||||
* Function Pointers for GtkFileChooser
|
||||
*/
|
||||
static gchar* (*fp_gtk_file_chooser_get_filename)(GtkFileChooser *chooser);
|
||||
static void (*fp_gtk_widget_hide)(void *widget);
|
||||
static void (*fp_gtk_main_quit)(void);
|
||||
static void* (*fp_gtk_file_chooser_dialog_new)(const gchar *title,
|
||||
GtkWindow *parent, GtkFileChooserAction action,
|
||||
const gchar *first_button_text, ...);
|
||||
static gboolean (*fp_gtk_file_chooser_set_current_folder)
|
||||
(GtkFileChooser *chooser, const gchar *filename);
|
||||
static gboolean (*fp_gtk_file_chooser_set_filename)(GtkFileChooser *chooser, const char *filename);
|
||||
static void (*fp_gtk_file_chooser_set_current_name)(GtkFileChooser *chooser, const gchar *name);
|
||||
static void (*fp_gtk_file_filter_add_custom)(GtkFileFilter *filter,
|
||||
GtkFileFilterFlags needed, GtkFileFilterFunc func, gpointer data,
|
||||
GDestroyNotify notify);
|
||||
static void (*fp_gtk_file_chooser_set_filter)(GtkFileChooser *chooser, GtkFileFilter *filter);
|
||||
static GType (*fp_gtk_file_chooser_get_type)(void);
|
||||
static GtkFileFilter* (*fp_gtk_file_filter_new)(void);
|
||||
static void (*fp_gtk_file_chooser_set_do_overwrite_confirmation)(
|
||||
GtkFileChooser *chooser, gboolean do_overwrite_confirmation);
|
||||
static void (*fp_gtk_file_chooser_set_select_multiple)(
|
||||
GtkFileChooser *chooser, gboolean select_multiple);
|
||||
static gchar* (*fp_gtk_file_chooser_get_current_folder)(GtkFileChooser *chooser);
|
||||
static GSList* (*fp_gtk_file_chooser_get_filenames)(GtkFileChooser *chooser);
|
||||
static guint (*fp_gtk_g_slist_length)(GSList *list);
|
||||
static gulong (*fp_g_signal_connect_data)(gpointer instance,
|
||||
const gchar *detailed_signal, GCallback c_handler, gpointer data,
|
||||
GClosureNotify destroy_data, GConnectFlags connect_flags);
|
||||
static void (*fp_gtk_widget_show)(void *widget);
|
||||
static void (*fp_gtk_main)(void);
|
||||
static guint (*fp_gtk_main_level)(void);
|
||||
static gchar* (*fp_g_path_get_dirname) (const gchar *file_name);
|
||||
|
||||
static GList* (*fp_g_list_append) (GList *list, gpointer data);
|
||||
static void (*fp_g_list_free) (GList *list);
|
||||
static void (*fp_g_list_free_full) (GList *list, GDestroyNotify free_func);
|
||||
|
||||
static void (*fp_gdk_threads_enter)(void);
|
||||
static void (*fp_gdk_threads_leave)(void);
|
||||
|
||||
static gboolean (*fp_gtk_show_uri_on_window)(GdkWindow *parent, const gchar *uri,
|
||||
guint32 timestamp, GError **error);
|
||||
|
||||
// Implementation functions prototypes
|
||||
static GValue* (*fp_g_value_init)(GValue *value, GType g_type);
|
||||
static gboolean (*fp_g_type_is_a)(GType type, GType is_a_type);
|
||||
static gboolean (*fp_g_value_get_boolean)(const GValue *value);
|
||||
static gchar (*fp_g_value_get_char)(const GValue *value);
|
||||
static guchar (*fp_g_value_get_uchar)(const GValue *value);
|
||||
static gint (*fp_g_value_get_int)(const GValue *value);
|
||||
static guint (*fp_g_value_get_uint)(const GValue *value);
|
||||
static glong (*fp_g_value_get_long)(const GValue *value);
|
||||
static gulong (*fp_g_value_get_ulong)(const GValue *value);
|
||||
static gint64 (*fp_g_value_get_int64)(const GValue *value);
|
||||
static guint64 (*fp_g_value_get_uint64)(const GValue *value);
|
||||
static gfloat (*fp_g_value_get_float)(const GValue *value);
|
||||
static gdouble (*fp_g_value_get_double)(const GValue *value);
|
||||
static const gchar* (*fp_g_value_get_string)(const GValue *value);
|
||||
static gint (*fp_g_value_get_enum)(const GValue *value);
|
||||
static guint (*fp_g_value_get_flags)(const GValue *value);
|
||||
static GParamSpec* (*fp_g_value_get_param)(const GValue *value);
|
||||
static gpointer* (*fp_g_value_get_boxed)(const GValue *value);
|
||||
static gpointer* (*fp_g_value_get_pointer)(const GValue *value);
|
||||
static void (*fp_g_object_get)(gpointer object, const gchar* fpn, ...);
|
||||
static void (*fp_g_object_set)(gpointer object, const gchar *first_property_name, ...);
|
||||
|
||||
static gboolean (*fp_g_main_context_iteration)(GMainContext *context, gboolean may_block);
|
||||
static GMainContext *(*fp_g_main_context_default)();
|
||||
static gboolean (*fp_g_main_context_is_owner)(GMainContext* context);
|
||||
|
||||
static gboolean (*fp_g_str_has_prefix)(const gchar *str, const gchar *prefix);
|
||||
static gchar** (*fp_g_strsplit)(const gchar *string, const gchar *delimiter, gint max_tokens);
|
||||
static void (*fp_g_strfreev)(gchar **str_array);
|
||||
|
||||
|
||||
static cairo_surface_t* (*fp_cairo_image_surface_create)(cairo_format_t format,
|
||||
int width, int height);
|
||||
static void (*fp_cairo_surface_destroy)(cairo_surface_t *surface);
|
||||
static cairo_status_t (*fp_cairo_surface_status)(cairo_surface_t *surface);
|
||||
static cairo_t* (*fp_cairo_create)(cairo_surface_t *target);
|
||||
static void (*fp_cairo_destroy)(cairo_t *cr);
|
||||
static cairo_status_t (*fp_cairo_status)(cairo_t *cr);
|
||||
static void (*fp_cairo_fill)(cairo_t *cr);
|
||||
static void (*fp_cairo_surface_flush)(cairo_surface_t *surface);
|
||||
static void (*fp_cairo_rectangle)(cairo_t *cr, double x, double y, double width, double height);
|
||||
static void (*fp_cairo_set_source_rgb)(cairo_t *cr, double red, double green, double blue);
|
||||
static void (*fp_cairo_set_source_rgba)(cairo_t *cr, double red, double green, double blue, double alpha);
|
||||
static void (*fp_cairo_paint)(cairo_t *cr);
|
||||
static void (*fp_cairo_clip)(cairo_t *cr);
|
||||
static unsigned char* (*fp_cairo_image_surface_get_data)(
|
||||
cairo_surface_t *surface);
|
||||
static int (*fp_cairo_image_surface_get_stride) (cairo_surface_t *surface);
|
||||
static GdkPixbuf* (*fp_gdk_pixbuf_get_from_surface)(cairo_surface_t *surface,
|
||||
gint src_x, gint src_y, gint width, gint height);
|
||||
static GtkStateType (*fp_gtk_widget_get_state)(GtkWidget *widget);
|
||||
static void (*fp_gtk_widget_set_state)(GtkWidget *widget, GtkStateType state);
|
||||
static gboolean (*fp_gtk_widget_is_focus)(GtkWidget *widget);
|
||||
static void (*fp_gtk_widget_set_allocation)(GtkWidget *widget, const GtkAllocation *allocation);
|
||||
static GtkWidget* (*fp_gtk_widget_get_parent)(GtkWidget *widget);
|
||||
static GtkStyleContext* (*fp_gtk_widget_get_style_context)(GtkWidget *widget);
|
||||
static void (*fp_gtk_style_context_get_color)(GtkStyleContext *context,
|
||||
GtkStateFlags state, GdkRGBA *color);
|
||||
static void (*fp_gtk_style_context_get_background_color)
|
||||
(GtkStyleContext *context, GtkStateFlags state, GdkRGBA *color);
|
||||
static void (*fp_gtk_style_context_get)(GtkStyleContext *context, GtkStateFlags state, ...);
|
||||
static GtkStateFlags (*fp_gtk_widget_get_state_flags)(GtkWidget* widget);
|
||||
static void (*fp_gtk_style_context_set_state)(GtkStyleContext* style, GtkStateFlags flags);
|
||||
static void (*fp_gtk_style_context_add_class)(GtkStyleContext *context, const gchar *class_name);
|
||||
static void (*fp_gtk_style_context_save)(GtkStyleContext *context);
|
||||
static void (*fp_gtk_style_context_restore)(GtkStyleContext *context);
|
||||
static void (*fp_gtk_render_check)(GtkStyleContext *context, cairo_t *cr,
|
||||
gdouble x, gdouble y, gdouble width, gdouble height);
|
||||
static void (*fp_gtk_render_option)(GtkStyleContext *context, cairo_t *cr,
|
||||
gdouble x, gdouble y, gdouble width, gdouble height);
|
||||
static void (*fp_gtk_render_extension)(GtkStyleContext *context, cairo_t *cr,
|
||||
gdouble x, gdouble y, gdouble width, gdouble height,
|
||||
GtkPositionType gap_side);
|
||||
static void (*fp_gtk_render_expander)(GtkStyleContext *context, cairo_t *cr,
|
||||
gdouble x, gdouble y, gdouble width, gdouble height);
|
||||
static void (*fp_gtk_render_frame_gap)(GtkStyleContext *context, cairo_t *cr,
|
||||
gdouble x, gdouble y, gdouble width, gdouble height,
|
||||
GtkPositionType gap_side, gdouble xy0_gap,
|
||||
gdouble xy1_gap);
|
||||
static void (*fp_gtk_render_line)(GtkStyleContext *context, cairo_t *cr,
|
||||
gdouble x0, gdouble y0, gdouble x1, gdouble y1);
|
||||
static GdkPixbuf* (*fp_gtk_widget_render_icon_pixbuf)(GtkWidget *widget,
|
||||
const gchar *stock_id, GtkIconSize size);
|
||||
static cairo_surface_t* (*fp_gdk_window_create_similar_image_surface)(
|
||||
GdkWindow *window, cairo_format_t format, int width,
|
||||
int height, int scale);
|
||||
static cairo_surface_t* (*fp_gdk_window_create_similar_surface)(
|
||||
GdkWindow *window, cairo_format_t format,
|
||||
int width, int height);
|
||||
static GdkWindow* (*fp_gtk_widget_get_window)(GtkWidget *widget);
|
||||
static GtkSettings *(*fp_gtk_settings_get_for_screen)(GdkScreen *screen);
|
||||
static GdkScreen *(*fp_gtk_widget_get_screen)(GtkWidget *widget);
|
||||
static GtkStyleProvider* (*fp_gtk_css_provider_get_named)(const gchar *name,
|
||||
const gchar *variant);
|
||||
static void (*fp_gtk_style_context_add_provider)(GtkStyleContext *context,
|
||||
GtkStyleProvider *provider, guint priority);
|
||||
static void (*fp_gtk_render_frame)(GtkStyleContext *context,cairo_t *cr,
|
||||
gdouble x, gdouble y, gdouble width, gdouble height);
|
||||
static void (*fp_gtk_render_focus)(GtkStyleContext *context,cairo_t *cr,
|
||||
gdouble x, gdouble y, gdouble width, gdouble height);
|
||||
static void (*fp_gtk_render_handle)(GtkStyleContext *context,cairo_t *cr,
|
||||
gdouble x, gdouble y, gdouble width, gdouble height);
|
||||
static void (*fp_gtk_style_context_get_property)(GtkStyleContext *context,
|
||||
const gchar *property, GtkStateFlags state, GValue *value);
|
||||
static void (*fp_gtk_render_activity)(GtkStyleContext *context, cairo_t *cr,
|
||||
gdouble x, gdouble y, gdouble width, gdouble height);
|
||||
static void (*fp_gtk_render_background)(GtkStyleContext *context, cairo_t *cr,
|
||||
gdouble x, gdouble y, gdouble width, gdouble height);
|
||||
static gboolean (*fp_gtk_style_context_has_class)(GtkStyleContext *context,
|
||||
const gchar *class_name);
|
||||
|
||||
static void (*fp_gtk_style_context_set_junction_sides)(GtkStyleContext *context,
|
||||
GtkJunctionSides sides);
|
||||
static void (*fp_gtk_style_context_add_region)(GtkStyleContext *context,
|
||||
const gchar *region_name, GtkRegionFlags flags);
|
||||
static void (*fp_gtk_render_arrow)(GtkStyleContext *context, cairo_t *cr,
|
||||
gdouble angle, gdouble x, gdouble y, gdouble size);
|
||||
static void (*fp_gtk_bin_set_child)(GtkBin *bin, GtkWidget *widget);
|
||||
static void (*fp_gtk_scrolled_window_set_shadow_type)(
|
||||
GtkScrolledWindow *scrolled_window, GtkShadowType type);
|
||||
static void (*fp_gtk_render_slider)(GtkStyleContext *context, cairo_t *cr,
|
||||
gdouble x, gdouble y, gdouble width, gdouble height,
|
||||
GtkOrientation orientation);
|
||||
static void (*fp_gtk_style_context_get_padding)(GtkStyleContext *self,
|
||||
GtkStateFlags state, GtkBorder* padding);
|
||||
static void (*fp_gtk_range_set_inverted)(GtkRange *range, gboolean setting);
|
||||
static PangoFontDescription* (*fp_gtk_style_context_get_font)(
|
||||
GtkStyleContext *context, GtkStateFlags state);
|
||||
static int (*fp_gtk_widget_get_allocated_width)(GtkWidget *widget);
|
||||
static int (*fp_gtk_widget_get_allocated_height)(GtkWidget *widget);
|
||||
static GtkIconTheme* (*fp_gtk_icon_theme_get_default)(void);
|
||||
static GdkPixbuf* (*fp_gtk_icon_theme_load_icon)(GtkIconTheme *icon_theme,
|
||||
const gchar *icon_name, gint size,
|
||||
GtkIconLookupFlags flags, GError **error);
|
||||
static void (*fp_gtk_adjustment_set_lower)(GtkAdjustment *adjustment, gdouble lower);
|
||||
static void (*fp_gtk_adjustment_set_page_increment)(GtkAdjustment *adjustment,
|
||||
gdouble page_increment);
|
||||
static void (*fp_gtk_adjustment_set_page_size)(GtkAdjustment *adjustment, gdouble page_size);
|
||||
static void (*fp_gtk_adjustment_set_step_increment)(GtkAdjustment *adjustment, gdouble step_increment);
|
||||
static void (*fp_gtk_adjustment_set_upper)(GtkAdjustment *adjustment, gdouble upper);
|
||||
static void (*fp_gtk_adjustment_set_value)(GtkAdjustment *adjustment, gdouble value);
|
||||
static GdkGC *(*fp_gdk_gc_new)(GdkDrawable*);
|
||||
static void (*fp_gdk_rgb_gc_set_foreground)(GdkGC*, guint32);
|
||||
static void (*fp_gdk_draw_rectangle)(GdkDrawable*, GdkGC*, gboolean, gint, gint, gint, gint);
|
||||
static GdkPixbuf *(*fp_gdk_pixbuf_new)(GdkColorspace colorspace,
|
||||
gboolean has_alpha, int bits_per_sample, int width, int height);
|
||||
|
||||
static GdkPixbuf *(*fp_gdk_pixbuf_new_from_data)(
|
||||
const guchar *data,
|
||||
GdkColorspace colorspace,
|
||||
gboolean has_alpha,
|
||||
int bits_per_sample,
|
||||
int width,
|
||||
int height,
|
||||
int rowstride,
|
||||
GdkPixbufDestroyNotify destroy_fn,
|
||||
gpointer destroy_fn_data
|
||||
);
|
||||
|
||||
static void (*fp_gdk_pixbuf_copy_area) (
|
||||
const GdkPixbuf* src_pixbuf,
|
||||
int src_x,
|
||||
int src_y,
|
||||
int width,
|
||||
int height,
|
||||
GdkPixbuf* dest_pixbuf,
|
||||
int dest_x,
|
||||
int dest_y
|
||||
);
|
||||
|
||||
static void (*fp_gdk_drawable_get_size)(GdkDrawable *drawable, gint* width, gint* height);
|
||||
static gboolean (*fp_gtk_init_check)(int* argc, char** argv);
|
||||
|
||||
/* Widget creation */
|
||||
static GtkWidget* (*fp_gtk_arrow_new)(GtkArrowType arrow_type, GtkShadowType shadow_type);
|
||||
static GtkWidget* (*fp_gtk_button_new)();
|
||||
static GtkWidget* (*fp_gtk_check_button_new)();
|
||||
static GtkWidget* (*fp_gtk_check_menu_item_new)();
|
||||
static GtkWidget* (*fp_gtk_color_selection_dialog_new)(const gchar* title);
|
||||
static GtkWidget* (*fp_gtk_combo_box_new)();
|
||||
static GtkWidget* (*fp_gtk_combo_box_entry_new)();
|
||||
static GtkWidget* (*fp_gtk_entry_new)();
|
||||
static GtkWidget* (*fp_gtk_fixed_new)();
|
||||
static GtkWidget* (*fp_gtk_handle_box_new)();
|
||||
static GtkWidget* (*fp_gtk_paned_new)(GtkOrientation orientation);
|
||||
static GtkWidget* (*fp_gtk_scale_new)(GtkOrientation orientation, GtkAdjustment* adjustment);
|
||||
static GtkWidget* (*fp_gtk_hscrollbar_new)(GtkAdjustment* adjustment);
|
||||
static GtkWidget* (*fp_gtk_vscrollbar_new)(GtkAdjustment* adjustment);
|
||||
static GtkWidget* (*fp_gtk_hseparator_new)();
|
||||
static GtkWidget* (*fp_gtk_vseparator_new)();
|
||||
static GtkWidget* (*fp_gtk_image_new)();
|
||||
static GtkWidget* (*fp_gtk_label_new)(const gchar* str);
|
||||
static GtkWidget* (*fp_gtk_menu_new)();
|
||||
static GtkWidget* (*fp_gtk_menu_bar_new)();
|
||||
static GtkWidget* (*fp_gtk_menu_item_new)();
|
||||
static GtkWidget* (*fp_gtk_notebook_new)();
|
||||
static GtkWidget* (*fp_gtk_progress_bar_new)();
|
||||
static GtkWidget* (*fp_gtk_progress_bar_set_orientation)(
|
||||
GtkProgressBar *pbar,
|
||||
GtkProgressBarOrientation orientation);
|
||||
static GtkWidget* (*fp_gtk_radio_button_new)(GSList *group);
|
||||
static GtkWidget* (*fp_gtk_radio_menu_item_new)(GSList *group);
|
||||
static GtkWidget* (*fp_gtk_scrolled_window_new)(GtkAdjustment *hadjustment, GtkAdjustment *vadjustment);
|
||||
static GtkWidget* (*fp_gtk_separator_menu_item_new)();
|
||||
static GtkWidget* (*fp_gtk_separator_tool_item_new)();
|
||||
static GtkWidget* (*fp_gtk_text_view_new)();
|
||||
static GtkWidget* (*fp_gtk_toggle_button_new)();
|
||||
static GtkWidget* (*fp_gtk_toolbar_new)();
|
||||
static GtkWidget* (*fp_gtk_tree_view_new)();
|
||||
static GtkWidget* (*fp_gtk_viewport_new)(GtkAdjustment *hadjustment, GtkAdjustment *vadjustment);
|
||||
static GtkWidget* (*fp_gtk_window_new)(GtkWindowType type);
|
||||
static GtkWidget* (*fp_gtk_dialog_new)();
|
||||
static GtkWidget* (*fp_gtk_spin_button_new)(GtkAdjustment *adjustment,
|
||||
gdouble climb_rate, guint digits);
|
||||
static GtkWidget* (*fp_gtk_frame_new)(const gchar *label);
|
||||
|
||||
/* Other widget operations */
|
||||
static GtkAdjustment* (*fp_gtk_adjustment_new)(gdouble value,
|
||||
gdouble lower, gdouble upper, gdouble step_increment,
|
||||
gdouble page_increment, gdouble page_size);
|
||||
static void (*fp_gtk_container_add)(GtkContainer *window, GtkWidget *widget);
|
||||
static void (*fp_gtk_menu_shell_append)(GtkMenuShell *menu_shell, GtkWidget *child);
|
||||
static void (*fp_gtk_menu_item_set_submenu)(GtkMenuItem *menu_item, GtkWidget *submenu);
|
||||
static void (*fp_gtk_widget_realize)(GtkWidget *widget);
|
||||
static GdkPixbuf* (*fp_gtk_widget_render_icon)(GtkWidget *widget,
|
||||
const gchar *stock_id, GtkIconSize size, const gchar *detail);
|
||||
static void (*fp_gtk_widget_set_name)(GtkWidget *widget, const gchar *name);
|
||||
static void (*fp_gtk_widget_set_parent)(GtkWidget *widget, GtkWidget *parent);
|
||||
static void (*fp_gtk_widget_set_direction)(GtkWidget *widget, GtkTextDirection direction);
|
||||
static void (*fp_gtk_widget_style_get)(GtkWidget *widget, const gchar *first_property_name, ...);
|
||||
static void (*fp_gtk_widget_class_install_style_property)(GtkWidgetClass* class, GParamSpec *pspec);
|
||||
static GParamSpec* (*fp_gtk_widget_class_find_style_property)(
|
||||
GtkWidgetClass* class, const gchar* property_name);
|
||||
static void (*fp_gtk_widget_style_get_property)(GtkWidget* widget,
|
||||
const gchar* property_name, GValue* value);
|
||||
static char* (*fp_pango_font_description_to_string)(const PangoFontDescription* fd);
|
||||
static GtkSettings* (*fp_gtk_settings_get_default)();
|
||||
static GtkSettings* (*fp_gtk_widget_get_settings)(GtkWidget *widget);
|
||||
static GType (*fp_gtk_border_get_type)();
|
||||
static void (*fp_gtk_arrow_set)(GtkWidget* arrow,
|
||||
GtkArrowType arrow_type,
|
||||
GtkShadowType shadow_type);
|
||||
static void (*fp_gtk_widget_size_request)(GtkWidget *widget, GtkRequisition *requisition);
|
||||
static GtkAdjustment* (*fp_gtk_range_get_adjustment)(GtkRange* range);
|
||||
static GtkWidgetPath* (*fp_gtk_widget_path_copy)(const GtkWidgetPath *path);
|
||||
static const GtkWidgetPath* (*fp_gtk_style_context_get_path)(GtkStyleContext *context);
|
||||
static GtkWidgetPath* (*fp_gtk_widget_path_new) (void);
|
||||
static gint (*fp_gtk_widget_path_append_type)(GtkWidgetPath *path, GType type);
|
||||
static void (*fp_gtk_widget_path_iter_set_object_name)(GtkWidgetPath *path, gint pos, const char *name);
|
||||
static void (*fp_gtk_style_context_set_path)(GtkStyleContext *context, GtkWidgetPath *path);
|
||||
static void (*fp_gtk_widget_path_unref) (GtkWidgetPath *path);
|
||||
static GtkStyleContext* (*fp_gtk_style_context_new) (void);
|
||||
|
||||
|
||||
// ---------- fp_g_dbus_* ----------
|
||||
static GVariant *(*fp_g_dbus_proxy_call_sync)(
|
||||
GDBusProxy *proxy,
|
||||
const gchar *method_name,
|
||||
GVariant *parameters,
|
||||
GDBusCallFlags flags,
|
||||
gint timeout_msec,
|
||||
GCancellable *cancellable,
|
||||
GError **error
|
||||
);
|
||||
|
||||
static GDBusProxy *(*fp_g_dbus_proxy_new_sync)(
|
||||
GDBusConnection *connection,
|
||||
GDBusProxyFlags flags,
|
||||
GDBusInterfaceInfo *info,
|
||||
const gchar *name,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
GCancellable *cancellable,
|
||||
GError **error
|
||||
);
|
||||
|
||||
static const gchar *(*fp_g_dbus_connection_get_unique_name)(
|
||||
GDBusConnection *connection
|
||||
);
|
||||
|
||||
static GDBusConnection *(*fp_g_bus_get_sync)(GBusType bus_type,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
static guint (*fp_g_dbus_connection_signal_subscribe)(
|
||||
GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
const gchar *interface_name,
|
||||
const gchar *member,
|
||||
const gchar *object_path,
|
||||
const gchar *arg0,
|
||||
GDBusSignalFlags flags,
|
||||
GDBusSignalCallback callback,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_data_free_func
|
||||
);
|
||||
|
||||
static void (*fp_g_dbus_connection_signal_unsubscribe)(
|
||||
GDBusConnection *connection,
|
||||
guint subscription_id
|
||||
);
|
||||
|
||||
static GVariant *(*fp_g_dbus_proxy_call_with_unix_fd_list_sync)(
|
||||
GDBusProxy *proxy,
|
||||
const gchar *method_name,
|
||||
GVariant *parameters,
|
||||
GDBusCallFlags flags,
|
||||
gint timeout_msec,
|
||||
GUnixFDList *fd_list,
|
||||
GUnixFDList **out_fd_list,
|
||||
GCancellable *cancellable,
|
||||
GError **error
|
||||
);
|
||||
|
||||
static GVariant *(*fp_g_dbus_connection_call_sync)(
|
||||
GDBusConnection *connection,
|
||||
const gchar *bus_name,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
const gchar *method_name,
|
||||
GVariant *parameters,
|
||||
const GVariantType *reply_type,
|
||||
GDBusCallFlags flags,
|
||||
gint timeout_msec,
|
||||
GCancellable *cancellable,
|
||||
GError **error
|
||||
);
|
||||
|
||||
// ---------- fp_g_variant_* ----------
|
||||
|
||||
static GVariant *(*fp_g_variant_new)(const gchar *format_string, ...);
|
||||
|
||||
static GVariant *(*fp_g_variant_new_string)(const gchar *string);
|
||||
|
||||
static GVariant *(*fp_g_variant_new_boolean)(gboolean value);
|
||||
|
||||
static GVariant *(*fp_g_variant_new_uint32)(guint32 value);
|
||||
|
||||
static void (*fp_g_variant_get)(GVariant *value, const gchar *format_string, ...);
|
||||
|
||||
static const gchar *(*fp_g_variant_get_string)(GVariant *value, gsize *length);
|
||||
|
||||
static guint32 (*fp_g_variant_get_uint32)(GVariant *value);
|
||||
|
||||
static gboolean (*fp_g_variant_lookup)(GVariant *dictionary,
|
||||
const gchar *key,
|
||||
const gchar *format_string,
|
||||
...);
|
||||
|
||||
static gboolean (*fp_g_variant_iter_loop)(GVariantIter *iter, const gchar *format_string, ...);
|
||||
|
||||
static void (*fp_g_variant_unref)(GVariant *value);
|
||||
|
||||
static void (*fp_g_variant_builder_init)(GVariantBuilder *builder, const GVariantType *type);
|
||||
|
||||
static void (*fp_g_variant_builder_add)(GVariantBuilder *builder, const gchar *format_string, ...);
|
||||
|
||||
static GVariant *(*fp_g_variant_lookup_value)(GVariant *dictionary,
|
||||
const gchar *key,
|
||||
const GVariantType *expected_type);
|
||||
|
||||
static gsize (*fp_g_variant_iter_init)(GVariantIter *iter, GVariant *value);
|
||||
|
||||
static gsize (*fp_g_variant_iter_n_children)(GVariantIter *iter);
|
||||
|
||||
|
||||
// ---------- fp_g_string_* ----------
|
||||
|
||||
static GString *(*fp_g_string_new)(const gchar *init);
|
||||
|
||||
static GString *(*fp_g_string_erase)(GString *string, gssize pos, gssize len);
|
||||
|
||||
static GString *(*fp_g_string_set_size)(GString* string, gsize len);
|
||||
|
||||
static gchar *(*fp_g_string_free)(GString *string, gboolean free_segment);
|
||||
|
||||
static guint (*fp_g_string_replace)(GString *string,
|
||||
const gchar *find,
|
||||
const gchar *replace,
|
||||
guint limit);
|
||||
|
||||
static void *(*fp_g_string_printf)(GString *string, const gchar *format, ...);
|
||||
|
||||
static gboolean (*fp_g_uuid_string_is_valid)(const gchar *str);
|
||||
|
||||
|
||||
// ---------- * ----------
|
||||
static void (*fp_g_error_free)(GError *error);
|
||||
|
||||
static gint (*fp_g_unix_fd_list_get)(GUnixFDList *list, gint index_, GError **error);
|
||||
|
||||
#endif /* !_GTK3_INTERFACE_H */
|
||||
|
||||
164
src/java.desktop/unix/native/libawt_wlawt/gtk_interface.c
Normal file
164
src/java.desktop/unix/native/libawt_wlawt/gtk_interface.c
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#ifdef HEADLESS
|
||||
#error This file should not be included in headless library
|
||||
#endif
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "jvm_md.h"
|
||||
|
||||
#include "gtk_interface.h"
|
||||
|
||||
GtkApi* gtk3_load(JNIEnv *env, const char* lib_name);
|
||||
|
||||
gboolean gtk3_check(const char* lib_name, gboolean load);
|
||||
|
||||
GtkApi *gtk;
|
||||
|
||||
typedef struct {
|
||||
GtkVersion version;
|
||||
const char* name;
|
||||
const char* vname;
|
||||
GtkApi* (*load)(JNIEnv *env, const char* lib_name);
|
||||
gboolean (*check)(const char* lib_name, gboolean load);
|
||||
} GtkLib;
|
||||
|
||||
static GtkLib gtk_libs[] = {
|
||||
{
|
||||
GTK_3,
|
||||
JNI_LIB_NAME("gtk-3"),
|
||||
VERSIONED_JNI_LIB_NAME("gtk-3", "0"),
|
||||
>k3_load,
|
||||
>k3_check
|
||||
},
|
||||
};
|
||||
|
||||
static GtkLib** get_libs_order(GtkVersion version) {
|
||||
static GtkLib** load_order;
|
||||
static int n_libs = 0;
|
||||
if (!n_libs) {
|
||||
n_libs = sizeof(gtk_libs) / sizeof(GtkLib);
|
||||
load_order = calloc(n_libs + 1, sizeof(GtkLib *));
|
||||
if (load_order == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
int i, first = 0;
|
||||
for (i = 0; i < n_libs; i++) {
|
||||
load_order[i] = >k_libs[i];
|
||||
if (load_order[i]->version == version) {
|
||||
first = i;
|
||||
}
|
||||
}
|
||||
if (first) {
|
||||
for (i = first; i > 0; i--) {
|
||||
load_order[i] = load_order[i - 1];
|
||||
}
|
||||
load_order[0] = >k_libs[first];
|
||||
}
|
||||
return load_order;
|
||||
}
|
||||
|
||||
static GtkLib* get_loaded() {
|
||||
GtkLib** libs = get_libs_order(GTK_ANY);
|
||||
if (libs == NULL) return NULL;
|
||||
while(!gtk && *libs) {
|
||||
GtkLib* lib = *libs++;
|
||||
if (lib->check(lib->vname, /* load = */FALSE)) {
|
||||
return lib;
|
||||
}
|
||||
if (lib->check(lib->name, /* load = */FALSE)) {
|
||||
return lib;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gboolean gtk_load(JNIEnv *env, GtkVersion version, gboolean verbose) {
|
||||
if (gtk == NULL) {
|
||||
GtkLib* lib = get_loaded();
|
||||
if (lib) {
|
||||
if (verbose) {
|
||||
fprintf(stderr, "Looking for GTK%d library...\n", lib->version);
|
||||
}
|
||||
gtk = lib->load(env, lib->vname);
|
||||
if (!gtk) {
|
||||
gtk = lib->load(env, lib->name);
|
||||
}
|
||||
} else {
|
||||
GtkLib** libs = get_libs_order(version);
|
||||
while (!gtk && libs && *libs) {
|
||||
lib = *libs++;
|
||||
if (version == GTK_ANY || lib->version == version) {
|
||||
if (verbose) {
|
||||
fprintf(stderr, "Looking for GTK%d library...\n", lib->version);
|
||||
}
|
||||
gtk = lib->load(env, lib->vname);
|
||||
if (!gtk) {
|
||||
gtk = lib->load(env, lib->name);
|
||||
}
|
||||
if (verbose && !gtk) {
|
||||
fprintf(stderr, "Not found.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (verbose) {
|
||||
if (gtk) {
|
||||
fprintf(stderr, "GTK%d library loaded.\n", lib->version);
|
||||
} else {
|
||||
fprintf(stderr, "Failed to load GTK library.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
return gtk != NULL;
|
||||
}
|
||||
|
||||
static gboolean check_version(GtkVersion version) {
|
||||
GtkLib** libs = get_libs_order(version);
|
||||
if (libs == NULL) return FALSE;
|
||||
while (*libs) {
|
||||
GtkLib* lib = *libs++;
|
||||
if (lib->check(lib->vname, /* load = */TRUE)) {
|
||||
return TRUE;
|
||||
}
|
||||
if (lib->check(lib->name, /* load = */TRUE)) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean gtk_check_version(GtkVersion version) {
|
||||
if (gtk || get_loaded()) {
|
||||
return TRUE;
|
||||
}
|
||||
return check_version(version);
|
||||
}
|
||||
|
||||
835
src/java.desktop/unix/native/libawt_wlawt/gtk_interface.h
Normal file
835
src/java.desktop/unix/native/libawt_wlawt/gtk_interface.h
Normal file
@@ -0,0 +1,835 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#ifdef HEADLESS
|
||||
#error This file should not be included in headless library
|
||||
#endif
|
||||
|
||||
#ifndef _GTK_INTERFACE_H
|
||||
#define _GTK_INTERFACE_H
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE (0)
|
||||
#define TRUE (!FALSE)
|
||||
#endif
|
||||
|
||||
#define _G_TYPE_CIC(ip, gt, ct) ((ct*) ip)
|
||||
#define G_TYPE_CHECK_INSTANCE_CAST(instance, g_type, c_type) \
|
||||
(_G_TYPE_CIC ((instance), (g_type), c_type))
|
||||
#define GTK_TYPE_FILE_CHOOSER (fp_gtk_file_chooser_get_type ())
|
||||
#define GTK_FILE_CHOOSER(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_CHOOSER, GtkFileChooser))
|
||||
#define G_CALLBACK(f) ((GCallback) (f))
|
||||
#define G_TYPE_FUNDAMENTAL_SHIFT (2)
|
||||
#define G_TYPE_MAKE_FUNDAMENTAL(x) ((GType) ((x) << G_TYPE_FUNDAMENTAL_SHIFT))
|
||||
#define G_TYPE_OBJECT G_TYPE_MAKE_FUNDAMENTAL (20)
|
||||
#define GTK_STOCK_CANCEL "gtk-cancel"
|
||||
#define GTK_STOCK_SAVE "gtk-save"
|
||||
#define GTK_STOCK_OPEN "gtk-open"
|
||||
#define GDK_CURRENT_TIME 0L
|
||||
|
||||
#define G_TYPE_INVALID G_TYPE_MAKE_FUNDAMENTAL (0)
|
||||
#define G_TYPE_NONE G_TYPE_MAKE_FUNDAMENTAL (1)
|
||||
#define G_TYPE_INTERFACE G_TYPE_MAKE_FUNDAMENTAL (2)
|
||||
#define G_TYPE_CHAR G_TYPE_MAKE_FUNDAMENTAL (3)
|
||||
#define G_TYPE_UCHAR G_TYPE_MAKE_FUNDAMENTAL (4)
|
||||
#define G_TYPE_BOOLEAN G_TYPE_MAKE_FUNDAMENTAL (5)
|
||||
#define G_TYPE_INT G_TYPE_MAKE_FUNDAMENTAL (6)
|
||||
#define G_TYPE_UINT G_TYPE_MAKE_FUNDAMENTAL (7)
|
||||
#define G_TYPE_LONG G_TYPE_MAKE_FUNDAMENTAL (8)
|
||||
#define G_TYPE_ULONG G_TYPE_MAKE_FUNDAMENTAL (9)
|
||||
#define G_TYPE_INT64 G_TYPE_MAKE_FUNDAMENTAL (10)
|
||||
#define G_TYPE_UINT64 G_TYPE_MAKE_FUNDAMENTAL (11)
|
||||
#define G_TYPE_ENUM G_TYPE_MAKE_FUNDAMENTAL (12)
|
||||
#define G_TYPE_FLAGS G_TYPE_MAKE_FUNDAMENTAL (13)
|
||||
#define G_TYPE_FLOAT G_TYPE_MAKE_FUNDAMENTAL (14)
|
||||
#define G_TYPE_DOUBLE G_TYPE_MAKE_FUNDAMENTAL (15)
|
||||
#define G_TYPE_STRING G_TYPE_MAKE_FUNDAMENTAL (16)
|
||||
#define G_TYPE_POINTER G_TYPE_MAKE_FUNDAMENTAL (17)
|
||||
#define G_TYPE_BOXED G_TYPE_MAKE_FUNDAMENTAL (18)
|
||||
#define G_TYPE_PARAM G_TYPE_MAKE_FUNDAMENTAL (19)
|
||||
#define G_TYPE_OBJECT G_TYPE_MAKE_FUNDAMENTAL (20)
|
||||
|
||||
#define GTK_TYPE_BORDER ((*fp_gtk_border_get_type)())
|
||||
|
||||
#define G_TYPE_FUNDAMENTAL_SHIFT (2)
|
||||
#define G_TYPE_MAKE_FUNDAMENTAL(x) ((GType) ((x) << G_TYPE_FUNDAMENTAL_SHIFT))
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#define CONV_BUFFER_SIZE 128
|
||||
#define NO_SYMBOL_EXCEPTION 1
|
||||
|
||||
/* basic types */
|
||||
typedef char gchar;
|
||||
typedef short gshort;
|
||||
typedef int gint;
|
||||
typedef long glong;
|
||||
typedef float gfloat;
|
||||
typedef double gdouble;
|
||||
typedef void* gpointer;
|
||||
typedef gint gboolean;
|
||||
typedef signed char gint8;
|
||||
typedef signed short gint16;
|
||||
typedef signed int gint32;
|
||||
typedef unsigned char guchar;
|
||||
typedef unsigned char guint8;
|
||||
typedef unsigned short gushort;
|
||||
typedef unsigned short guint16;
|
||||
typedef unsigned int guint;
|
||||
typedef unsigned int guint32;
|
||||
typedef unsigned long gsize;
|
||||
typedef unsigned long gulong;
|
||||
typedef signed long long gint64;
|
||||
typedef unsigned long long guint64;
|
||||
typedef gulong GType;
|
||||
|
||||
typedef struct _GList GList;
|
||||
struct _GList
|
||||
{
|
||||
gpointer data;
|
||||
GList *next;
|
||||
GList *prev;
|
||||
};
|
||||
|
||||
typedef struct _GSList GSList;
|
||||
struct _GSList {
|
||||
gpointer data;
|
||||
GSList *next;
|
||||
};
|
||||
|
||||
typedef signed long gssize;
|
||||
typedef struct _GString GString;
|
||||
|
||||
struct _GString
|
||||
{
|
||||
gchar *str;
|
||||
gsize len;
|
||||
gsize allocated_len;
|
||||
};
|
||||
|
||||
typedef struct _GVariant GVariant;
|
||||
typedef struct _GVariantIter GVariantIter;
|
||||
struct _GVariantIter {
|
||||
/*< private >*/
|
||||
gsize x[16];
|
||||
};
|
||||
|
||||
typedef struct _GVariantType GVariantType;
|
||||
typedef struct _GVariantBuilder GVariantBuilder;
|
||||
|
||||
struct _GVariantBuilder {
|
||||
/*< private >*/
|
||||
union
|
||||
{
|
||||
struct {
|
||||
gsize partial_magic;
|
||||
const GVariantType *type;
|
||||
gsize y[14];
|
||||
} s;
|
||||
gsize x[16];
|
||||
} u;
|
||||
};
|
||||
|
||||
|
||||
#define G_VARIANT_TYPE_VARDICT ((const GVariantType *) "a{sv}")
|
||||
#define G_VARIANT_TYPE_ARRAY ((const GVariantType *) "a*")
|
||||
#define G_VARIANT_TYPE_STRING ((const GVariantType *) "s")
|
||||
|
||||
typedef struct _GDBusProxy GDBusProxy;
|
||||
typedef enum {
|
||||
G_DBUS_CALL_FLAGS_NONE = 0,
|
||||
G_DBUS_CALL_FLAGS_NO_AUTO_START = (1<<0),
|
||||
G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION = (1<<1)
|
||||
} GDBusCallFlags;
|
||||
|
||||
typedef void GMainContext;
|
||||
typedef void GUnixFDList;
|
||||
|
||||
typedef void GDBusConnection;
|
||||
typedef enum /*< flags >*/
|
||||
{
|
||||
G_DBUS_SIGNAL_FLAGS_NONE = 0,
|
||||
G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE = (1<<0),
|
||||
G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE = (1<<1),
|
||||
G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH = (1<<2)
|
||||
} GDBusSignalFlags;
|
||||
|
||||
typedef void (*GDBusSignalCallback) (GDBusConnection *connection,
|
||||
const gchar *sender_name,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
const gchar *signal_name,
|
||||
GVariant *parameters,
|
||||
gpointer user_data);
|
||||
|
||||
typedef struct _GCancellable GCancellable;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
G_BUS_TYPE_STARTER = -1,
|
||||
G_BUS_TYPE_NONE = 0,
|
||||
G_BUS_TYPE_SYSTEM = 1,
|
||||
G_BUS_TYPE_SESSION = 2
|
||||
} GBusType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
G_DBUS_PROXY_FLAGS_NONE = 0,
|
||||
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES = (1<<0),
|
||||
G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS = (1<<1),
|
||||
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START = (1<<2),
|
||||
G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES = (1<<3),
|
||||
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START_AT_CONSTRUCTION = (1<<4)
|
||||
} GDBusProxyFlags;
|
||||
|
||||
typedef struct _GDBusInterfaceInfo GDBusInterfaceInfo;
|
||||
|
||||
typedef enum {
|
||||
BUTTON, /* GtkButton */
|
||||
CHECK_BOX, /* GtkCheckButton */
|
||||
CHECK_BOX_MENU_ITEM, /* GtkCheckMenuItem */
|
||||
COLOR_CHOOSER, /* GtkColorSelectionDialog */
|
||||
COMBO_BOX, /* GtkComboBox */
|
||||
COMBO_BOX_ARROW_BUTTON, /* GtkComboBoxEntry */
|
||||
COMBO_BOX_TEXT_FIELD, /* GtkComboBoxEntry */
|
||||
DESKTOP_ICON, /* GtkLabel */
|
||||
DESKTOP_PANE, /* GtkContainer */
|
||||
EDITOR_PANE, /* GtkTextView */
|
||||
FORMATTED_TEXT_FIELD, /* GtkEntry */
|
||||
HANDLE_BOX, /* GtkHandleBox */
|
||||
HPROGRESS_BAR, /* GtkProgressBar */
|
||||
HSCROLL_BAR, /* GtkHScrollbar */
|
||||
HSCROLL_BAR_BUTTON_LEFT, /* GtkHScrollbar */
|
||||
HSCROLL_BAR_BUTTON_RIGHT, /* GtkHScrollbar */
|
||||
HSCROLL_BAR_TRACK, /* GtkHScrollbar */
|
||||
HSCROLL_BAR_THUMB, /* GtkHScrollbar */
|
||||
HSEPARATOR, /* GtkHSeparator */
|
||||
HSLIDER, /* GtkHScale */
|
||||
HSLIDER_TRACK, /* GtkHScale */
|
||||
HSLIDER_THUMB, /* GtkHScale */
|
||||
HSPLIT_PANE_DIVIDER, /* GtkHPaned */
|
||||
INTERNAL_FRAME, /* GtkWindow */
|
||||
INTERNAL_FRAME_TITLE_PANE, /* GtkLabel */
|
||||
IMAGE, /* GtkImage */
|
||||
LABEL, /* GtkLabel */
|
||||
LIST, /* GtkTreeView */
|
||||
MENU, /* GtkMenu */
|
||||
MENU_BAR, /* GtkMenuBar */
|
||||
MENU_ITEM, /* GtkMenuItem */
|
||||
MENU_ITEM_ACCELERATOR, /* GtkLabel */
|
||||
OPTION_PANE, /* GtkMessageDialog */
|
||||
PANEL, /* GtkContainer */
|
||||
PASSWORD_FIELD, /* GtkEntry */
|
||||
POPUP_MENU, /* GtkMenu */
|
||||
POPUP_MENU_SEPARATOR, /* GtkSeparatorMenuItem */
|
||||
RADIO_BUTTON, /* GtkRadioButton */
|
||||
RADIO_BUTTON_MENU_ITEM, /* GtkRadioMenuItem */
|
||||
ROOT_PANE, /* GtkContainer */
|
||||
SCROLL_PANE, /* GtkScrolledWindow */
|
||||
SPINNER, /* GtkSpinButton */
|
||||
SPINNER_ARROW_BUTTON, /* GtkSpinButton */
|
||||
SPINNER_TEXT_FIELD, /* GtkSpinButton */
|
||||
SPLIT_PANE, /* GtkPaned */
|
||||
TABBED_PANE, /* GtkNotebook */
|
||||
TABBED_PANE_TAB_AREA, /* GtkNotebook */
|
||||
TABBED_PANE_CONTENT, /* GtkNotebook */
|
||||
TABBED_PANE_TAB, /* GtkNotebook */
|
||||
TABLE, /* GtkTreeView */
|
||||
TABLE_HEADER, /* GtkButton */
|
||||
TEXT_AREA, /* GtkTextView */
|
||||
TEXT_FIELD, /* GtkEntry */
|
||||
TEXT_PANE, /* GtkTextView */
|
||||
TITLED_BORDER, /* GtkFrame */
|
||||
TOGGLE_BUTTON, /* GtkToggleButton */
|
||||
TOOL_BAR, /* GtkToolbar */
|
||||
TOOL_BAR_DRAG_WINDOW, /* GtkToolbar */
|
||||
TOOL_BAR_SEPARATOR, /* GtkSeparatorToolItem */
|
||||
TOOL_TIP, /* GtkWindow */
|
||||
TREE, /* GtkTreeView */
|
||||
TREE_CELL, /* GtkTreeView */
|
||||
VIEWPORT, /* GtkViewport */
|
||||
VPROGRESS_BAR, /* GtkProgressBar */
|
||||
VSCROLL_BAR, /* GtkVScrollbar */
|
||||
VSCROLL_BAR_BUTTON_UP, /* GtkVScrollbar */
|
||||
VSCROLL_BAR_BUTTON_DOWN, /* GtkVScrollbar */
|
||||
VSCROLL_BAR_TRACK, /* GtkVScrollbar */
|
||||
VSCROLL_BAR_THUMB, /* GtkVScrollbar */
|
||||
VSEPARATOR, /* GtkVSeparator */
|
||||
VSLIDER, /* GtkVScale */
|
||||
VSLIDER_TRACK, /* GtkVScale */
|
||||
VSLIDER_THUMB, /* GtkVScale */
|
||||
VSPLIT_PANE_DIVIDER, /* GtkVPaned */
|
||||
WIDGET_TYPE_SIZE
|
||||
} WidgetType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
_GTK_ARROW_TYPE,
|
||||
_GTK_BUTTON_TYPE,
|
||||
_GTK_CHECK_BUTTON_TYPE,
|
||||
_GTK_CHECK_MENU_ITEM_TYPE,
|
||||
_GTK_COLOR_SELECTION_DIALOG_TYPE,
|
||||
_GTK_COMBO_BOX_TYPE,
|
||||
_GTK_COMBO_BOX_ARROW_BUTTON_TYPE,
|
||||
_GTK_COMBO_BOX_TEXT_FIELD_TYPE,
|
||||
_GTK_CONTAINER_TYPE,
|
||||
_GTK_ENTRY_TYPE,
|
||||
_GTK_FRAME_TYPE,
|
||||
_GTK_HANDLE_BOX_TYPE,
|
||||
_GTK_HPANED_TYPE,
|
||||
_GTK_HPROGRESS_BAR_TYPE,
|
||||
_GTK_HSCALE_TYPE,
|
||||
_GTK_HSCROLLBAR_TYPE,
|
||||
_GTK_HSEPARATOR_TYPE,
|
||||
_GTK_IMAGE_TYPE,
|
||||
_GTK_MENU_TYPE,
|
||||
_GTK_MENU_BAR_TYPE,
|
||||
_GTK_MENU_ITEM_TYPE,
|
||||
_GTK_NOTEBOOK_TYPE,
|
||||
_GTK_LABEL_TYPE,
|
||||
_GTK_RADIO_BUTTON_TYPE,
|
||||
_GTK_RADIO_MENU_ITEM_TYPE,
|
||||
_GTK_SCROLLED_WINDOW_TYPE,
|
||||
_GTK_SEPARATOR_MENU_ITEM_TYPE,
|
||||
_GTK_SEPARATOR_TOOL_ITEM_TYPE,
|
||||
_GTK_SPIN_BUTTON_TYPE,
|
||||
_GTK_TEXT_VIEW_TYPE,
|
||||
_GTK_TOGGLE_BUTTON_TYPE,
|
||||
_GTK_TOOLBAR_TYPE,
|
||||
_GTK_TOOLTIP_TYPE,
|
||||
_GTK_TREE_VIEW_TYPE,
|
||||
_GTK_VIEWPORT_TYPE,
|
||||
_GTK_VPANED_TYPE,
|
||||
_GTK_VPROGRESS_BAR_TYPE,
|
||||
_GTK_VSCALE_TYPE,
|
||||
_GTK_VSCROLLBAR_TYPE,
|
||||
_GTK_VSEPARATOR_TYPE,
|
||||
_GTK_WINDOW_TYPE,
|
||||
_GTK_DIALOG_TYPE,
|
||||
_GTK_WIDGET_TYPE_SIZE
|
||||
} GtkWidgetType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GTK_STATE_NORMAL,
|
||||
GTK_STATE_ACTIVE,
|
||||
GTK_STATE_PRELIGHT,
|
||||
GTK_STATE_SELECTED,
|
||||
GTK_STATE_INSENSITIVE,
|
||||
GTK_STATE_INCONSISTENT,
|
||||
GTK_STATE_FOCUSED
|
||||
} GtkStateType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GTK_SHADOW_NONE,
|
||||
GTK_SHADOW_IN,
|
||||
GTK_SHADOW_OUT,
|
||||
GTK_SHADOW_ETCHED_IN,
|
||||
GTK_SHADOW_ETCHED_OUT
|
||||
} GtkShadowType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GTK_EXPANDER_COLLAPSED,
|
||||
GTK_EXPANDER_SEMI_COLLAPSED,
|
||||
GTK_EXPANDER_SEMI_EXPANDED,
|
||||
GTK_EXPANDER_EXPANDED
|
||||
} GtkExpanderStyle;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GTK_ICON_SIZE_INVALID,
|
||||
GTK_ICON_SIZE_MENU,
|
||||
GTK_ICON_SIZE_SMALL_TOOLBAR,
|
||||
GTK_ICON_SIZE_LARGE_TOOLBAR,
|
||||
GTK_ICON_SIZE_BUTTON,
|
||||
GTK_ICON_SIZE_DND,
|
||||
GTK_ICON_SIZE_DIALOG
|
||||
} GtkIconSize;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GTK_ORIENTATION_HORIZONTAL,
|
||||
GTK_ORIENTATION_VERTICAL
|
||||
} GtkOrientation;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
FOREGROUND,
|
||||
BACKGROUND,
|
||||
TEXT_FOREGROUND,
|
||||
TEXT_BACKGROUND,
|
||||
FOCUS,
|
||||
LIGHT,
|
||||
DARK,
|
||||
MID,
|
||||
BLACK,
|
||||
WHITE
|
||||
} ColorType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GTK_FONT_NAME,
|
||||
GTK_ICON_SIZES,
|
||||
GTK_XFT_DPI,
|
||||
GTK_CURSOR_BLINK,
|
||||
GTK_CURSOR_BLINK_TIME
|
||||
} Setting;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GTK_ARROW_UP,
|
||||
GTK_ARROW_DOWN,
|
||||
GTK_ARROW_LEFT,
|
||||
GTK_ARROW_RIGHT,
|
||||
GTK_ARROW_NONE
|
||||
} GtkArrowType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GTK_TEXT_DIR_NONE,
|
||||
GTK_TEXT_DIR_LTR,
|
||||
GTK_TEXT_DIR_RTL
|
||||
} GtkTextDirection;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GTK_POS_LEFT,
|
||||
GTK_POS_RIGHT,
|
||||
GTK_POS_TOP,
|
||||
GTK_POS_BOTTOM
|
||||
} GtkPositionType;
|
||||
|
||||
/* SynthConstants */
|
||||
static const gint ENABLED = 1 << 0;
|
||||
static const gint MOUSE_OVER = 1 << 1;
|
||||
static const gint PRESSED = 1 << 2;
|
||||
static const gint DISABLED = 1 << 3;
|
||||
static const gint FOCUSED = 1 << 8;
|
||||
static const gint SELECTED = 1 << 9;
|
||||
static const gint DEFAULT = 1 << 10;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GTK_ANY = 0,
|
||||
GTK_3 = 3
|
||||
} GtkVersion;
|
||||
|
||||
typedef enum {
|
||||
GTK_RESPONSE_NONE = -1,
|
||||
GTK_RESPONSE_REJECT = -2,
|
||||
GTK_RESPONSE_ACCEPT = -3,
|
||||
GTK_RESPONSE_DELETE_EVENT = -4,
|
||||
GTK_RESPONSE_OK = -5,
|
||||
GTK_RESPONSE_CANCEL = -6,
|
||||
GTK_RESPONSE_CLOSE = -7,
|
||||
GTK_RESPONSE_YES = -8,
|
||||
GTK_RESPONSE_NO = -9,
|
||||
GTK_RESPONSE_APPLY = -10,
|
||||
GTK_RESPONSE_HELP = -11
|
||||
} GtkResponseType;
|
||||
|
||||
typedef enum {
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
GTK_FILE_CHOOSER_ACTION_SAVE,
|
||||
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
|
||||
GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
|
||||
} GtkFileChooserAction;
|
||||
|
||||
typedef enum {
|
||||
GTK_FILE_FILTER_FILENAME = 1 << 0,
|
||||
GTK_FILE_FILTER_URI = 1 << 1,
|
||||
GTK_FILE_FILTER_DISPLAY_NAME = 1 << 2,
|
||||
GTK_FILE_FILTER_MIME_TYPE = 1 << 3
|
||||
} GtkFileFilterFlags;
|
||||
|
||||
typedef enum {
|
||||
GDK_COLORSPACE_RGB
|
||||
} GdkColorspace;
|
||||
|
||||
typedef enum {
|
||||
GDK_INTERP_NEAREST,
|
||||
GDK_INTERP_TILES,
|
||||
GDK_INTERP_BILINEAR,
|
||||
GDK_INTERP_HYPER
|
||||
} GdkInterpType;
|
||||
|
||||
typedef enum {
|
||||
G_CONNECT_AFTER = 1 << 0, G_CONNECT_SWAPPED = 1 << 1
|
||||
} GConnectFlags;
|
||||
//------------------------------
|
||||
|
||||
typedef guint32 GQuark;
|
||||
struct _GError
|
||||
{
|
||||
GQuark domain;
|
||||
gint code;
|
||||
gchar *message;
|
||||
};
|
||||
typedef struct _GError GError;
|
||||
|
||||
typedef void GdkScreen;
|
||||
typedef void GtkWindow;
|
||||
typedef void GdkWindow;
|
||||
typedef void GClosure;
|
||||
typedef void GtkFileChooser;
|
||||
typedef void GtkFileFilter;
|
||||
|
||||
typedef struct {
|
||||
gint x;
|
||||
gint y;
|
||||
gint width;
|
||||
gint height;
|
||||
} GdkRectangle;
|
||||
|
||||
typedef struct {
|
||||
GtkFileFilterFlags contains;
|
||||
const gchar *filename;
|
||||
const gchar *uri;
|
||||
const gchar *display_name;
|
||||
const gchar *mime_type;
|
||||
} GtkFileFilterInfo;
|
||||
typedef gboolean (*GtkFileFilterFunc)(const GtkFileFilterInfo *filter_info,
|
||||
gpointer data);
|
||||
typedef void (*GClosureNotify)(gpointer data, GClosure *closure);
|
||||
typedef void (*GDestroyNotify)(gpointer data);
|
||||
typedef void (*GCallback)(void);
|
||||
|
||||
typedef void GdkPixbuf;
|
||||
typedef void (* GdkPixbufDestroyNotify) (guchar *pixels, gpointer data);
|
||||
|
||||
typedef struct GtkApi {
|
||||
int version;
|
||||
gboolean (*show_uri_load)(JNIEnv *env);
|
||||
gboolean (*unload)();
|
||||
void (*flush_event_loop)();
|
||||
gchar* (*gtk_check_version)(guint required_major, guint required_minor, guint required_micro);
|
||||
jobject (*get_setting)(JNIEnv *env, Setting property);
|
||||
|
||||
void (*paint_arrow)(WidgetType widget_type, GtkStateType state_type,
|
||||
GtkShadowType shadow_type, const gchar *detail,
|
||||
gint x, gint y, gint width, gint height,
|
||||
GtkArrowType arrow_type, gboolean fill);
|
||||
void (*paint_box)(WidgetType widget_type, GtkStateType state_type,
|
||||
GtkShadowType shadow_type, const gchar *detail,
|
||||
gint x, gint y, gint width, gint height,
|
||||
gint synth_state, GtkTextDirection dir);
|
||||
void (*paint_box_gap)(WidgetType widget_type, GtkStateType state_type,
|
||||
GtkShadowType shadow_type, const gchar *detail,
|
||||
gint x, gint y, gint width, gint height,
|
||||
GtkPositionType gap_side, gint gap_x, gint gap_width);
|
||||
void (*paint_expander)(WidgetType widget_type, GtkStateType state_type,
|
||||
const gchar *detail, gint x, gint y, gint width, gint height,
|
||||
GtkExpanderStyle expander_style);
|
||||
void (*paint_extension)(WidgetType widget_type, GtkStateType state_type,
|
||||
GtkShadowType shadow_type, const gchar *detail,
|
||||
gint x, gint y, gint width, gint height, GtkPositionType gap_side);
|
||||
void (*paint_flat_box)(WidgetType widget_type, GtkStateType state_type,
|
||||
GtkShadowType shadow_type, const gchar *detail,
|
||||
gint x, gint y, gint width, gint height, gboolean has_focus);
|
||||
void (*paint_focus)(WidgetType widget_type, GtkStateType state_type,
|
||||
const char *detail, gint x, gint y, gint width, gint height);
|
||||
void (*paint_handle)(WidgetType widget_type, GtkStateType state_type,
|
||||
GtkShadowType shadow_type, const gchar *detail,
|
||||
gint x, gint y, gint width, gint height, GtkOrientation orientation);
|
||||
void (*paint_hline)(WidgetType widget_type, GtkStateType state_type,
|
||||
const gchar *detail, gint x, gint y, gint width, gint height);
|
||||
void (*paint_vline)(WidgetType widget_type, GtkStateType state_type,
|
||||
const gchar *detail, gint x, gint y, gint width, gint height);
|
||||
void (*paint_option)(WidgetType widget_type, gint synth_state,
|
||||
const gchar *detail, gint x, gint y, gint width, gint height);
|
||||
void (*paint_shadow)(WidgetType widget_type, GtkStateType state_type,
|
||||
GtkShadowType shadow_type, const gchar *detail,
|
||||
gint x, gint y, gint width, gint height,
|
||||
gint synth_state, GtkTextDirection dir);
|
||||
void (*paint_slider)(WidgetType widget_type, GtkStateType state_type,
|
||||
GtkShadowType shadow_type, const gchar *detail,
|
||||
gint x, gint y, gint width, gint height, GtkOrientation orientation,
|
||||
gboolean has_focus);
|
||||
void (*paint_background)(WidgetType widget_type, GtkStateType state_type,
|
||||
gint x, gint y, gint width, gint height);
|
||||
void (*paint_check)(WidgetType widget_type, gint synth_state,
|
||||
const gchar *detail, gint x, gint y, gint width, gint height);
|
||||
void (*set_range_value)(WidgetType widget_type, jdouble value,
|
||||
jdouble min, jdouble max, jdouble visible);
|
||||
|
||||
void (*init_painting)(JNIEnv *env, gint w, gint h, gint scale);
|
||||
gint (*copy_image)(gint *dest, gint width, gint height);
|
||||
|
||||
gint (*get_xthickness)(JNIEnv *env, WidgetType widget_type);
|
||||
gint (*get_ythickness)(JNIEnv *env, WidgetType widget_type);
|
||||
gint (*get_color_for_state)(JNIEnv *env, WidgetType widget_type,
|
||||
GtkStateType state_type, ColorType color_type);
|
||||
jobject (*get_class_value)(JNIEnv *env, WidgetType widget_type,
|
||||
const char* key);
|
||||
|
||||
jstring (*get_pango_font_name)(JNIEnv *env, WidgetType widget_type);
|
||||
jboolean (*get_icon_data)(JNIEnv *env, gint widget_type,
|
||||
const gchar *stock_id, GtkIconSize size,
|
||||
GtkTextDirection direction, const char *detail,
|
||||
jmethodID icon_upcall_method, jobject this);
|
||||
jboolean (*get_file_icon_data)(JNIEnv *env, const char *filename,
|
||||
GError **error, jmethodID icon_upcall_method, jobject this);
|
||||
void (*gdk_threads_enter)(void);
|
||||
void (*gdk_threads_leave)(void);
|
||||
gboolean (*gtk_show_uri_on_window)(GdkWindow *parent, const gchar *uri,
|
||||
guint32 timestamp, GError **error);
|
||||
void (*g_free)(gpointer mem);
|
||||
|
||||
gchar* (*gtk_file_chooser_get_filename)(GtkFileChooser *chooser);
|
||||
void (*gtk_widget_hide)(void* widget);
|
||||
void (*gtk_main_quit)(void);
|
||||
void* (*gtk_file_chooser_dialog_new)(const gchar *title,
|
||||
GtkWindow *parent, GtkFileChooserAction action,
|
||||
const gchar *first_button_text, ...);
|
||||
gboolean (*gtk_file_chooser_set_current_folder)(GtkFileChooser *chooser, const gchar *filename);
|
||||
gboolean (*gtk_file_chooser_set_filename)(GtkFileChooser *chooser, const char *filename);
|
||||
void (*gtk_file_chooser_set_current_name)(GtkFileChooser *chooser, const gchar *name);
|
||||
void (*gtk_file_filter_add_custom)(GtkFileFilter *filter,
|
||||
GtkFileFilterFlags needed, GtkFileFilterFunc func, gpointer data,
|
||||
GDestroyNotify notify);
|
||||
void (*gtk_file_chooser_set_filter)(GtkFileChooser *chooser, GtkFileFilter *filter);
|
||||
GType (*gtk_file_chooser_get_type)(void);
|
||||
GtkFileFilter* (*gtk_file_filter_new)(void);
|
||||
void (*gtk_file_chooser_set_do_overwrite_confirmation)(
|
||||
GtkFileChooser *chooser, gboolean do_overwrite_confirmation);
|
||||
void (*gtk_file_chooser_set_select_multiple)(
|
||||
GtkFileChooser *chooser, gboolean select_multiple);
|
||||
gchar* (*gtk_file_chooser_get_current_folder)(GtkFileChooser *chooser);
|
||||
GSList* (*gtk_file_chooser_get_filenames)(GtkFileChooser *chooser);
|
||||
guint (*gtk_g_slist_length)(GSList *list);
|
||||
gulong (*g_signal_connect_data)(gpointer instance,
|
||||
const gchar *detailed_signal, GCallback c_handler, gpointer data,
|
||||
GClosureNotify destroy_data, GConnectFlags connect_flags);
|
||||
void (*gtk_widget_show)(void *widget);
|
||||
void (*gtk_main)(void);
|
||||
guint (*gtk_main_level)(void);
|
||||
gchar* (*g_path_get_dirname) (const gchar *file_name);
|
||||
void (*gtk_widget_destroy)(void *widget);
|
||||
void (*gtk_window_present)(void *window);
|
||||
void (*gtk_window_present_with_time)(void *window, guint32 timestamp);
|
||||
void (*gtk_window_move)(void *window, gint x, gint y);
|
||||
void (*gtk_window_resize)(void *window, gint width, gint height);
|
||||
|
||||
void (*g_object_unref)(gpointer object);
|
||||
GList* (*g_list_append) (GList *list, gpointer data);
|
||||
void (*g_list_free) (GList *list);
|
||||
void (*g_list_free_full) (GList *list, GDestroyNotify free_func);
|
||||
|
||||
|
||||
/* <for screencast, used only with GTK3> */
|
||||
GVariant *(*g_dbus_proxy_call_sync)(
|
||||
GDBusProxy *proxy,
|
||||
const gchar *method_name,
|
||||
GVariant *parameters,
|
||||
GDBusCallFlags flags,
|
||||
gint timeout_msec,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
GVariant *(*g_variant_new)(const gchar *format_string, ...);
|
||||
GVariant *(*g_variant_new_string)(const gchar *string);
|
||||
GVariant *(*g_variant_new_boolean)(gboolean value);
|
||||
GVariant *(*g_variant_new_uint32)(guint32 value);
|
||||
|
||||
|
||||
void (*g_variant_get)(GVariant *value,
|
||||
const gchar *format_string,
|
||||
...);
|
||||
const gchar *(*g_variant_get_string)(GVariant *value, gsize *length);
|
||||
guint32 (*g_variant_get_uint32)(GVariant *value);
|
||||
|
||||
gboolean (*g_variant_lookup)(GVariant *dictionary,
|
||||
const gchar *key,
|
||||
const gchar *format_string,
|
||||
...);
|
||||
gboolean (*g_variant_iter_loop)(GVariantIter *iter,
|
||||
const gchar *format_string,
|
||||
...);
|
||||
|
||||
void (*g_variant_unref)(GVariant *value);
|
||||
|
||||
void (*g_variant_builder_init)(GVariantBuilder *builder, //+
|
||||
const GVariantType *type);
|
||||
|
||||
void (*g_variant_builder_add)(GVariantBuilder *builder, //+
|
||||
const gchar *format_string,
|
||||
...);
|
||||
|
||||
GVariant *(*g_variant_lookup_value)(GVariant *dictionary,
|
||||
const gchar *key,
|
||||
const GVariantType *expected_type);
|
||||
|
||||
gsize (*g_variant_iter_init)(GVariantIter *iter, GVariant *value);
|
||||
|
||||
gsize (*g_variant_iter_n_children)(GVariantIter *iter);
|
||||
|
||||
|
||||
GString *(*g_string_new)(const gchar *init);
|
||||
|
||||
GString *(*g_string_erase)(GString *string, gssize pos, gssize len);
|
||||
|
||||
GString *(*g_string_set_size)(GString* string, gsize len);
|
||||
|
||||
|
||||
gchar *(*g_string_free)(GString *string, gboolean free_segment);
|
||||
|
||||
guint (*g_string_replace)(GString *string,
|
||||
const gchar *find,
|
||||
const gchar *replace,
|
||||
guint limit);
|
||||
|
||||
void *(*g_string_printf)(GString *string, const gchar *format, ...);
|
||||
|
||||
gboolean (*g_uuid_string_is_valid)(const gchar *str);
|
||||
|
||||
GDBusConnection *(*g_bus_get_sync)(GBusType bus_type,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
GDBusProxy *(*g_dbus_proxy_new_sync)(GDBusConnection *connection,
|
||||
GDBusProxyFlags flags,
|
||||
GDBusInterfaceInfo *info,
|
||||
const gchar *name,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
const gchar *(*g_dbus_connection_get_unique_name)(GDBusConnection *connection);
|
||||
|
||||
|
||||
|
||||
guint (*g_dbus_connection_signal_subscribe)(GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
const gchar *interface_name,
|
||||
const gchar *member,
|
||||
const gchar *object_path,
|
||||
const gchar *arg0,
|
||||
GDBusSignalFlags flags,
|
||||
GDBusSignalCallback callback,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_data_free_func);
|
||||
|
||||
void (*g_dbus_connection_signal_unsubscribe)(GDBusConnection *connection,
|
||||
guint subscription_id);
|
||||
|
||||
GVariant *(*g_dbus_proxy_call_with_unix_fd_list_sync)(GDBusProxy *proxy,
|
||||
const gchar *method_name,
|
||||
GVariant *parameters,
|
||||
GDBusCallFlags flags,
|
||||
gint timeout_msec,
|
||||
GUnixFDList *fd_list,
|
||||
GUnixFDList **out_fd_list,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
GVariant *(*g_dbus_connection_call_sync)(GDBusConnection *connection,
|
||||
const gchar *bus_name,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
const gchar *method_name,
|
||||
GVariant *parameters,
|
||||
const GVariantType *reply_type,
|
||||
GDBusCallFlags flags,
|
||||
gint timeout_msec,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean (*g_main_context_iteration)(GMainContext *context,
|
||||
gboolean may_block);
|
||||
GMainContext *(*g_main_context_default)();
|
||||
gboolean (*g_main_context_is_owner)(GMainContext* context);
|
||||
|
||||
void (*g_error_free)(GError *error);
|
||||
|
||||
gint (*g_unix_fd_list_get)(GUnixFDList *list,
|
||||
gint index_,
|
||||
GError **error);
|
||||
|
||||
GdkPixbuf *(*gdk_pixbuf_new)(GdkColorspace colorspace,
|
||||
gboolean has_alpha,
|
||||
int bits_per_sample,
|
||||
int width,
|
||||
int height);
|
||||
|
||||
|
||||
GdkPixbuf *(*gdk_pixbuf_new_from_data)(
|
||||
const guchar *data,
|
||||
GdkColorspace colorspace,
|
||||
gboolean has_alpha,
|
||||
int bits_per_sample,
|
||||
int width,
|
||||
int height,
|
||||
int rowstride,
|
||||
GdkPixbufDestroyNotify destroy_fn,
|
||||
gpointer destroy_fn_data
|
||||
);
|
||||
|
||||
|
||||
GdkPixbuf *(*gdk_pixbuf_scale_simple)(GdkPixbuf *src,
|
||||
int dest_width,
|
||||
int dest_heigh,
|
||||
GdkInterpType interp_type
|
||||
);
|
||||
|
||||
guchar* (*gdk_pixbuf_get_pixels) (const GdkPixbuf* pixbuf);
|
||||
|
||||
|
||||
void (*gdk_pixbuf_copy_area) (
|
||||
const GdkPixbuf* src_pixbuf,
|
||||
int src_x,
|
||||
int src_y,
|
||||
int width,
|
||||
int height,
|
||||
GdkPixbuf* dest_pixbuf,
|
||||
int dest_x,
|
||||
int dest_y
|
||||
);
|
||||
|
||||
/* </for screencast, used only with GTK3> */
|
||||
} GtkApi;
|
||||
|
||||
gboolean gtk_load(JNIEnv *env, GtkVersion version, gboolean verbose);
|
||||
gboolean gtk_check_version(GtkVersion version);
|
||||
|
||||
typedef struct _GThreadFunctions GThreadFunctions;
|
||||
static gboolean (*fp_g_thread_get_initialized)(void);
|
||||
static void (*fp_g_thread_init)(GThreadFunctions *vtable);
|
||||
static void (*fp_gdk_threads_init)(void);
|
||||
static void (*fp_gdk_threads_enter)(void);
|
||||
static void (*fp_gdk_threads_leave)(void);
|
||||
|
||||
extern GtkApi* gtk;
|
||||
|
||||
#endif /* !_GTK_INTERFACE_H */
|
||||
|
||||
291
src/java.desktop/unix/native/libawt_wlawt/swing_GTKEngine.c
Normal file
291
src/java.desktop/unix/native/libawt_wlawt/swing_GTKEngine.c
Normal file
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#ifdef HEADLESS
|
||||
#error This file should not be included in headless library
|
||||
#endif
|
||||
|
||||
#include "gtk_interface.h"
|
||||
#include "com_sun_java_swing_plaf_gtk_GTKEngine.h"
|
||||
#include <jni_util.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Static buffer for conversion from java.lang.String to UTF-8 */
|
||||
static char conversionBuffer[(CONV_BUFFER_SIZE - 1) * 3 + 1];
|
||||
|
||||
const char *getStrFor(JNIEnv *env, jstring val)
|
||||
{
|
||||
int length = (*env)->GetStringLength(env, val);
|
||||
if (length > CONV_BUFFER_SIZE-1) {
|
||||
length = CONV_BUFFER_SIZE-1;
|
||||
}
|
||||
|
||||
memset(conversionBuffer, 0, sizeof(conversionBuffer));
|
||||
(*env)->GetStringUTFRegion(env, val, 0, length, conversionBuffer);
|
||||
return conversionBuffer;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1arrow(
|
||||
JNIEnv *env, jobject this,
|
||||
jint widget_type, jint state, jint shadow_type, jstring detail,
|
||||
jint x, jint y, jint w, jint h, jint arrow_type)
|
||||
{
|
||||
gtk->gdk_threads_enter();
|
||||
gtk->paint_arrow(widget_type, state, shadow_type, getStrFor(env, detail),
|
||||
x, y, w, h, arrow_type, TRUE);
|
||||
gtk->gdk_threads_leave();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1box(
|
||||
JNIEnv *env, jobject this,
|
||||
jint widget_type, jint state, jint shadow_type, jstring detail,
|
||||
jint x, jint y, jint w, jint h,
|
||||
jint synth_state, jint dir)
|
||||
{
|
||||
gtk->gdk_threads_enter();
|
||||
gtk->paint_box(widget_type, state, shadow_type, getStrFor(env, detail),
|
||||
x, y, w, h, synth_state, dir);
|
||||
gtk->gdk_threads_leave();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1box_1gap(
|
||||
JNIEnv *env, jobject this,
|
||||
jint widget_type, jint state, jint shadow_type, jstring detail,
|
||||
jint x, jint y, jint w, jint h,
|
||||
jint gap_side, jint gap_x, jint gap_w)
|
||||
{
|
||||
gtk->gdk_threads_enter();
|
||||
gtk->paint_box_gap(widget_type, state, shadow_type, getStrFor(env, detail),
|
||||
x, y, w, h, gap_side, gap_x, gap_w);
|
||||
gtk->gdk_threads_leave();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1check(
|
||||
JNIEnv *env, jobject this,
|
||||
jint widget_type, jint synth_state, jstring detail,
|
||||
jint x, jint y, jint w, jint h)
|
||||
{
|
||||
gtk->gdk_threads_enter();
|
||||
gtk->paint_check(widget_type, synth_state, getStrFor(env, detail),
|
||||
x, y, w, h);
|
||||
gtk->gdk_threads_leave();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1expander(
|
||||
JNIEnv *env, jobject this,
|
||||
jint widget_type, jint state, jstring detail,
|
||||
jint x, jint y, jint w, jint h, jint expander_style)
|
||||
{
|
||||
gtk->gdk_threads_enter();
|
||||
gtk->paint_expander(widget_type, state, getStrFor(env, detail),
|
||||
x, y, w, h, expander_style);
|
||||
gtk->gdk_threads_leave();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1extension(
|
||||
JNIEnv *env, jobject this,
|
||||
jint widget_type, jint state, jint shadow_type, jstring detail,
|
||||
jint x, jint y, jint w, jint h, jint placement)
|
||||
{
|
||||
gtk->gdk_threads_enter();
|
||||
gtk->paint_extension(widget_type, state, shadow_type,
|
||||
getStrFor(env, detail), x, y, w, h, placement);
|
||||
gtk->gdk_threads_leave();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1flat_1box(
|
||||
JNIEnv *env, jobject this,
|
||||
jint widget_type, jint state, jint shadow_type, jstring detail,
|
||||
jint x, jint y, jint w, jint h, jboolean has_focus)
|
||||
{
|
||||
gtk->gdk_threads_enter();
|
||||
gtk->paint_flat_box(widget_type, state, shadow_type,
|
||||
getStrFor(env, detail), x, y, w, h, has_focus);
|
||||
gtk->gdk_threads_leave();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1focus(
|
||||
JNIEnv *env, jobject this,
|
||||
jint widget_type, jint state, jstring detail,
|
||||
jint x, jint y, jint w, jint h)
|
||||
{
|
||||
gtk->gdk_threads_enter();
|
||||
gtk->paint_focus(widget_type, state, getStrFor(env, detail),
|
||||
x, y, w, h);
|
||||
gtk->gdk_threads_leave();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1handle(
|
||||
JNIEnv *env, jobject this,
|
||||
jint widget_type, jint state, jint shadow_type, jstring detail,
|
||||
jint x, jint y, jint w, jint h, jint orientation)
|
||||
{
|
||||
gtk->gdk_threads_enter();
|
||||
gtk->paint_handle(widget_type, state, shadow_type, getStrFor(env, detail),
|
||||
x, y, w, h, orientation);
|
||||
gtk->gdk_threads_leave();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1hline(
|
||||
JNIEnv *env, jobject this,
|
||||
jint widget_type, jint state, jstring detail,
|
||||
jint x, jint y, jint w, jint h)
|
||||
{
|
||||
gtk->gdk_threads_enter();
|
||||
gtk->paint_hline(widget_type, state, getStrFor(env, detail),
|
||||
x, y, w, h);
|
||||
gtk->gdk_threads_leave();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1option(
|
||||
JNIEnv *env, jobject this,
|
||||
jint widget_type, jint synth_state, jstring detail,
|
||||
jint x, jint y, jint w, jint h)
|
||||
{
|
||||
gtk->gdk_threads_enter();
|
||||
gtk->paint_option(widget_type, synth_state, getStrFor(env, detail),
|
||||
x, y, w, h);
|
||||
gtk->gdk_threads_leave();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1shadow(
|
||||
JNIEnv *env, jobject this,
|
||||
jint widget_type, jint state, jint shadow_type, jstring detail,
|
||||
jint x, jint y, jint w, jint h,
|
||||
jint synth_state, jint dir)
|
||||
{
|
||||
gtk->gdk_threads_enter();
|
||||
gtk->paint_shadow(widget_type, state, shadow_type, getStrFor(env, detail),
|
||||
x, y, w, h, synth_state, dir);
|
||||
gtk->gdk_threads_leave();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1slider(
|
||||
JNIEnv *env, jobject this,
|
||||
jint widget_type, jint state, jint shadow_type, jstring detail,
|
||||
jint x, jint y, jint w, jint h, jint orientation, jboolean has_focus)
|
||||
{
|
||||
gtk->gdk_threads_enter();
|
||||
gtk->paint_slider(widget_type, state, shadow_type, getStrFor(env, detail),
|
||||
x, y, w, h, orientation, has_focus);
|
||||
gtk->gdk_threads_leave();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1vline(
|
||||
JNIEnv *env, jobject this,
|
||||
jint widget_type, jint state, jstring detail,
|
||||
jint x, jint y, jint w, jint h)
|
||||
{
|
||||
gtk->gdk_threads_enter();
|
||||
gtk->paint_vline(widget_type, state, getStrFor(env, detail),
|
||||
x, y, w, h);
|
||||
gtk->gdk_threads_leave();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1background(
|
||||
JNIEnv *env, jobject this, jint widget_type, jint state,
|
||||
jint x, jint y, jint w, jint h)
|
||||
{
|
||||
gtk->gdk_threads_enter();
|
||||
gtk->paint_background(widget_type, state, x, y, w, h);
|
||||
gtk->gdk_threads_leave();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKEngine_nativeStartPainting(
|
||||
JNIEnv *env, jobject this, jint w, jint h, jint scale)
|
||||
{
|
||||
if (w > 0x7FFF || h > 0x7FFF || (uintptr_t)4 * w * h > 0x7FFFFFFFL) {
|
||||
// Same limitation as in X11SurfaceData.c
|
||||
JNU_ThrowOutOfMemoryError(env, "Can't create offscreen surface");
|
||||
return;
|
||||
}
|
||||
gtk->gdk_threads_enter();
|
||||
gtk->init_painting(env, w, h, scale);
|
||||
gtk->gdk_threads_leave();
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKEngine_nativeFinishPainting(
|
||||
JNIEnv *env, jobject this, jintArray dest, jint width, jint height)
|
||||
{
|
||||
jint transparency;
|
||||
gint *buffer = (gint*) (*env)->GetPrimitiveArrayCritical(env, dest, 0);
|
||||
if (buffer == 0) {
|
||||
(*env)->ExceptionClear(env);
|
||||
JNU_ThrowOutOfMemoryError(env, "Could not get image buffer");
|
||||
return -1;
|
||||
}
|
||||
gtk->gdk_threads_enter();
|
||||
transparency = gtk->copy_image(buffer, width, height);
|
||||
gtk->gdk_threads_leave();
|
||||
(*env)->ReleasePrimitiveArrayCritical(env, dest, buffer, 0);
|
||||
return transparency;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1switch_1theme(
|
||||
JNIEnv *env, jobject this)
|
||||
{
|
||||
// Note that gtk->flush_event_loop takes care of locks (7053002), gdk_threads_enter/gdk_threads_leave should not be used.
|
||||
gtk->flush_event_loop();
|
||||
}
|
||||
|
||||
JNIEXPORT jobject JNICALL Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1get_1gtk_1setting(
|
||||
JNIEnv *env, jobject this, jint property)
|
||||
{
|
||||
jobject obj;
|
||||
gtk->gdk_threads_enter();
|
||||
obj = gtk->get_setting(env, property);
|
||||
gtk->gdk_threads_leave();
|
||||
return obj;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKEngine_nativeSetRangeValue(
|
||||
JNIEnv *env, jobject this, jint widget_type,
|
||||
jdouble value, jdouble min, jdouble max, jdouble visible)
|
||||
{
|
||||
gtk->gdk_threads_enter();
|
||||
gtk->set_range_value(widget_type, value, min, max, visible);
|
||||
gtk->gdk_threads_leave();
|
||||
}
|
||||
|
||||
93
src/java.desktop/unix/native/libawt_wlawt/swing_GTKStyle.c
Normal file
93
src/java.desktop/unix/native/libawt_wlawt/swing_GTKStyle.c
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#ifdef HEADLESS
|
||||
#error This file should not be included in headless library
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "gtk_interface.h"
|
||||
#include "com_sun_java_swing_plaf_gtk_GTKStyle.h"
|
||||
|
||||
const char *getStrFor(JNIEnv *env, jstring val);
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKStyle_nativeGetXThickness(
|
||||
JNIEnv *env, jclass klass, jint widget_type)
|
||||
{
|
||||
jint ret;
|
||||
gtk->gdk_threads_enter();
|
||||
ret = gtk->get_xthickness(env, widget_type);
|
||||
gtk->gdk_threads_leave();
|
||||
return ret;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKStyle_nativeGetYThickness(
|
||||
JNIEnv *env, jclass klass, jint widget_type)
|
||||
{
|
||||
jint ret;
|
||||
gtk->gdk_threads_enter();
|
||||
ret = gtk->get_ythickness(env, widget_type);
|
||||
gtk->gdk_threads_leave();
|
||||
return ret;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKStyle_nativeGetColorForState(
|
||||
JNIEnv *env, jclass klass, jint widget_type,
|
||||
jint state_type, jint type_id)
|
||||
{
|
||||
jint ret;
|
||||
gtk->gdk_threads_enter();
|
||||
ret = gtk->get_color_for_state(env, widget_type, state_type, type_id);
|
||||
gtk->gdk_threads_leave();
|
||||
return ret;
|
||||
}
|
||||
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKStyle_nativeGetClassValue(
|
||||
JNIEnv *env, jclass klass, jint widget_type, jstring key)
|
||||
{
|
||||
jobject ret;
|
||||
gtk->gdk_threads_enter();
|
||||
ret = gtk->get_class_value(env, widget_type, getStrFor(env, key));
|
||||
gtk->gdk_threads_leave();
|
||||
return ret;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKStyle_nativeGetPangoFontName(
|
||||
JNIEnv *env, jclass klass, jint widget_type)
|
||||
{
|
||||
jstring ret;
|
||||
gtk->gdk_threads_enter();
|
||||
ret = gtk->get_pango_font_name(env, widget_type);
|
||||
gtk->gdk_threads_leave();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -313,6 +313,7 @@ static GdkPixbuf *(*fp_gdk_pixbuf_scale_simple)(GdkPixbuf *src,
|
||||
|
||||
static void (*fp_gtk_widget_destroy)(void *widget);
|
||||
static void (*fp_gtk_window_present)(GtkWindow *window);
|
||||
static void (*fp_gtk_window_present_with_time)(GtkWindow *window, guint32 timestamp);
|
||||
static void (*fp_gtk_window_move)(GtkWindow *window, gint x, gint y);
|
||||
static void (*fp_gtk_window_resize)(GtkWindow *window, gint width, gint height);
|
||||
|
||||
|
||||
@@ -312,7 +312,7 @@ Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1background(
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_sun_java_swing_plaf_gtk_GTKEngine_nativeStartPainting(
|
||||
JNIEnv *env, jobject this, jint w, jint h)
|
||||
JNIEnv *env, jobject this, jint w, jint h, jint scale)
|
||||
{
|
||||
if (w > 0x7FFF || h > 0x7FFF || (uintptr_t)4 * w * h > 0x7FFFFFFFL) {
|
||||
// Same limitation as in X11SurfaceData.c
|
||||
|
||||
@@ -30,7 +30,6 @@ import java.awt.BufferCapabilities;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.FontMetrics;
|
||||
@@ -74,8 +73,6 @@ import sun.java2d.opengl.OGLSurfaceData;
|
||||
import sun.java2d.pipe.Region;
|
||||
import sun.util.logging.PlatformLogger;
|
||||
|
||||
import static sun.awt.windows.WToolkit.getWToolkit;
|
||||
|
||||
public abstract class WComponentPeer extends WObjectPeer
|
||||
implements ComponentPeer, DropTargetPeer
|
||||
{
|
||||
@@ -132,10 +129,6 @@ public abstract class WComponentPeer extends WObjectPeer
|
||||
@Override
|
||||
public native Point getLocationOnScreen();
|
||||
|
||||
Cursor getCursor(final Point p) {
|
||||
return getTarget() instanceof Component ? ((Component)getTarget()).getCursor() : null;
|
||||
}
|
||||
|
||||
/* New 1.1 API */
|
||||
@Override
|
||||
public void setVisible(boolean b) {
|
||||
@@ -715,10 +708,9 @@ public abstract class WComponentPeer extends WObjectPeer
|
||||
_setFont(f);
|
||||
}
|
||||
synchronized native void _setFont(Font f);
|
||||
|
||||
@Override
|
||||
public void updateCursorImmediately() {
|
||||
WGlobalCursorManager.getInstance().updateCursor();
|
||||
WGlobalCursorManager.getCursorManager().updateCursorImmediately();
|
||||
}
|
||||
|
||||
// TODO: consider moving it to KeyboardFocusManagerPeerImpl
|
||||
|
||||
@@ -25,68 +25,37 @@
|
||||
|
||||
package sun.awt.windows;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Point;
|
||||
import java.awt.*;
|
||||
import sun.awt.GlobalCursorManager;
|
||||
|
||||
import sun.awt.CachedCursorManager;
|
||||
final class WGlobalCursorManager extends GlobalCursorManager {
|
||||
private static WGlobalCursorManager manager;
|
||||
|
||||
import static sun.awt.windows.WToolkit.getWToolkit;
|
||||
|
||||
final class WGlobalCursorManager extends CachedCursorManager {
|
||||
private Component lastMouseEventComponent;
|
||||
|
||||
private WGlobalCursorManager() {
|
||||
}
|
||||
|
||||
public void setMouseEventComponent(Component component) {
|
||||
lastMouseEventComponent = component;
|
||||
}
|
||||
|
||||
public Component getMouseEventComponent() {
|
||||
return lastMouseEventComponent;
|
||||
}
|
||||
|
||||
private static final WGlobalCursorManager theInstance = new WGlobalCursorManager();
|
||||
public static WGlobalCursorManager getInstance() {
|
||||
return theInstance;
|
||||
}
|
||||
|
||||
private native void getCursorPos(Point p);
|
||||
private native void setCursor(Cursor cursor, boolean u);
|
||||
@Override
|
||||
protected native Point getLocationOnScreen(Component component);
|
||||
|
||||
@Override
|
||||
protected Cursor getCursorByPosition(Point cursorPos, Component c) {
|
||||
final Object peer = WToolkit.targetToPeer(c);
|
||||
if (peer instanceof WComponentPeer && c.isShowing()) {
|
||||
final WComponentPeer wpeer = (WComponentPeer) peer;
|
||||
final Point p = getLocationOnScreen((Component) wpeer.getTarget());
|
||||
return wpeer.getCursor(new Point(cursorPos.x - p.x,
|
||||
cursorPos.y - p.y));
|
||||
public static GlobalCursorManager getCursorManager() {
|
||||
if (manager == null) {
|
||||
manager = new WGlobalCursorManager();
|
||||
}
|
||||
return null;
|
||||
return manager;
|
||||
}
|
||||
|
||||
public static void nativeUpdateCursor(Component component) {
|
||||
getInstance().updateCursorLater(component);
|
||||
/**
|
||||
* Should be called in response to a native mouse enter or native mouse
|
||||
* button released message. Should not be called during a mouse drag.
|
||||
*/
|
||||
public static void nativeUpdateCursor(Component heavy) {
|
||||
WGlobalCursorManager.getCursorManager().updateCursorLater(heavy);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Component getComponentUnderCursor() {
|
||||
return lastMouseEventComponent;
|
||||
}
|
||||
|
||||
protected native void setCursor(Component comp, Cursor cursor, boolean u);
|
||||
@Override
|
||||
public Point getCursorPosition() {
|
||||
Point cursorPos = new Point();
|
||||
getCursorPos(cursorPos);
|
||||
return cursorPos;
|
||||
}
|
||||
|
||||
protected native void getCursorPos(Point p);
|
||||
/*
|
||||
* two native methods to call corresponding methods in Container and
|
||||
* Component
|
||||
*/
|
||||
@Override
|
||||
protected void setCursor(Cursor cursor) {
|
||||
setCursor(cursor, true);
|
||||
}
|
||||
protected native Component findHeavyweightUnderCursor(boolean useCache);
|
||||
@Override
|
||||
protected native Point getLocationOnScreen(Component com);
|
||||
}
|
||||
|
||||
@@ -1300,14 +1300,4 @@ public final class WToolkit extends SunToolkit implements Runnable {
|
||||
public boolean needUpdateWindow() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLastMouseEventComponent(Component component) {
|
||||
WGlobalCursorManager.getInstance().setMouseEventComponent(component);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getLastMouseEventComponent() {
|
||||
return WGlobalCursorManager.getInstance().getMouseEventComponent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,11 +528,11 @@ static void GlobalSetCursor(void* pStruct) {
|
||||
/*
|
||||
* Class: sun_awt_windows_WGlobalCursorManager
|
||||
* Method: setCursor
|
||||
* Signature: (Ljava/awt/Cursor;Z)V
|
||||
* Signature: (Ljava/awt/Component;Ljava/awt/Cursor;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_windows_WGlobalCursorManager_setCursor(JNIEnv *env, jobject,
|
||||
jobject cursor, jboolean u)
|
||||
jobject, jobject cursor, jboolean u)
|
||||
{
|
||||
TRY;
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ public enum GCCause {
|
||||
|
||||
_dcmd_gc_run ("Diagnostic Command"),
|
||||
_jbr_gc_run("JBR full GC"),
|
||||
_jbr_shrinking_gc_run("JBR shrinking GC"),
|
||||
|
||||
_z_timer ("Timer"),
|
||||
_z_warmup ("Warmup"),
|
||||
|
||||
@@ -203,6 +203,7 @@ serviceability/sa/ClhsdbFindPC.java#xcomp-core 8294316,8267433,JBR-6259 macosx-x
|
||||
serviceability/sa/ClhsdbFindPC.java#no-xcomp-core 8294316,8267433,JBR-6259 macosx-x64,linux_aarch64,windows-aarch64
|
||||
|
||||
serviceability/sa/ClhsdbJdis.java initial_run windows-aarch64
|
||||
serviceability/sa/ClhsdbJstack.java#id0 JBR-8573 windows-aarch64
|
||||
serviceability/sa/ClhsdbJstack.java#id1 initial_run windows-aarch64
|
||||
serviceability/sa/ClhsdbPmap.java#core 8294316,8267433,JBR-6259 macosx-x64,windows-aarch64
|
||||
serviceability/sa/ClhsdbPmap.java#id1 initial_run generic-all
|
||||
|
||||
@@ -846,7 +846,6 @@ jdk_awt_wayland = \
|
||||
-java/awt/datatransfer/SystemSelection/SystemSelectionSwingTest.java \
|
||||
-java/awt/datatransfer/UnicodeTransferTest/UnicodeTransferTest.java \
|
||||
-java/awt/Debug \
|
||||
-java/awt/Desktop \
|
||||
-java/awt/Dialog \
|
||||
-java/awt/dnd \
|
||||
-java/awt/event/ComponentEvent/ComponentItemEventTest.java \
|
||||
@@ -904,7 +903,7 @@ jdk_awt_wayland = \
|
||||
-java/awt/EventDispatchThread/LoopRobustness/LoopRobustness.java \
|
||||
-java/awt/EventDispatchThread/PropertyPermissionOnEDT/PropertyPermissionOnEDT.java \
|
||||
-java/awt/EventQueue/6980209/bug6980209.java \
|
||||
-java/awt/FileDialog \
|
||||
-java/awt/FileDialog/ModalFocus/FileDialogModalFocusTest.java \
|
||||
-java/awt/FlowLayout \
|
||||
-java/awt/Focus/6378278 \
|
||||
-java/awt/Focus/6382144 \
|
||||
@@ -1244,8 +1243,10 @@ jdk_swing_wayland= \
|
||||
-javax/swing/JSplitPane/4820080/JSplitPaneDragColorTest.java \
|
||||
-javax/swing/JSplitPane/4885629/bug4885629.java \
|
||||
-javax/swing/JSplitPane/bug4870674.java \
|
||||
-javax/swing/JTabbedPane/6495408/bug6495408.java \
|
||||
-javax/swing/JTabbedPane/7024235/Test7024235.java \
|
||||
-javax/swing/JTabbedPane/7161568/bug7161568.java \
|
||||
-javax/swing/JTabbedPane/bug4703690.java \
|
||||
-javax/swing/JTabbedPane/TabbedPaneBug.java \
|
||||
-javax/swing/JTabbedPane/TabProb.java \
|
||||
-javax/swing/JTable/4220171/bug4220171.java \
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
;
|
||||
grant {
|
||||
permission java.lang.RuntimePermission "loadLibrary.awt";
|
||||
};
|
||||
|
||||
@@ -28,9 +28,12 @@
|
||||
* @summary AWT_Desktop/Automated/Exceptions/BasicTest loads incorrect GTK
|
||||
* version when jdk.gtk.version=3
|
||||
* @requires (os.family == "linux")
|
||||
* @library /test/lib
|
||||
* @run main DesktopGtkLoadTest
|
||||
*/
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.*;
|
||||
@@ -43,26 +46,14 @@ public class DesktopGtkLoadTest {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Process p = Runtime.getRuntime().exec(System.getProperty("java.home") +
|
||||
"/bin/java -Djdk.gtk.version=3 -Djdk.gtk.verbose=true " +
|
||||
"-cp " + System.getProperty("java.class.path", ".") +
|
||||
" DesktopGtkLoadTest$RunDesktop");
|
||||
p.waitFor();
|
||||
try (BufferedReader br = new BufferedReader(
|
||||
new InputStreamReader(p.getErrorStream()))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
System.out.println(line);
|
||||
if (line.contains("Looking for GTK2 library")) {
|
||||
break;
|
||||
}
|
||||
if (line.contains("Looking for GTK3 library")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Wrong GTK library version: \n" + line);
|
||||
}
|
||||
|
||||
final ProcessBuilder pbJava = ProcessTools.createTestJavaProcessBuilder(
|
||||
"-Djdk.gtk.version=3",
|
||||
"-Djdk.gtk.verbose=true",
|
||||
RunDesktop.class.getName()
|
||||
);
|
||||
final OutputAnalyzer output = ProcessTools.executeProcess(pbJava);
|
||||
output.shouldNotContain("Looking for GTK2 library");
|
||||
output.shouldContain("Looking for GTK3 library");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user