Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/intern
diff options
context:
space:
mode:
Diffstat (limited to 'intern')
-rw-r--r--intern/cycles/CMakeLists.txt6
-rw-r--r--intern/cycles/app/CMakeLists.txt5
-rw-r--r--intern/cycles/blender/addon/engine.py15
-rw-r--r--intern/cycles/blender/addon/properties.py18
-rw-r--r--intern/cycles/blender/addon/ui.py14
-rw-r--r--intern/cycles/blender/session.cpp6
-rw-r--r--intern/cycles/blender/sync.cpp206
-rw-r--r--intern/cycles/cmake/external_libs.cmake56
-rw-r--r--intern/cycles/device/CMakeLists.txt22
-rw-r--r--intern/cycles/device/cuda/queue.cpp2
-rw-r--r--intern/cycles/device/cuda/queue.h2
-rw-r--r--intern/cycles/device/hip/queue.cpp2
-rw-r--r--intern/cycles/device/hip/queue.h2
-rw-r--r--intern/cycles/device/metal/device_impl.mm10
-rw-r--r--intern/cycles/device/metal/kernel.mm13
-rw-r--r--intern/cycles/device/metal/queue.h2
-rw-r--r--intern/cycles/device/metal/queue.mm51
-rw-r--r--intern/cycles/device/metal/util.mm6
-rw-r--r--intern/cycles/device/oneapi/device.cpp4
-rw-r--r--intern/cycles/device/oneapi/device_impl.cpp69
-rw-r--r--intern/cycles/device/oneapi/device_impl.h2
-rw-r--r--intern/cycles/device/oneapi/queue.cpp2
-rw-r--r--intern/cycles/device/oneapi/queue.h2
-rw-r--r--intern/cycles/device/queue.h2
-rw-r--r--intern/cycles/integrator/path_trace_work_gpu.cpp31
-rw-r--r--intern/cycles/kernel/CMakeLists.txt34
-rw-r--r--intern/cycles/kernel/bvh/shadow_all.h2
-rw-r--r--intern/cycles/kernel/bvh/util.h8
-rw-r--r--intern/cycles/kernel/camera/projection.h26
-rw-r--r--intern/cycles/kernel/device/cpu/bvh.h2
-rw-r--r--intern/cycles/kernel/device/gpu/parallel_active_index.h33
-rw-r--r--intern/cycles/kernel/device/metal/context_begin.h37
-rw-r--r--intern/cycles/kernel/device/metal/kernel.metal2
-rw-r--r--intern/cycles/kernel/device/oneapi/compat.h45
-rw-r--r--intern/cycles/kernel/device/oneapi/globals.h9
-rw-r--r--intern/cycles/kernel/device/oneapi/kernel.cpp17
-rw-r--r--intern/cycles/kernel/device/optix/bvh.h2
-rw-r--r--intern/cycles/kernel/types.h1
-rw-r--r--intern/cycles/scene/camera.cpp1
-rw-r--r--intern/cycles/util/transform_inverse.h2
-rw-r--r--intern/cycles/util/types_float3.h5
-rw-r--r--intern/ffmpeg/tests/ffmpeg_codecs.cc7
-rw-r--r--intern/ghost/CMakeLists.txt14
-rw-r--r--intern/ghost/GHOST_C-api.h2
-rw-r--r--intern/ghost/GHOST_ISystem.h6
-rw-r--r--intern/ghost/GHOST_Types.h12
-rw-r--r--intern/ghost/intern/GHOST_C-api.cpp4
-rw-r--r--intern/ghost/intern/GHOST_ContextCGL.h3
-rw-r--r--intern/ghost/intern/GHOST_ContextCGL.mm4
-rw-r--r--intern/ghost/intern/GHOST_ContextGLX.cpp2
-rw-r--r--intern/ghost/intern/GHOST_Debug.h34
-rw-r--r--intern/ghost/intern/GHOST_ISystem.cpp8
-rw-r--r--intern/ghost/intern/GHOST_ImeWin32.h2
-rw-r--r--intern/ghost/intern/GHOST_NDOFManager.cpp385
-rw-r--r--intern/ghost/intern/GHOST_NDOFManager.h53
-rw-r--r--intern/ghost/intern/GHOST_NDOFManagerUnix.cpp16
-rw-r--r--intern/ghost/intern/GHOST_NDOFManagerUnix.h2
-rw-r--r--intern/ghost/intern/GHOST_System.cpp2
-rw-r--r--intern/ghost/intern/GHOST_System.h2
-rw-r--r--intern/ghost/intern/GHOST_SystemCocoa.h2
-rw-r--r--intern/ghost/intern/GHOST_SystemCocoa.mm5
-rw-r--r--intern/ghost/intern/GHOST_SystemHeadless.h3
-rw-r--r--intern/ghost/intern/GHOST_SystemSDL.cpp3
-rw-r--r--intern/ghost/intern/GHOST_SystemSDL.h1
-rw-r--r--intern/ghost/intern/GHOST_SystemWayland.cpp3212
-rw-r--r--intern/ghost/intern/GHOST_SystemWayland.h19
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.cpp3
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.h2
-rw-r--r--intern/ghost/intern/GHOST_SystemX11.cpp4
-rw-r--r--intern/ghost/intern/GHOST_SystemX11.h2
-rw-r--r--intern/ghost/intern/GHOST_Window.h8
-rw-r--r--intern/ghost/intern/GHOST_WindowCocoa.mm4
-rw-r--r--intern/ghost/intern/GHOST_WindowWayland.cpp84
-rw-r--r--intern/ghost/intern/GHOST_WindowWayland.h4
-rw-r--r--intern/ghost/test/gears/GHOST_Test.cpp21
-rw-r--r--intern/guardedalloc/intern/leak_detector.cc2
-rw-r--r--intern/opensubdiv/CMakeLists.txt6
-rw-r--r--intern/wayland_dynload/extern/wayland_dynload_client.h1
78 files changed, 3428 insertions, 1292 deletions
diff --git a/intern/cycles/CMakeLists.txt b/intern/cycles/CMakeLists.txt
index f619e6b104e..329aa3990f6 100644
--- a/intern/cycles/CMakeLists.txt
+++ b/intern/cycles/CMakeLists.txt
@@ -263,8 +263,7 @@ if(WITH_CYCLES_DEVICE_OPTIX)
${OPTIX_INCLUDE_DIR}
)
else()
- message(STATUS "OptiX not found, disabling it from Cycles")
- set(WITH_CYCLES_DEVICE_OPTIX OFF)
+ set_and_warn_library_found("OptiX" OPTIX_FOUND WITH_CYCLES_DEVICE_OPTIX)
endif()
endif()
@@ -387,8 +386,7 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_C_COMPILER_ID MATCHES "Clang")
endif()
if(WITH_CYCLES_HYDRA_RENDER_DELEGATE AND (NOT WITH_USD))
- message(STATUS "USD not found, disabling WITH_CYCLES_HYDRA_RENDER_DELEGATE")
- set(WITH_CYCLES_HYDRA_RENDER_DELEGATE OFF)
+ set_and_warn_library_found("USD" WITH_USD WITH_CYCLES_HYDRA_RENDER_DELEGATE)
endif()
if(WITH_CYCLES_HYDRA_RENDER_DELEGATE AND (NOT WITH_BLENDER) AND (NOT WITH_CYCLES_STANDALONE))
set(CYCLES_INSTALL_PATH ${CYCLES_INSTALL_PATH}/hdCycles/resources)
diff --git a/intern/cycles/app/CMakeLists.txt b/intern/cycles/app/CMakeLists.txt
index 0988b1c0ac4..1c7a861ea93 100644
--- a/intern/cycles/app/CMakeLists.txt
+++ b/intern/cycles/app/CMakeLists.txt
@@ -43,7 +43,10 @@ else()
endif()
if(WITH_CYCLES_STANDALONE AND WITH_CYCLES_STANDALONE_GUI)
- list(APPEND INC_SYS ${Epoxy_INCLUDE_DIRS} ${SDL2_INCLUDE_DIRS})
+ list(APPEND INC_SYS
+ ${Epoxy_INCLUDE_DIRS}
+ ${SDL2_INCLUDE_DIRS}
+ )
list(APPEND LIB ${Epoxy_LIBRARIES} ${SDL2_LIBRARIES})
endif()
diff --git a/intern/cycles/blender/addon/engine.py b/intern/cycles/blender/addon/engine.py
index 794338fe78e..e33891fa7a2 100644
--- a/intern/cycles/blender/addon/engine.py
+++ b/intern/cycles/blender/addon/engine.py
@@ -209,22 +209,25 @@ def list_render_passes(scene, srl):
yield ("Debug Sample Count", "X", 'VALUE')
# Cryptomatte passes.
- crypto_depth = (srl.pass_cryptomatte_depth + 1) // 2
+ # NOTE: Name channels are lowercase RGBA so that compression rules check in OpenEXR DWA code
+ # uses lossless compression. Reportedly this naming is the only one which works good from the
+ # interoperability point of view. Using XYZW naming is not portable.
+ crypto_depth = (min(16, srl.pass_cryptomatte_depth) + 1) // 2
if srl.use_pass_cryptomatte_object:
for i in range(0, crypto_depth):
- yield ("CryptoObject" + '{:02d}'.format(i), "RGBA", 'COLOR')
+ yield ("CryptoObject" + '{:02d}'.format(i), "rgba", 'COLOR')
if srl.use_pass_cryptomatte_material:
for i in range(0, crypto_depth):
- yield ("CryptoMaterial" + '{:02d}'.format(i), "RGBA", 'COLOR')
+ yield ("CryptoMaterial" + '{:02d}'.format(i), "rgba", 'COLOR')
if srl.use_pass_cryptomatte_asset:
for i in range(0, crypto_depth):
- yield ("CryptoAsset" + '{:02d}'.format(i), "RGBA", 'COLOR')
+ yield ("CryptoAsset" + '{:02d}'.format(i), "rgba", 'COLOR')
# Denoising passes.
if scene.cycles.use_denoising and crl.use_denoising:
yield ("Noisy Image", "RGBA", 'COLOR')
if crl.use_pass_shadow_catcher:
- yield ("Noisy Shadow Catcher", "RGBA", 'COLOR')
+ yield ("Noisy Shadow Catcher", "RGB", 'COLOR')
if crl.denoising_store_passes:
yield ("Denoising Normal", "XYZ", 'VECTOR')
yield ("Denoising Albedo", "RGB", 'COLOR')
@@ -232,6 +235,8 @@ def list_render_passes(scene, srl):
# Custom AOV passes.
for aov in srl.aovs:
+ if not aov.is_valid:
+ continue
if aov.type == 'VALUE':
yield (aov.name, "X", 'VALUE')
else:
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index b7ce76d8f44..f5cd88f6b6a 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -60,13 +60,14 @@ enum_filter_types = (
)
enum_panorama_types = (
- ('EQUIRECTANGULAR', "Equirectangular", "Render the scene with a spherical camera, also known as Lat Long panorama"),
- ('FISHEYE_EQUIDISTANT', "Fisheye Equidistant", "Ideal for fulldomes, ignore the sensor dimensions"),
+ ('EQUIRECTANGULAR', "Equirectangular", "Spherical camera for environment maps, also known as Lat Long panorama", 0),
+ ('EQUIANGULAR_CUBEMAP_FACE', "Equiangular Cubemap Face", "Single face of an equiangular cubemap", 5),
+ ('MIRRORBALL', "Mirror Ball", "Mirror ball mapping for environment maps", 3),
+ ('FISHEYE_EQUIDISTANT', "Fisheye Equidistant", "Ideal for fulldomes, ignore the sensor dimensions", 1),
('FISHEYE_EQUISOLID', "Fisheye Equisolid",
- "Similar to most fisheye modern lens, takes sensor dimensions into consideration"),
- ('MIRRORBALL', "Mirror Ball", "Uses the mirror ball mapping"),
+ "Similar to most fisheye modern lens, takes sensor dimensions into consideration", 2),
('FISHEYE_LENS_POLYNOMIAL', "Fisheye Lens Polynomial",
- "Defines the lens projection as polynomial to allow real world camera lenses to be mimicked"),
+ "Defines the lens projection as polynomial to allow real world camera lenses to be mimicked", 4),
)
enum_curve_shape = (
@@ -1636,11 +1637,13 @@ class CyclesPreferences(bpy.types.AddonPreferences):
col.label(text="and AMD driver version 22.10 or newer", icon='BLANK1')
elif device_type == 'ONEAPI':
import sys
- col.label(text="Requires Intel GPU with Xe-HPG architecture", icon='BLANK1')
if sys.platform.startswith("win"):
+ col.label(text="Requires Intel GPU with Xe-HPG architecture", icon='BLANK1')
col.label(text="and Windows driver version 101.3430 or newer", icon='BLANK1')
elif sys.platform.startswith("linux"):
- col.label(text="and Linux driver version xx.xx.23904 or newer", icon='BLANK1')
+ col.label(text="Requires Intel GPU with Xe-HPG architecture and", icon='BLANK1')
+ col.label(text=" - Linux driver version xx.xx.23904 or newer", icon='BLANK1')
+ col.label(text=" - oneAPI Level-Zero Loader", icon='BLANK1')
elif device_type == 'METAL':
col.label(text="Requires Apple Silicon with macOS 12.2 or newer", icon='BLANK1')
col.label(text="or AMD with macOS 12.3 or newer", icon='BLANK1')
@@ -1651,6 +1654,7 @@ class CyclesPreferences(bpy.types.AddonPreferences):
box.prop(
device, "use", text=device.name
.replace('(TM)', unicodedata.lookup('TRADE MARK SIGN'))
+ .replace('(tm)', unicodedata.lookup('TRADE MARK SIGN'))
.replace('(R)', unicodedata.lookup('REGISTERED SIGN'))
.replace('(C)', unicodedata.lookup('COPYRIGHT SIGN'))
)
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index f763fe0eb0b..305accc8f1a 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -150,6 +150,16 @@ def get_effective_preview_denoiser(context):
return 'OIDN'
+def use_mnee(context):
+ # The MNEE kernel doesn't compile on macOS < 13.
+ if use_metal(context):
+ import platform
+ v, _, _ = platform.mac_ver()
+ if float(v) < 13.0:
+ return False
+ return True
+
+
class CYCLES_RENDER_PT_sampling(CyclesButtonsPanel, Panel):
bl_label = "Sampling"
@@ -1235,7 +1245,7 @@ class CYCLES_OBJECT_PT_shading_caustics(CyclesButtonsPanel, Panel):
@classmethod
def poll(cls, context):
- return CyclesButtonsPanel.poll(context) and not use_metal(context) and context.object.type != 'LIGHT'
+ return CyclesButtonsPanel.poll(context) and use_mnee(context) and context.object.type != 'LIGHT'
def draw(self, context):
layout = self.layout
@@ -1449,7 +1459,7 @@ class CYCLES_LIGHT_PT_light(CyclesButtonsPanel, Panel):
sub.active = not (light.type == 'AREA' and clamp.is_portal)
sub.prop(clamp, "cast_shadow")
sub.prop(clamp, "use_multiple_importance_sampling", text="Multiple Importance")
- if not use_metal(context):
+ if use_mnee(context):
sub.prop(clamp, "is_caustics_light", text="Shadow Caustics")
if light.type == 'AREA':
diff --git a/intern/cycles/blender/session.cpp b/intern/cycles/blender/session.cpp
index f9a83b2dc4b..6641e2b8ac5 100644
--- a/intern/cycles/blender/session.cpp
+++ b/intern/cycles/blender/session.cpp
@@ -497,9 +497,9 @@ void BlenderSession::render_frame_finish()
session->full_buffer_written_cb = function_null;
/* The display driver is the source of drawing context for both drawing and possible graphics
- * interop objects in the path trace. Once the frame is finished the OpenGL context might be
- * freed form Blender side. Need to ensure that all GPU resources are freed prior to that
- * point.
+ * interoperability objects in the path trace. Once the frame is finished the OpenGL context
+ * might be freed form Blender side. Need to ensure that all GPU resources are freed prior to
+ * that point.
* Ideally would only do this when OpenGL context is actually destroyed, but there is no way to
* know when this happens (at least in the code at the time when this comment was written).
* The penalty of re-creating resources on every frame is unlikely to be noticed. */
diff --git a/intern/cycles/blender/sync.cpp b/intern/cycles/blender/sync.cpp
index a69a94614d3..5251f0fee9c 100644
--- a/intern/cycles/blender/sync.cpp
+++ b/intern/cycles/blender/sync.cpp
@@ -575,68 +575,72 @@ void BlenderSync::sync_images()
/* Passes */
-static PassType get_blender_pass_type(BL::RenderPass &b_pass)
+static bool get_known_pass_type(BL::RenderPass &b_pass, PassType &type, PassMode &mode)
{
string name = b_pass.name();
-#define MAP_PASS(passname, passtype) \
+#define MAP_PASS(passname, passtype, noisy) \
if (name == passname) { \
- return passtype; \
+ type = passtype; \
+ mode = (noisy) ? PassMode::NOISY : PassMode::DENOISED; \
+ return true; \
} \
((void)0)
- /* NOTE: Keep in sync with defined names from DNA_scene_types.h */
+ /* NOTE: Keep in sync with defined names from engine.py */
- MAP_PASS("Combined", PASS_COMBINED);
- MAP_PASS("Noisy Image", PASS_COMBINED);
+ MAP_PASS("Combined", PASS_COMBINED, false);
+ MAP_PASS("Noisy Image", PASS_COMBINED, true);
- MAP_PASS("Depth", PASS_DEPTH);
- MAP_PASS("Mist", PASS_MIST);
- MAP_PASS("Position", PASS_POSITION);
- MAP_PASS("Normal", PASS_NORMAL);
- MAP_PASS("IndexOB", PASS_OBJECT_ID);
- MAP_PASS("UV", PASS_UV);
- MAP_PASS("Vector", PASS_MOTION);
- MAP_PASS("IndexMA", PASS_MATERIAL_ID);
+ MAP_PASS("Depth", PASS_DEPTH, false);
+ MAP_PASS("Mist", PASS_MIST, false);
+ MAP_PASS("Position", PASS_POSITION, false);
+ MAP_PASS("Normal", PASS_NORMAL, false);
+ MAP_PASS("IndexOB", PASS_OBJECT_ID, false);
+ MAP_PASS("UV", PASS_UV, false);
+ MAP_PASS("Vector", PASS_MOTION, false);
+ MAP_PASS("IndexMA", PASS_MATERIAL_ID, false);
- MAP_PASS("DiffDir", PASS_DIFFUSE_DIRECT);
- MAP_PASS("GlossDir", PASS_GLOSSY_DIRECT);
- MAP_PASS("TransDir", PASS_TRANSMISSION_DIRECT);
- MAP_PASS("VolumeDir", PASS_VOLUME_DIRECT);
+ MAP_PASS("DiffDir", PASS_DIFFUSE_DIRECT, false);
+ MAP_PASS("GlossDir", PASS_GLOSSY_DIRECT, false);
+ MAP_PASS("TransDir", PASS_TRANSMISSION_DIRECT, false);
+ MAP_PASS("VolumeDir", PASS_VOLUME_DIRECT, false);
- MAP_PASS("DiffInd", PASS_DIFFUSE_INDIRECT);
- MAP_PASS("GlossInd", PASS_GLOSSY_INDIRECT);
- MAP_PASS("TransInd", PASS_TRANSMISSION_INDIRECT);
- MAP_PASS("VolumeInd", PASS_VOLUME_INDIRECT);
+ MAP_PASS("DiffInd", PASS_DIFFUSE_INDIRECT, false);
+ MAP_PASS("GlossInd", PASS_GLOSSY_INDIRECT, false);
+ MAP_PASS("TransInd", PASS_TRANSMISSION_INDIRECT, false);
+ MAP_PASS("VolumeInd", PASS_VOLUME_INDIRECT, false);
- MAP_PASS("DiffCol", PASS_DIFFUSE_COLOR);
- MAP_PASS("GlossCol", PASS_GLOSSY_COLOR);
- MAP_PASS("TransCol", PASS_TRANSMISSION_COLOR);
+ MAP_PASS("DiffCol", PASS_DIFFUSE_COLOR, false);
+ MAP_PASS("GlossCol", PASS_GLOSSY_COLOR, false);
+ MAP_PASS("TransCol", PASS_TRANSMISSION_COLOR, false);
- MAP_PASS("Emit", PASS_EMISSION);
- MAP_PASS("Env", PASS_BACKGROUND);
- MAP_PASS("AO", PASS_AO);
- MAP_PASS("Shadow", PASS_SHADOW);
+ MAP_PASS("Emit", PASS_EMISSION, false);
+ MAP_PASS("Env", PASS_BACKGROUND, false);
+ MAP_PASS("AO", PASS_AO, false);
+ MAP_PASS("Shadow", PASS_SHADOW, false);
- MAP_PASS("BakePrimitive", PASS_BAKE_PRIMITIVE);
- MAP_PASS("BakeDifferential", PASS_BAKE_DIFFERENTIAL);
+ MAP_PASS("BakePrimitive", PASS_BAKE_PRIMITIVE, false);
+ MAP_PASS("BakeDifferential", PASS_BAKE_DIFFERENTIAL, false);
- MAP_PASS("Denoising Normal", PASS_DENOISING_NORMAL);
- MAP_PASS("Denoising Albedo", PASS_DENOISING_ALBEDO);
- MAP_PASS("Denoising Depth", PASS_DENOISING_DEPTH);
+ MAP_PASS("Denoising Normal", PASS_DENOISING_NORMAL, true);
+ MAP_PASS("Denoising Albedo", PASS_DENOISING_ALBEDO, true);
+ MAP_PASS("Denoising Depth", PASS_DENOISING_DEPTH, true);
- MAP_PASS("Shadow Catcher", PASS_SHADOW_CATCHER);
- MAP_PASS("Noisy Shadow Catcher", PASS_SHADOW_CATCHER);
+ MAP_PASS("Shadow Catcher", PASS_SHADOW_CATCHER, false);
+ MAP_PASS("Noisy Shadow Catcher", PASS_SHADOW_CATCHER, true);
- MAP_PASS("AdaptiveAuxBuffer", PASS_ADAPTIVE_AUX_BUFFER);
- MAP_PASS("Debug Sample Count", PASS_SAMPLE_COUNT);
+ MAP_PASS("AdaptiveAuxBuffer", PASS_ADAPTIVE_AUX_BUFFER, false);
+ MAP_PASS("Debug Sample Count", PASS_SAMPLE_COUNT, false);
if (string_startswith(name, cryptomatte_prefix)) {
- return PASS_CRYPTOMATTE;
+ type = PASS_CRYPTOMATTE;
+ mode = PassMode::DENOISED;
+ return true;
}
#undef MAP_PASS
- return PASS_NONE;
+ return false;
}
static Pass *pass_add(Scene *scene,
@@ -655,8 +659,6 @@ static Pass *pass_add(Scene *scene,
void BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLayer &b_view_layer)
{
- PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
-
/* Delete all existing passes. */
set<Pass *> clear_passes(scene->passes.begin(), scene->passes.end());
scene->delete_nodes(clear_passes);
@@ -664,103 +666,23 @@ void BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLayer &b_v
/* Always add combined pass. */
pass_add(scene, PASS_COMBINED, "Combined");
- /* Blender built-in data and light passes. */
- for (BL::RenderPass &b_pass : b_rlay.passes) {
- const PassType pass_type = get_blender_pass_type(b_pass);
-
- if (pass_type == PASS_NONE) {
- LOG(ERROR) << "Unknown pass " << b_pass.name();
- continue;
- }
-
- if (pass_type == PASS_MOTION &&
- (b_view_layer.use_motion_blur() && b_scene.render().use_motion_blur())) {
- continue;
- }
-
- pass_add(scene, pass_type, b_pass.name().c_str());
- }
-
- PointerRNA crl = RNA_pointer_get(&b_view_layer.ptr, "cycles");
-
- /* Debug passes. */
- if (get_boolean(crl, "pass_debug_sample_count")) {
- b_engine.add_pass("Debug Sample Count", 1, "X", b_view_layer.name().c_str());
- pass_add(scene, PASS_SAMPLE_COUNT, "Debug Sample Count");
- }
-
- /* Cycles specific passes. */
- if (get_boolean(crl, "use_pass_volume_direct")) {
- b_engine.add_pass("VolumeDir", 3, "RGB", b_view_layer.name().c_str());
- pass_add(scene, PASS_VOLUME_DIRECT, "VolumeDir");
- }
- if (get_boolean(crl, "use_pass_volume_indirect")) {
- b_engine.add_pass("VolumeInd", 3, "RGB", b_view_layer.name().c_str());
- pass_add(scene, PASS_VOLUME_INDIRECT, "VolumeInd");
- }
- if (get_boolean(crl, "use_pass_shadow_catcher")) {
- b_engine.add_pass("Shadow Catcher", 3, "RGB", b_view_layer.name().c_str());
- pass_add(scene, PASS_SHADOW_CATCHER, "Shadow Catcher");
- }
-
/* Cryptomatte stores two ID/weight pairs per RGBA layer.
- * User facing parameter is the number of pairs.
- *
- * NOTE: Name channels lowercase RGBA so that compression rules check in OpenEXR DWA code uses
- * lossless compression. Reportedly this naming is the only one which works good from the
- * interoperability point of view. Using XYZW naming is not portable. */
+ * User facing parameter is the number of pairs. */
int crypto_depth = divide_up(min(16, b_view_layer.pass_cryptomatte_depth()), 2);
scene->film->set_cryptomatte_depth(crypto_depth);
CryptomatteType cryptomatte_passes = CRYPT_NONE;
if (b_view_layer.use_pass_cryptomatte_object()) {
- for (int i = 0; i < crypto_depth; i++) {
- string passname = cryptomatte_prefix + string_printf("Object%02d", i);
- b_engine.add_pass(passname.c_str(), 4, "rgba", b_view_layer.name().c_str());
- pass_add(scene, PASS_CRYPTOMATTE, passname.c_str());
- }
cryptomatte_passes = (CryptomatteType)(cryptomatte_passes | CRYPT_OBJECT);
}
if (b_view_layer.use_pass_cryptomatte_material()) {
- for (int i = 0; i < crypto_depth; i++) {
- string passname = cryptomatte_prefix + string_printf("Material%02d", i);
- b_engine.add_pass(passname.c_str(), 4, "rgba", b_view_layer.name().c_str());
- pass_add(scene, PASS_CRYPTOMATTE, passname.c_str());
- }
cryptomatte_passes = (CryptomatteType)(cryptomatte_passes | CRYPT_MATERIAL);
}
if (b_view_layer.use_pass_cryptomatte_asset()) {
- for (int i = 0; i < crypto_depth; i++) {
- string passname = cryptomatte_prefix + string_printf("Asset%02d", i);
- b_engine.add_pass(passname.c_str(), 4, "rgba", b_view_layer.name().c_str());
- pass_add(scene, PASS_CRYPTOMATTE, passname.c_str());
- }
cryptomatte_passes = (CryptomatteType)(cryptomatte_passes | CRYPT_ASSET);
}
scene->film->set_cryptomatte_passes(cryptomatte_passes);
- /* Denoising passes. */
- const bool use_denoising = get_boolean(cscene, "use_denoising") &&
- get_boolean(crl, "use_denoising");
- const bool store_denoising_passes = get_boolean(crl, "denoising_store_passes");
- if (use_denoising) {
- b_engine.add_pass("Noisy Image", 4, "RGBA", b_view_layer.name().c_str());
- pass_add(scene, PASS_COMBINED, "Noisy Image", PassMode::NOISY);
- if (get_boolean(crl, "use_pass_shadow_catcher")) {
- b_engine.add_pass("Noisy Shadow Catcher", 3, "RGB", b_view_layer.name().c_str());
- pass_add(scene, PASS_SHADOW_CATCHER, "Noisy Shadow Catcher", PassMode::NOISY);
- }
- }
- if (store_denoising_passes) {
- b_engine.add_pass("Denoising Normal", 3, "XYZ", b_view_layer.name().c_str());
- pass_add(scene, PASS_DENOISING_NORMAL, "Denoising Normal", PassMode::NOISY);
-
- b_engine.add_pass("Denoising Albedo", 3, "RGB", b_view_layer.name().c_str());
- pass_add(scene, PASS_DENOISING_ALBEDO, "Denoising Albedo", PassMode::NOISY);
-
- b_engine.add_pass("Denoising Depth", 1, "Z", b_view_layer.name().c_str());
- pass_add(scene, PASS_DENOISING_DEPTH, "Denoising Depth", PassMode::NOISY);
- }
-
+ /* Path guiding debug passes. */
#ifdef WITH_CYCLES_DEBUG
b_engine.add_pass("Guiding Color", 3, "RGB", b_view_layer.name().c_str());
pass_add(scene, PASS_GUIDING_COLOR, "Guiding Color", PassMode::NOISY);
@@ -772,6 +694,8 @@ void BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLayer &b_v
pass_add(scene, PASS_GUIDING_AVG_ROUGHNESS, "Guiding Average Roughness", PassMode::NOISY);
#endif
+ unordered_set<string> expected_passes;
+
/* Custom AOV passes. */
BL::ViewLayer::aovs_iterator b_aov_iter;
for (b_view_layer.aovs.begin(b_aov_iter); b_aov_iter != b_view_layer.aovs.end(); ++b_aov_iter) {
@@ -781,16 +705,10 @@ void BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLayer &b_v
}
string name = b_aov.name();
- bool is_color = b_aov.type() == BL::AOV::type_COLOR;
+ PassType type = (b_aov.type() == BL::AOV::type_COLOR) ? PASS_AOV_COLOR : PASS_AOV_VALUE;
- if (is_color) {
- b_engine.add_pass(name.c_str(), 4, "RGBA", b_view_layer.name().c_str());
- pass_add(scene, PASS_AOV_COLOR, name.c_str());
- }
- else {
- b_engine.add_pass(name.c_str(), 1, "X", b_view_layer.name().c_str());
- pass_add(scene, PASS_AOV_VALUE, name.c_str());
- }
+ pass_add(scene, type, name.c_str());
+ expected_passes.insert(name);
}
/* Light Group passes. */
@@ -802,9 +720,29 @@ void BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLayer &b_v
string name = string_printf("Combined_%s", b_lightgroup.name().c_str());
- b_engine.add_pass(name.c_str(), 3, "RGB", b_view_layer.name().c_str());
Pass *pass = pass_add(scene, PASS_COMBINED, name.c_str(), PassMode::NOISY);
pass->set_lightgroup(ustring(b_lightgroup.name()));
+ expected_passes.insert(name);
+ }
+
+ /* Sync the passes that were defined in engine.py. */
+ for (BL::RenderPass &b_pass : b_rlay.passes) {
+ PassType pass_type = PASS_NONE;
+ PassMode pass_mode = PassMode::DENOISED;
+
+ if (!get_known_pass_type(b_pass, pass_type, pass_mode)) {
+ if (!expected_passes.count(b_pass.name())) {
+ LOG(ERROR) << "Unknown pass " << b_pass.name();
+ }
+ continue;
+ }
+
+ if (pass_type == PASS_MOTION &&
+ (b_view_layer.use_motion_blur() && b_scene.render().use_motion_blur())) {
+ continue;
+ }
+
+ pass_add(scene, pass_type, b_pass.name().c_str(), pass_mode);
}
scene->film->set_pass_alpha_threshold(b_view_layer.pass_alpha_threshold());
diff --git a/intern/cycles/cmake/external_libs.cmake b/intern/cycles/cmake/external_libs.cmake
index 9524cda54f5..44542a08156 100644
--- a/intern/cycles/cmake/external_libs.cmake
+++ b/intern/cycles/cmake/external_libs.cmake
@@ -289,8 +289,7 @@ if(CYCLES_STANDALONE_REPOSITORY AND WITH_CYCLES_PATH_GUIDING)
endif()
get_target_property(OPENPGL_INCLUDE_DIR openpgl::openpgl INTERFACE_INCLUDE_DIRECTORIES)
else()
- set(WITH_CYCLES_PATH_GUIDING OFF)
- message(STATUS "OpenPGL not found, disabling WITH_CYCLES_PATH_GUIDING")
+ set_and_warn_library_found("OpenPGL" openpgl_FOUND WITH_CYCLES_PATH_GUIDING)
endif()
endif()
@@ -588,16 +587,14 @@ if(WITH_CYCLES_STANDALONE AND WITH_CYCLES_STANDALONE_GUI)
# We can't use the version from the Blender precompiled libraries because
# it does not include the video subsystem.
find_package(SDL2 REQUIRED)
+ set_and_warn_library_found("SDL" SDL2_FOUND WITH_CYCLES_STANDALONE_GUI)
- if(NOT SDL2_FOUND)
- set(WITH_CYCLES_STANDALONE_GUI OFF)
- message(STATUS "SDL not found, disabling Cycles standalone GUI")
+ if(SDL2_FOUND)
+ include_directories(
+ SYSTEM
+ ${SDL2_INCLUDE_DIRS}
+ )
endif()
-
- include_directories(
- SYSTEM
- ${SDL2_INCLUDE_DIRS}
- )
endif()
###########################################################################
@@ -606,11 +603,11 @@ endif()
if(WITH_CYCLES_DEVICE_CUDA AND (WITH_CYCLES_CUDA_BINARIES OR NOT WITH_CUDA_DYNLOAD))
find_package(CUDA) # Try to auto locate CUDA toolkit
+ set_and_warn_library_found("CUDA compiler" CUDA_FOUND WITH_CYCLES_CUDA_BINARIES)
+
if(CUDA_FOUND)
message(STATUS "Found CUDA ${CUDA_NVCC_EXECUTABLE} (${CUDA_VERSION})")
else()
- message(STATUS "CUDA compiler not found, disabling WITH_CYCLES_CUDA_BINARIES")
- set(WITH_CYCLES_CUDA_BINARIES OFF)
if(NOT WITH_CUDA_DYNLOAD)
message(STATUS "Additionally falling back to dynamic CUDA load")
set(WITH_CUDA_DYNLOAD ON)
@@ -624,11 +621,10 @@ endif()
if(WITH_CYCLES_HIP_BINARIES AND WITH_CYCLES_DEVICE_HIP)
find_package(HIP)
+ set_and_warn_library_found("HIP compiler" HIP_FOUND WITH_CYCLES_HIP_BINARIES)
+
if(HIP_FOUND)
message(STATUS "Found HIP ${HIP_HIPCC_EXECUTABLE} (${HIP_VERSION})")
- else()
- message(STATUS "HIP compiler not found, disabling WITH_CYCLES_HIP_BINARIES")
- set(WITH_CYCLES_HIP_BINARIES OFF)
endif()
endif()
@@ -644,13 +640,17 @@ if(WITH_CYCLES_DEVICE_METAL)
find_library(METAL_LIBRARY Metal)
# This file was added in the 12.0 SDK, use it as a way to detect the version.
- if(METAL_LIBRARY AND NOT EXISTS "${METAL_LIBRARY}/Headers/MTLFunctionStitching.h")
- message(STATUS "Metal version too old, must be SDK 12.0 or newer, disabling WITH_CYCLES_DEVICE_METAL")
- set(WITH_CYCLES_DEVICE_METAL OFF)
- elseif(NOT METAL_LIBRARY)
- message(STATUS "Metal not found, disabling WITH_CYCLES_DEVICE_METAL")
- set(WITH_CYCLES_DEVICE_METAL OFF)
- else()
+ if(METAL_LIBRARY)
+ if(EXISTS "${METAL_LIBRARY}/Headers/MTLFunctionStitching.h")
+ set(METAL_FOUND ON)
+ else()
+ message(STATUS "Metal version too old, must be SDK 12.0 or newer")
+ set(METAL_FOUND OFF)
+ endif()
+ endif()
+
+ set_and_warn_library_found("Metal" METAL_FOUND WITH_CYCLES_DEVICE_METAL)
+ if(METAL_FOUND)
message(STATUS "Found Metal: ${METAL_LIBRARY}")
endif()
endif()
@@ -662,9 +662,10 @@ endif()
if(WITH_CYCLES_DEVICE_ONEAPI)
find_package(SYCL)
find_package(LevelZero)
+ set_and_warn_library_found("oneAPI" SYCL_FOUND WITH_CYCLES_DEVICE_ONEAPI)
+ set_and_warn_library_found("Level Zero" LEVEL_ZERO_FOUND WITH_CYCLES_DEVICE_ONEAPI)
- if(SYCL_FOUND AND LEVEL_ZERO_FOUND)
- message(STATUS "Found oneAPI: ${SYCL_LIBRARY}")
+ if(SYCL_FOUND AND SYCL_VERSION VERSION_GREATER_EQUAL 6.0 AND LEVEL_ZERO_FOUND)
message(STATUS "Found Level Zero: ${LEVEL_ZERO_LIBRARY}")
if(WITH_CYCLES_ONEAPI_BINARIES)
@@ -675,13 +676,14 @@ if(WITH_CYCLES_DEVICE_ONEAPI)
endif()
if(NOT EXISTS ${OCLOC_INSTALL_DIR})
- message(STATUS "oneAPI ocloc not found in ${OCLOC_INSTALL_DIR}, disabling WITH_CYCLES_ONEAPI_BINARIES."
+ set(OCLOC_FOUND OFF)
+ message(STATUS "oneAPI ocloc not found in ${OCLOC_INSTALL_DIR}."
" A different ocloc directory can be set using OCLOC_INSTALL_DIR cmake variable.")
- set(WITH_CYCLES_ONEAPI_BINARIES OFF)
+ set_and_warn_library_found("ocloc" OCLOC_FOUND WITH_CYCLES_ONEAPI_BINARIES)
endif()
endif()
else()
- message(STATUS "oneAPI or Level Zero not found, disabling WITH_CYCLES_DEVICE_ONEAPI")
+ message(STATUS "SYCL 6.0+ or Level Zero not found, disabling WITH_CYCLES_DEVICE_ONEAPI")
set(WITH_CYCLES_DEVICE_ONEAPI OFF)
endif()
endif()
diff --git a/intern/cycles/device/CMakeLists.txt b/intern/cycles/device/CMakeLists.txt
index 5516e97f34f..5296d819e42 100644
--- a/intern/cycles/device/CMakeLists.txt
+++ b/intern/cycles/device/CMakeLists.txt
@@ -187,18 +187,22 @@ if(WITH_CYCLES_DEVICE_METAL)
)
endif()
if (WITH_CYCLES_DEVICE_ONEAPI)
+ if(WITH_CYCLES_ONEAPI_BINARIES)
+ set(cycles_kernel_oneapi_lib_suffix "_aot")
+ else()
+ set(cycles_kernel_oneapi_lib_suffix "_jit")
+ endif()
if(WIN32)
- set(cycles_kernel_oneapi_lib ${CMAKE_CURRENT_BINARY_DIR}/../kernel/cycles_kernel_oneapi.lib)
+ set(cycles_kernel_oneapi_lib ${CMAKE_CURRENT_BINARY_DIR}/../kernel/cycles_kernel_oneapi${cycles_kernel_oneapi_lib_suffix}.lib)
else()
- set(cycles_kernel_oneapi_lib ${CMAKE_CURRENT_BINARY_DIR}/../kernel/libcycles_kernel_oneapi.so)
+ set(cycles_kernel_oneapi_lib ${CMAKE_CURRENT_BINARY_DIR}/../kernel/libcycles_kernel_oneapi${cycles_kernel_oneapi_lib_suffix}.so)
+ endif()
+ list(APPEND LIB ${cycles_kernel_oneapi_lib})
+ if(WIN32)
+ list(APPEND LIB debug ${SYCL_LIBRARY_DEBUG} optimized ${SYCL_LIBRARY})
+ else()
+ list(APPEND LIB ${SYCL_LIBRARY})
endif()
- list(APPEND LIB
- ${cycles_kernel_oneapi_lib}
- "$<$<CONFIG:Debug>:${SYCL_LIBRARY_DEBUG}>"
- "$<$<CONFIG:Release>:${SYCL_LIBRARY}>"
- "$<$<CONFIG:RelWithDebInfo>:${SYCL_LIBRARY}>"
- "$<$<CONFIG:MinSizeRel>:${SYCL_LIBRARY}>"
- )
add_definitions(-DWITH_ONEAPI)
list(APPEND SRC
${SRC_ONEAPI}
diff --git a/intern/cycles/device/cuda/queue.cpp b/intern/cycles/device/cuda/queue.cpp
index 84b0a1e0dd6..69fae03e32c 100644
--- a/intern/cycles/device/cuda/queue.cpp
+++ b/intern/cycles/device/cuda/queue.cpp
@@ -49,7 +49,7 @@ int CUDADeviceQueue::num_concurrent_states(const size_t state_size) const
return num_states;
}
-int CUDADeviceQueue::num_concurrent_busy_states() const
+int CUDADeviceQueue::num_concurrent_busy_states(const size_t /*state_size*/) const
{
const int max_num_threads = cuda_device_->get_num_multiprocessors() *
cuda_device_->get_max_num_threads_per_multiprocessor();
diff --git a/intern/cycles/device/cuda/queue.h b/intern/cycles/device/cuda/queue.h
index b450f5b3592..7107afe70c9 100644
--- a/intern/cycles/device/cuda/queue.h
+++ b/intern/cycles/device/cuda/queue.h
@@ -23,7 +23,7 @@ class CUDADeviceQueue : public DeviceQueue {
~CUDADeviceQueue();
virtual int num_concurrent_states(const size_t state_size) const override;
- virtual int num_concurrent_busy_states() const override;
+ virtual int num_concurrent_busy_states(const size_t state_size) const override;
virtual void init_execution() override;
diff --git a/intern/cycles/device/hip/queue.cpp b/intern/cycles/device/hip/queue.cpp
index 3f8b6267100..e93a9b4df3a 100644
--- a/intern/cycles/device/hip/queue.cpp
+++ b/intern/cycles/device/hip/queue.cpp
@@ -49,7 +49,7 @@ int HIPDeviceQueue::num_concurrent_states(const size_t state_size) const
return num_states;
}
-int HIPDeviceQueue::num_concurrent_busy_states() const
+int HIPDeviceQueue::num_concurrent_busy_states(const size_t /*state_size*/) const
{
const int max_num_threads = hip_device_->get_num_multiprocessors() *
hip_device_->get_max_num_threads_per_multiprocessor();
diff --git a/intern/cycles/device/hip/queue.h b/intern/cycles/device/hip/queue.h
index 729d8a19acb..df0678108af 100644
--- a/intern/cycles/device/hip/queue.h
+++ b/intern/cycles/device/hip/queue.h
@@ -23,7 +23,7 @@ class HIPDeviceQueue : public DeviceQueue {
~HIPDeviceQueue();
virtual int num_concurrent_states(const size_t state_size) const override;
- virtual int num_concurrent_busy_states() const override;
+ virtual int num_concurrent_busy_states(const size_t state_size) const override;
virtual void init_execution() override;
diff --git a/intern/cycles/device/metal/device_impl.mm b/intern/cycles/device/metal/device_impl.mm
index 6a16d4bb3b4..6f1042b1e55 100644
--- a/intern/cycles/device/metal/device_impl.mm
+++ b/intern/cycles/device/metal/device_impl.mm
@@ -296,9 +296,11 @@ void MetalDevice::make_source(MetalPipelineType pso_type, const uint kernel_feat
}
source = global_defines + source;
+# if 0
metal_printf("================\n%s================\n\%s================\n",
global_defines.c_str(),
baked_constants.c_str());
+# endif
/* Generate an MD5 from the source and include any baked constants. This is used when caching
* PSOs. */
@@ -339,6 +341,14 @@ bool MetalDevice::compile_and_load(MetalPipelineType pso_type)
MTLCompileOptions *options = [[MTLCompileOptions alloc] init];
+# if defined(MAC_OS_VERSION_13_0)
+ if (@available(macos 13.0, *)) {
+ if (device_vendor == METAL_GPU_INTEL) {
+ [options setOptimizationLevel:MTLLibraryOptimizationLevelSize];
+ }
+ }
+# endif
+
options.fastMathEnabled = YES;
if (@available(macOS 12.0, *)) {
options.languageVersion = MTLLanguageVersion2_4;
diff --git a/intern/cycles/device/metal/kernel.mm b/intern/cycles/device/metal/kernel.mm
index 5e0cb6d18f4..55938d1a03a 100644
--- a/intern/cycles/device/metal/kernel.mm
+++ b/intern/cycles/device/metal/kernel.mm
@@ -162,6 +162,13 @@ bool ShaderCache::should_load_kernel(DeviceKernel device_kernel,
}
}
+ if (device_kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE) {
+ if ((device->kernel_features & KERNEL_FEATURE_MNEE) == 0) {
+ /* Skip shade_surface_mnee kernel if the scene doesn't require it. */
+ return false;
+ }
+ }
+
if (pso_type != PSO_GENERIC) {
/* Only specialize kernels where it can make an impact. */
if (device_kernel < DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST ||
@@ -317,6 +324,12 @@ bool MetalKernelPipeline::should_use_binary_archive() const
}
}
+ /* Workaround for Intel GPU having issue using Binary Archives */
+ MetalGPUVendor gpu_vendor = MetalInfo::get_device_vendor(mtlDevice);
+ if (gpu_vendor == METAL_GPU_INTEL) {
+ return false;
+ }
+
if (pso_type == PSO_GENERIC) {
/* Archive the generic kernels. */
return true;
diff --git a/intern/cycles/device/metal/queue.h b/intern/cycles/device/metal/queue.h
index fc32740f3e1..2a6c12e2a60 100644
--- a/intern/cycles/device/metal/queue.h
+++ b/intern/cycles/device/metal/queue.h
@@ -23,7 +23,7 @@ class MetalDeviceQueue : public DeviceQueue {
~MetalDeviceQueue();
virtual int num_concurrent_states(const size_t) const override;
- virtual int num_concurrent_busy_states() const override;
+ virtual int num_concurrent_busy_states(const size_t) const override;
virtual int num_sort_partition_elements() const override;
virtual void init_execution() override;
diff --git a/intern/cycles/device/metal/queue.mm b/intern/cycles/device/metal/queue.mm
index 5ac63a16c61..c0df2c8553f 100644
--- a/intern/cycles/device/metal/queue.mm
+++ b/intern/cycles/device/metal/queue.mm
@@ -264,33 +264,46 @@ MetalDeviceQueue::~MetalDeviceQueue()
}
}
-int MetalDeviceQueue::num_concurrent_states(const size_t /*state_size*/) const
+int MetalDeviceQueue::num_concurrent_states(const size_t state_size) const
{
- /* METAL_WIP */
- /* TODO: compute automatically. */
- /* TODO: must have at least num_threads_per_block. */
- int result = 1048576;
- if (metal_device_->device_vendor == METAL_GPU_AMD) {
- result *= 2;
+ static int result = 0;
+ if (result) {
+ return result;
}
- else if (metal_device_->device_vendor == METAL_GPU_APPLE) {
+
+ result = 1048576;
+ if (metal_device_->device_vendor == METAL_GPU_APPLE) {
result *= 4;
+
+ if (MetalInfo::get_apple_gpu_architecture(metal_device_->mtlDevice) == APPLE_M2) {
+ size_t system_ram = system_physical_ram();
+ size_t allocated_so_far = [metal_device_->mtlDevice currentAllocatedSize];
+ size_t max_recommended_working_set = [metal_device_->mtlDevice recommendedMaxWorkingSetSize];
+
+ /* Determine whether we can double the state count, and leave enough GPU-available memory
+ * (1/8 the system RAM or 1GB - whichever is largest). Enlarging the state size allows us to
+ * keep dispatch sizes high and minimize work submission overheads. */
+ size_t min_headroom = std::max(system_ram / 8, size_t(1024 * 1024 * 1024));
+ size_t total_state_size = result * state_size;
+ if (max_recommended_working_set - allocated_so_far - total_state_size * 2 >= min_headroom) {
+ result *= 2;
+ metal_printf("Doubling state count to exploit available RAM (new size = %d)\n", result);
+ }
+ }
+ }
+ else if (metal_device_->device_vendor == METAL_GPU_AMD) {
+ /* METAL_WIP */
+ /* TODO: compute automatically. */
+ /* TODO: must have at least num_threads_per_block. */
+ result *= 2;
}
return result;
}
-int MetalDeviceQueue::num_concurrent_busy_states() const
+int MetalDeviceQueue::num_concurrent_busy_states(const size_t state_size) const
{
- /* METAL_WIP */
- /* TODO: compute automatically. */
- int result = 65536;
- if (metal_device_->device_vendor == METAL_GPU_AMD) {
- result *= 2;
- }
- else if (metal_device_->device_vendor == METAL_GPU_APPLE) {
- result *= 4;
- }
- return result;
+ /* A 1:4 busy:total ratio gives best rendering performance, independent of total state count. */
+ return num_concurrent_states(state_size) / 4;
}
int MetalDeviceQueue::num_sort_partition_elements() const
diff --git a/intern/cycles/device/metal/util.mm b/intern/cycles/device/metal/util.mm
index 65c67c400fe..f47638fac15 100644
--- a/intern/cycles/device/metal/util.mm
+++ b/intern/cycles/device/metal/util.mm
@@ -110,6 +110,12 @@ vector<id<MTLDevice>> const &MetalInfo::get_usable_devices()
usable |= (vendor == METAL_GPU_AMD);
}
+# if defined(MAC_OS_VERSION_13_0)
+ if (@available(macos 13.0, *)) {
+ usable |= (vendor == METAL_GPU_INTEL);
+ }
+# endif
+
if (usable) {
metal_printf("- %s\n", device_name.c_str());
[device retain];
diff --git a/intern/cycles/device/oneapi/device.cpp b/intern/cycles/device/oneapi/device.cpp
index f303ab41627..66d6f749e30 100644
--- a/intern/cycles/device/oneapi/device.cpp
+++ b/intern/cycles/device/oneapi/device.cpp
@@ -39,7 +39,7 @@ bool device_oneapi_init()
_putenv_s("SYCL_CACHE_THRESHOLD", "0");
}
if (getenv("SYCL_DEVICE_FILTER") == nullptr) {
- _putenv_s("SYCL_DEVICE_FILTER", "host,level_zero");
+ _putenv_s("SYCL_DEVICE_FILTER", "level_zero");
}
if (getenv("SYCL_ENABLE_PCI") == nullptr) {
_putenv_s("SYCL_ENABLE_PCI", "1");
@@ -50,7 +50,7 @@ bool device_oneapi_init()
# elif __linux__
setenv("SYCL_CACHE_PERSISTENT", "1", false);
setenv("SYCL_CACHE_THRESHOLD", "0", false);
- setenv("SYCL_DEVICE_FILTER", "host,level_zero", false);
+ setenv("SYCL_DEVICE_FILTER", "level_zero", false);
setenv("SYCL_ENABLE_PCI", "1", false);
setenv("SYCL_PI_LEVEL_ZERO_USE_COPY_ENGINE_FOR_IN_ORDER_QUEUE", "0", false);
# endif
diff --git a/intern/cycles/device/oneapi/device_impl.cpp b/intern/cycles/device/oneapi/device_impl.cpp
index 91f53fd1eae..d0ddd69289c 100644
--- a/intern/cycles/device/oneapi/device_impl.cpp
+++ b/intern/cycles/device/oneapi/device_impl.cpp
@@ -430,9 +430,9 @@ void OneapiDevice::check_usm(SyclQueue *queue_, const void *usm_ptr, bool allow_
sycl::usm::alloc usm_type = get_pointer_type(usm_ptr, queue->get_context());
(void)usm_type;
assert(usm_type == sycl::usm::alloc::device ||
- ((device_type == sycl::info::device_type::host ||
- device_type == sycl::info::device_type::cpu || allow_host) &&
- usm_type == sycl::usm::alloc::host));
+ ((device_type == sycl::info::device_type::cpu || allow_host) &&
+ usm_type == sycl::usm::alloc::host ||
+ usm_type == sycl::usm::alloc::unknown));
# else
/* Silence warning about unused arguments. */
(void)queue_;
@@ -668,16 +668,9 @@ int OneapiDevice::parse_driver_build_version(const sycl::device &device)
std::vector<sycl::device> OneapiDevice::available_devices()
{
bool allow_all_devices = false;
- if (getenv("CYCLES_ONEAPI_ALL_DEVICES") != nullptr)
+ if (getenv("CYCLES_ONEAPI_ALL_DEVICES") != nullptr) {
allow_all_devices = true;
-
- /* Host device is useful only for debugging at the moment
- * so we hide this device with default build settings. */
-# ifdef WITH_ONEAPI_SYCL_HOST_ENABLED
- bool allow_host = true;
-# else
- bool allow_host = false;
-# endif
+ }
const std::vector<sycl::platform> &oneapi_platforms = sycl::platform::get_platforms();
@@ -690,33 +683,28 @@ std::vector<sycl::device> OneapiDevice::available_devices()
}
const std::vector<sycl::device> &oneapi_devices =
- (allow_all_devices || allow_host) ? platform.get_devices(sycl::info::device_type::all) :
- platform.get_devices(sycl::info::device_type::gpu);
+ (allow_all_devices) ? platform.get_devices(sycl::info::device_type::all) :
+ platform.get_devices(sycl::info::device_type::gpu);
for (const sycl::device &device : oneapi_devices) {
- if (allow_all_devices) {
- /* still filter out host device if build doesn't support it. */
- if (allow_host || !device.is_host()) {
- available_devices.push_back(device);
- }
- }
- else {
- bool filter_out = false;
-
+ bool filter_out = false;
+ if (!allow_all_devices) {
/* For now we support all Intel(R) Arc(TM) devices and likely any future GPU,
* assuming they have either more than 96 Execution Units or not 7 threads per EU.
* Official support can be broaden to older and smaller GPUs once ready. */
- if (device.is_gpu() && platform.get_backend() == sycl::backend::ext_oneapi_level_zero) {
- /* Filtered-out defaults in-case these values aren't available through too old L0
- * runtime. */
+ if (!device.is_gpu() || platform.get_backend() != sycl::backend::ext_oneapi_level_zero) {
+ filter_out = true;
+ }
+ else {
+ /* Filtered-out defaults in-case these values aren't available. */
int number_of_eus = 96;
int threads_per_eu = 7;
if (device.has(sycl::aspect::ext_intel_gpu_eu_count)) {
- number_of_eus = device.get_info<sycl::info::device::ext_intel_gpu_eu_count>();
+ number_of_eus = device.get_info<sycl::ext::intel::info::device::gpu_eu_count>();
}
if (device.has(sycl::aspect::ext_intel_gpu_hw_threads_per_eu)) {
threads_per_eu =
- device.get_info<sycl::info::device::ext_intel_gpu_hw_threads_per_eu>();
+ device.get_info<sycl::ext::intel::info::device::gpu_hw_threads_per_eu>();
}
/* This filters out all Level-Zero supported GPUs from older generation than Arc. */
if (number_of_eus <= 96 && threads_per_eu == 7) {
@@ -732,16 +720,9 @@ std::vector<sycl::device> OneapiDevice::available_devices()
}
}
}
- else if (!allow_host && device.is_host()) {
- filter_out = true;
- }
- else if (!allow_all_devices) {
- filter_out = true;
- }
-
- if (!filter_out) {
- available_devices.push_back(device);
- }
+ }
+ if (!filter_out) {
+ available_devices.push_back(device);
}
}
}
@@ -797,9 +778,7 @@ char *OneapiDevice::device_capabilities()
GET_NUM_ATTR(native_vector_width_double)
GET_NUM_ATTR(native_vector_width_half)
- size_t max_clock_frequency =
- (size_t)(device.is_host() ? (size_t)0 :
- device.get_info<sycl::info::device::max_clock_frequency>());
+ size_t max_clock_frequency = device.get_info<sycl::info::device::max_clock_frequency>();
WRITE_ATTR("max_clock_frequency", max_clock_frequency)
GET_NUM_ATTR(address_bits)
@@ -837,7 +816,7 @@ void OneapiDevice::iterate_devices(OneAPIDeviceIteratorCallback cb, void *user_p
std::string name = device.get_info<sycl::info::device::name>();
std::string id = "ONEAPI_" + platform_name + "_" + name;
if (device.has(sycl::aspect::ext_intel_pci_address)) {
- id.append("_" + device.get_info<sycl::info::device::ext_intel_pci_address>());
+ id.append("_" + device.get_info<sycl::ext::intel::info::device::pci_address>());
}
(cb)(id.c_str(), name.c_str(), num, user_ptr);
num++;
@@ -855,7 +834,7 @@ int OneapiDevice::get_num_multiprocessors()
{
const sycl::device &device = reinterpret_cast<sycl::queue *>(device_queue_)->get_device();
if (device.has(sycl::aspect::ext_intel_gpu_eu_count)) {
- return device.get_info<sycl::info::device::ext_intel_gpu_eu_count>();
+ return device.get_info<sycl::ext::intel::info::device::gpu_eu_count>();
}
else
return 0;
@@ -866,8 +845,8 @@ int OneapiDevice::get_max_num_threads_per_multiprocessor()
const sycl::device &device = reinterpret_cast<sycl::queue *>(device_queue_)->get_device();
if (device.has(sycl::aspect::ext_intel_gpu_eu_simd_width) &&
device.has(sycl::aspect::ext_intel_gpu_hw_threads_per_eu)) {
- return device.get_info<sycl::info::device::ext_intel_gpu_eu_simd_width>() *
- device.get_info<sycl::info::device::ext_intel_gpu_hw_threads_per_eu>();
+ return device.get_info<sycl::ext::intel::info::device::gpu_eu_simd_width>() *
+ device.get_info<sycl::ext::intel::info::device::gpu_hw_threads_per_eu>();
}
else
return 0;
diff --git a/intern/cycles/device/oneapi/device_impl.h b/intern/cycles/device/oneapi/device_impl.h
index 62034150eac..197cf03d60d 100644
--- a/intern/cycles/device/oneapi/device_impl.h
+++ b/intern/cycles/device/oneapi/device_impl.h
@@ -3,7 +3,7 @@
#ifdef WITH_ONEAPI
-# include <CL/sycl.hpp>
+# include <sycl/sycl.hpp>
# include "device/device.h"
# include "device/oneapi/device.h"
diff --git a/intern/cycles/device/oneapi/queue.cpp b/intern/cycles/device/oneapi/queue.cpp
index 9632b14d485..3d019661aa8 100644
--- a/intern/cycles/device/oneapi/queue.cpp
+++ b/intern/cycles/device/oneapi/queue.cpp
@@ -43,7 +43,7 @@ int OneapiDeviceQueue::num_concurrent_states(const size_t state_size) const
return num_states;
}
-int OneapiDeviceQueue::num_concurrent_busy_states() const
+int OneapiDeviceQueue::num_concurrent_busy_states(const size_t /*state_size*/) const
{
const int max_num_threads = oneapi_device_->get_num_multiprocessors() *
oneapi_device_->get_max_num_threads_per_multiprocessor();
diff --git a/intern/cycles/device/oneapi/queue.h b/intern/cycles/device/oneapi/queue.h
index 32363bf2a6e..bbd947b49cb 100644
--- a/intern/cycles/device/oneapi/queue.h
+++ b/intern/cycles/device/oneapi/queue.h
@@ -25,7 +25,7 @@ class OneapiDeviceQueue : public DeviceQueue {
virtual int num_concurrent_states(const size_t state_size) const override;
- virtual int num_concurrent_busy_states() const override;
+ virtual int num_concurrent_busy_states(const size_t state_size) const override;
virtual void init_execution() override;
diff --git a/intern/cycles/device/queue.h b/intern/cycles/device/queue.h
index 1d6a8d736b7..e27e081a407 100644
--- a/intern/cycles/device/queue.h
+++ b/intern/cycles/device/queue.h
@@ -103,7 +103,7 @@ class DeviceQueue {
/* Number of states which keeps the device occupied with work without losing performance.
* The renderer will add more work (when available) when number of active paths falls below this
* value. */
- virtual int num_concurrent_busy_states() const = 0;
+ virtual int num_concurrent_busy_states(const size_t state_size) const = 0;
/* Number of elements in a partition of sorted shaders, that improves memory locality of
* integrator state fetch at the cost of decreased coherence for shader kernel execution. */
diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp
index ee250a6916b..547e8d50a22 100644
--- a/intern/cycles/integrator/path_trace_work_gpu.cpp
+++ b/intern/cycles/integrator/path_trace_work_gpu.cpp
@@ -18,13 +18,15 @@
CCL_NAMESPACE_BEGIN
-static size_t estimate_single_state_size()
+static size_t estimate_single_state_size(const uint kernel_features)
{
size_t state_size = 0;
#define KERNEL_STRUCT_BEGIN(name) for (int array_index = 0;; array_index++) {
-#define KERNEL_STRUCT_MEMBER(parent_struct, type, name, feature) state_size += sizeof(type);
-#define KERNEL_STRUCT_ARRAY_MEMBER(parent_struct, type, name, feature) state_size += sizeof(type);
+#define KERNEL_STRUCT_MEMBER(parent_struct, type, name, feature) \
+ state_size += (kernel_features & (feature)) ? sizeof(type) : 0;
+#define KERNEL_STRUCT_ARRAY_MEMBER(parent_struct, type, name, feature) \
+ state_size += (kernel_features & (feature)) ? sizeof(type) : 0;
#define KERNEL_STRUCT_END(name) \
break; \
}
@@ -76,16 +78,11 @@ PathTraceWorkGPU::PathTraceWorkGPU(Device *device,
num_queued_paths_(device, "num_queued_paths", MEM_READ_WRITE),
work_tiles_(device, "work_tiles", MEM_READ_WRITE),
display_rgba_half_(device, "display buffer half", MEM_READ_WRITE),
- max_num_paths_(queue_->num_concurrent_states(estimate_single_state_size())),
- min_num_active_main_paths_(queue_->num_concurrent_busy_states()),
+ max_num_paths_(0),
+ min_num_active_main_paths_(0),
max_active_main_path_index_(0)
{
memset(&integrator_state_gpu_, 0, sizeof(integrator_state_gpu_));
-
- /* Limit number of active paths to the half of the overall state. This is due to the logic in the
- * path compaction which relies on the fact that regeneration does not happen sooner than half of
- * the states are available again. */
- min_num_active_main_paths_ = min(min_num_active_main_paths_, max_num_paths_ / 2);
}
void PathTraceWorkGPU::alloc_integrator_soa()
@@ -103,6 +100,20 @@ void PathTraceWorkGPU::alloc_integrator_soa()
integrator_state_soa_volume_stack_size_ = max(integrator_state_soa_volume_stack_size_,
requested_volume_stack_size);
+ /* Determine the number of path states. Deferring this for as long as possible allows the
+ * back-end to make better decisions about memory availability. */
+ if (max_num_paths_ == 0) {
+ size_t single_state_size = estimate_single_state_size(kernel_features);
+
+ max_num_paths_ = queue_->num_concurrent_states(single_state_size);
+ min_num_active_main_paths_ = queue_->num_concurrent_busy_states(single_state_size);
+
+ /* Limit number of active paths to the half of the overall state. This is due to the logic in
+ * the path compaction which relies on the fact that regeneration does not happen sooner than
+ * half of the states are available again. */
+ min_num_active_main_paths_ = min(min_num_active_main_paths_, max_num_paths_ / 2);
+ }
+
/* Allocate a device only memory buffer before for each struct member, and then
* write the pointers into a struct that resides in constant memory.
*
diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt
index 36c8b23d983..81c5f593974 100644
--- a/intern/cycles/kernel/CMakeLists.txt
+++ b/intern/cycles/kernel/CMakeLists.txt
@@ -713,10 +713,17 @@ endif()
# oneAPI module
if(WITH_CYCLES_DEVICE_ONEAPI)
+ if(WITH_CYCLES_ONEAPI_BINARIES)
+ set(cycles_kernel_oneapi_lib_suffix "_aot")
+ else()
+ set(cycles_kernel_oneapi_lib_suffix "_jit")
+ endif()
+
if(WIN32)
- set(cycles_kernel_oneapi_lib ${CMAKE_CURRENT_BINARY_DIR}/cycles_kernel_oneapi.dll)
+ set(cycles_kernel_oneapi_lib ${CMAKE_CURRENT_BINARY_DIR}/cycles_kernel_oneapi${cycles_kernel_oneapi_lib_suffix}.dll)
+ set(cycles_kernel_oneapi_linker_lib ${CMAKE_CURRENT_BINARY_DIR}/cycles_kernel_oneapi${cycles_kernel_oneapi_lib_suffix}.lib)
else()
- set(cycles_kernel_oneapi_lib ${CMAKE_CURRENT_BINARY_DIR}/libcycles_kernel_oneapi.so)
+ set(cycles_kernel_oneapi_lib ${CMAKE_CURRENT_BINARY_DIR}/libcycles_kernel_oneapi${cycles_kernel_oneapi_lib_suffix}.so)
endif()
set(cycles_oneapi_kernel_sources
@@ -751,10 +758,6 @@ if(WITH_CYCLES_DEVICE_ONEAPI)
${SYCL_CPP_FLAGS}
)
- if (WITH_CYCLES_ONEAPI_SYCL_HOST_ENABLED)
- list(APPEND sycl_compiler_flags -DWITH_ONEAPI_SYCL_HOST_ENABLED)
- endif()
-
# Set defaults for spir64 and spir64_gen options
if (NOT DEFINED CYCLES_ONEAPI_SYCL_OPTIONS_spir64)
set(CYCLES_ONEAPI_SYCL_OPTIONS_spir64 "-options '-ze-opt-large-register-file -ze-opt-regular-grf-kernel integrator_intersect'")
@@ -767,6 +770,8 @@ if(WITH_CYCLES_DEVICE_ONEAPI)
string(PREPEND CYCLES_ONEAPI_SYCL_OPTIONS_spir64_gen "-device ${CYCLES_ONEAPI_SPIR64_GEN_DEVICES} ")
if (WITH_CYCLES_ONEAPI_BINARIES)
+ # AoT binaries aren't currently reused when calling sycl::build.
+ list (APPEND sycl_compiler_flags -DSYCL_SKIP_KERNELS_PRELOAD)
# Iterate over all targest and their options
list (JOIN CYCLES_ONEAPI_SYCL_TARGETS "," targets_string)
list (APPEND sycl_compiler_flags -fsycl-targets=${targets_string})
@@ -819,12 +824,17 @@ if(WITH_CYCLES_DEVICE_ONEAPI)
-DONEAPI_EXPORT)
string(REPLACE /Redist/ /Tools/ MSVC_TOOLS_DIR ${MSVC_REDIST_DIR})
- if(NOT CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION) # case for Ninja on Windows
+ # Version Folder between Redist and Tools can mismatch sometimes
+ if(NOT EXISTS ${MSVC_TOOLS_DIR})
+ get_filename_component(cmake_ar_dir ${CMAKE_AR} DIRECTORY)
+ get_filename_component(MSVC_TOOLS_DIR "${cmake_ar_dir}/../../../" ABSOLUTE)
+ endif()
+ if(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION)
+ set(WINDOWS_KIT_DIR ${WINDOWS_KITS_DIR}/Lib/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION})
+ else() # case for Ninja on Windows
get_filename_component(cmake_mt_dir ${CMAKE_MT} DIRECTORY)
string(REPLACE /bin/ /Lib/ WINDOWS_KIT_DIR ${cmake_mt_dir})
get_filename_component(WINDOWS_KIT_DIR "${WINDOWS_KIT_DIR}/../" ABSOLUTE)
- else()
- set(WINDOWS_KIT_DIR ${WINDOWS_KITS_DIR}/Lib/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION})
endif()
list(APPEND sycl_compiler_flags
-L "${MSVC_TOOLS_DIR}/lib/x64"
@@ -836,15 +846,13 @@ if(WITH_CYCLES_DEVICE_ONEAPI)
set(sycl_compiler_flags_RelWithDebInfo ${sycl_compiler_flags})
set(sycl_compiler_flags_MinSizeRel ${sycl_compiler_flags})
list(APPEND sycl_compiler_flags_RelWithDebInfo -g)
- get_filename_component(sycl_library_debug_name ${SYCL_LIBRARY_DEBUG} NAME_WE)
list(APPEND sycl_compiler_flags_Debug
-g
-D_DEBUG
- -nostdlib -Xclang --dependent-lib=msvcrtd
- -Xclang --dependent-lib=${sycl_library_debug_name})
+ -nostdlib -Xclang --dependent-lib=msvcrtd)
add_custom_command(
- OUTPUT ${cycles_kernel_oneapi_lib}
+ OUTPUT ${cycles_kernel_oneapi_lib} ${cycles_kernel_oneapi_linker_lib}
COMMAND ${CMAKE_COMMAND} -E env
"LIB=${sycl_compiler_root}/../lib" # for compiler to find sycl.lib
"PATH=${OCLOC_INSTALL_DIR}\;${sycl_compiler_root}"
diff --git a/intern/cycles/kernel/bvh/shadow_all.h b/intern/cycles/kernel/bvh/shadow_all.h
index 2ffe1496c72..b31ba479e4f 100644
--- a/intern/cycles/kernel/bvh/shadow_all.h
+++ b/intern/cycles/kernel/bvh/shadow_all.h
@@ -229,7 +229,7 @@ ccl_device_inline
/* Always use baked shadow transparency for curves. */
if (isect.type & PRIMITIVE_CURVE) {
*r_throughput *= intersection_curve_shadow_transparency(
- kg, isect.object, isect.prim, isect.u);
+ kg, isect.object, isect.prim, isect.type, isect.u);
if (*r_throughput < CURVE_SHADOW_TRANSPARENCY_CUTOFF) {
return true;
diff --git a/intern/cycles/kernel/bvh/util.h b/intern/cycles/kernel/bvh/util.h
index a57703a8b8c..9ba787550c5 100644
--- a/intern/cycles/kernel/bvh/util.h
+++ b/intern/cycles/kernel/bvh/util.h
@@ -190,10 +190,8 @@ ccl_device_inline int intersection_find_attribute(KernelGlobals kg,
/* Cut-off value to stop transparent shadow tracing when practically opaque. */
#define CURVE_SHADOW_TRANSPARENCY_CUTOFF 0.001f
-ccl_device_inline float intersection_curve_shadow_transparency(KernelGlobals kg,
- const int object,
- const int prim,
- const float u)
+ccl_device_inline float intersection_curve_shadow_transparency(
+ KernelGlobals kg, const int object, const int prim, const int type, const float u)
{
/* Find attribute. */
const int offset = intersection_find_attribute(kg, object, ATTR_STD_SHADOW_TRANSPARENCY);
@@ -204,7 +202,7 @@ ccl_device_inline float intersection_curve_shadow_transparency(KernelGlobals kg,
/* Interpolate transparency between curve keys. */
const KernelCurve kcurve = kernel_data_fetch(curves, prim);
- const int k0 = kcurve.first_key + PRIMITIVE_UNPACK_SEGMENT(kcurve.type);
+ const int k0 = kcurve.first_key + PRIMITIVE_UNPACK_SEGMENT(type);
const int k1 = k0 + 1;
const float f0 = kernel_data_fetch(attributes_float, offset + k0);
diff --git a/intern/cycles/kernel/camera/projection.h b/intern/cycles/kernel/camera/projection.h
index c9fe3a6c7fb..1d16aa35abe 100644
--- a/intern/cycles/kernel/camera/projection.h
+++ b/intern/cycles/kernel/camera/projection.h
@@ -201,11 +201,35 @@ ccl_device float2 direction_to_mirrorball(float3 dir)
return make_float2(u, v);
}
+/* Single face of a equiangular cube map projection as described in
+ https://blog.google/products/google-ar-vr/bringing-pixels-front-and-center-vr-video/ */
+ccl_device float3 equiangular_cubemap_face_to_direction(float u, float v)
+{
+ u = (1.0f - u);
+
+ u = tanf(u * M_PI_2_F - M_PI_4_F);
+ v = tanf(v * M_PI_2_F - M_PI_4_F);
+
+ return make_float3(1.0f, u, v);
+}
+
+ccl_device float2 direction_to_equiangular_cubemap_face(float3 dir)
+{
+ float u = atan2f(dir.y, dir.x) * 2.0f / M_PI_F + 0.5f;
+ float v = atan2f(dir.z, dir.x) * 2.0f / M_PI_F + 0.5f;
+
+ u = 1.0f - u;
+
+ return make_float2(u, v);
+}
+
ccl_device_inline float3 panorama_to_direction(ccl_constant KernelCamera *cam, float u, float v)
{
switch (cam->panorama_type) {
case PANORAMA_EQUIRECTANGULAR:
return equirectangular_range_to_direction(u, v, cam->equirectangular_range);
+ case PANORAMA_EQUIANGULAR_CUBEMAP_FACE:
+ return equiangular_cubemap_face_to_direction(u, v);
case PANORAMA_MIRRORBALL:
return mirrorball_to_direction(u, v);
case PANORAMA_FISHEYE_EQUIDISTANT:
@@ -230,6 +254,8 @@ ccl_device_inline float2 direction_to_panorama(ccl_constant KernelCamera *cam, f
switch (cam->panorama_type) {
case PANORAMA_EQUIRECTANGULAR:
return direction_to_equirectangular_range(dir, cam->equirectangular_range);
+ case PANORAMA_EQUIANGULAR_CUBEMAP_FACE:
+ return direction_to_equiangular_cubemap_face(dir);
case PANORAMA_MIRRORBALL:
return direction_to_mirrorball(dir);
case PANORAMA_FISHEYE_EQUIDISTANT:
diff --git a/intern/cycles/kernel/device/cpu/bvh.h b/intern/cycles/kernel/device/cpu/bvh.h
index d9267e1cd6d..2d7d8c2d704 100644
--- a/intern/cycles/kernel/device/cpu/bvh.h
+++ b/intern/cycles/kernel/device/cpu/bvh.h
@@ -252,7 +252,7 @@ ccl_device void kernel_embree_filter_occluded_func(const RTCFilterFunctionNArgum
/* Always use baked shadow transparency for curves. */
if (current_isect.type & PRIMITIVE_CURVE) {
ctx->throughput *= intersection_curve_shadow_transparency(
- kg, current_isect.object, current_isect.prim, current_isect.u);
+ kg, current_isect.object, current_isect.prim, current_isect.type, current_isect.u);
if (ctx->throughput < CURVE_SHADOW_TRANSPARENCY_CUTOFF) {
ctx->opaque_hit = true;
diff --git a/intern/cycles/kernel/device/gpu/parallel_active_index.h b/intern/cycles/kernel/device/gpu/parallel_active_index.h
index c1df49c4f49..38cdcb572eb 100644
--- a/intern/cycles/kernel/device/gpu/parallel_active_index.h
+++ b/intern/cycles/kernel/device/gpu/parallel_active_index.h
@@ -23,22 +23,6 @@ CCL_NAMESPACE_BEGIN
* and keep device specific code in compat.h */
#ifdef __KERNEL_ONEAPI__
-# ifdef WITH_ONEAPI_SYCL_HOST_ENABLED
-template<typename IsActiveOp>
-void cpu_serial_active_index_array_impl(const uint num_states,
- ccl_global int *ccl_restrict indices,
- ccl_global int *ccl_restrict num_indices,
- IsActiveOp is_active_op)
-{
- int write_index = 0;
- for (int state_index = 0; state_index < num_states; state_index++) {
- if (is_active_op(state_index))
- indices[write_index++] = state_index;
- }
- *num_indices = write_index;
- return;
-}
-# endif /* WITH_ONEAPI_SYCL_HOST_ENABLED */
template<typename IsActiveOp>
void gpu_parallel_active_index_array_impl(const uint num_states,
@@ -182,18 +166,11 @@ __device__
num_simd_groups, \
simdgroup_offset)
#elif defined(__KERNEL_ONEAPI__)
-# ifdef WITH_ONEAPI_SYCL_HOST_ENABLED
-# define gpu_parallel_active_index_array( \
- blocksize, num_states, indices, num_indices, is_active_op) \
- if (ccl_gpu_global_size_x() == 1) \
- cpu_serial_active_index_array_impl(num_states, indices, num_indices, is_active_op); \
- else \
- gpu_parallel_active_index_array_impl(num_states, indices, num_indices, is_active_op);
-# else
-# define gpu_parallel_active_index_array( \
- blocksize, num_states, indices, num_indices, is_active_op) \
- gpu_parallel_active_index_array_impl(num_states, indices, num_indices, is_active_op)
-# endif
+
+# define gpu_parallel_active_index_array( \
+ blocksize, num_states, indices, num_indices, is_active_op) \
+ gpu_parallel_active_index_array_impl(num_states, indices, num_indices, is_active_op)
+
#else
# define gpu_parallel_active_index_array( \
diff --git a/intern/cycles/kernel/device/metal/context_begin.h b/intern/cycles/kernel/device/metal/context_begin.h
index 99cb1e3826e..e75ec9cadec 100644
--- a/intern/cycles/kernel/device/metal/context_begin.h
+++ b/intern/cycles/kernel/device/metal/context_begin.h
@@ -34,21 +34,48 @@ class MetalKernelContext {
kernel_assert(0);
return 0;
}
-
+
+#ifdef __KERNEL_METAL_INTEL__
+ template<typename TextureType, typename CoordsType>
+ inline __attribute__((__always_inline__))
+ auto ccl_gpu_tex_object_read_intel_workaround(TextureType texture_array,
+ const uint tid, const uint sid,
+ CoordsType coords) const
+ {
+ switch(sid) {
+ default:
+ case 0: return texture_array[tid].tex.sample(sampler(address::repeat, filter::nearest), coords);
+ case 1: return texture_array[tid].tex.sample(sampler(address::clamp_to_edge, filter::nearest), coords);
+ case 2: return texture_array[tid].tex.sample(sampler(address::clamp_to_zero, filter::nearest), coords);
+ case 3: return texture_array[tid].tex.sample(sampler(address::repeat, filter::linear), coords);
+ case 4: return texture_array[tid].tex.sample(sampler(address::clamp_to_edge, filter::linear), coords);
+ case 5: return texture_array[tid].tex.sample(sampler(address::clamp_to_zero, filter::linear), coords);
+ }
+ }
+#endif
+
// texture2d
template<>
inline __attribute__((__always_inline__))
float4 ccl_gpu_tex_object_read_2D(ccl_gpu_tex_object_2D tex, float x, float y) const {
const uint tid(tex);
const uint sid(tex >> 32);
+#ifndef __KERNEL_METAL_INTEL__
return metal_ancillaries->textures_2d[tid].tex.sample(metal_samplers[sid], float2(x, y));
+#else
+ return ccl_gpu_tex_object_read_intel_workaround(metal_ancillaries->textures_2d, tid, sid, float2(x, y));
+#endif
}
template<>
inline __attribute__((__always_inline__))
float ccl_gpu_tex_object_read_2D(ccl_gpu_tex_object_2D tex, float x, float y) const {
const uint tid(tex);
const uint sid(tex >> 32);
+#ifndef __KERNEL_METAL_INTEL__
return metal_ancillaries->textures_2d[tid].tex.sample(metal_samplers[sid], float2(x, y)).x;
+#else
+ return ccl_gpu_tex_object_read_intel_workaround(metal_ancillaries->textures_2d, tid, sid, float2(x, y)).x;
+#endif
}
// texture3d
@@ -57,14 +84,22 @@ class MetalKernelContext {
float4 ccl_gpu_tex_object_read_3D(ccl_gpu_tex_object_3D tex, float x, float y, float z) const {
const uint tid(tex);
const uint sid(tex >> 32);
+#ifndef __KERNEL_METAL_INTEL__
return metal_ancillaries->textures_3d[tid].tex.sample(metal_samplers[sid], float3(x, y, z));
+#else
+ return ccl_gpu_tex_object_read_intel_workaround(metal_ancillaries->textures_3d, tid, sid, float3(x, y, z));
+#endif
}
template<>
inline __attribute__((__always_inline__))
float ccl_gpu_tex_object_read_3D(ccl_gpu_tex_object_3D tex, float x, float y, float z) const {
const uint tid(tex);
const uint sid(tex >> 32);
+#ifndef __KERNEL_METAL_INTEL__
return metal_ancillaries->textures_3d[tid].tex.sample(metal_samplers[sid], float3(x, y, z)).x;
+#else
+ return ccl_gpu_tex_object_read_intel_workaround(metal_ancillaries->textures_3d, tid, sid, float3(x, y, z)).x;
+#endif
}
# include "kernel/device/gpu/image.h"
diff --git a/intern/cycles/kernel/device/metal/kernel.metal b/intern/cycles/kernel/device/metal/kernel.metal
index 5646c7446db..8b69ee025cd 100644
--- a/intern/cycles/kernel/device/metal/kernel.metal
+++ b/intern/cycles/kernel/device/metal/kernel.metal
@@ -228,7 +228,7 @@ bool metalrt_shadow_all_hit(constant KernelParamsMetal &launch_params_metal,
/* Always use baked shadow transparency for curves. */
if (type & PRIMITIVE_CURVE) {
float throughput = payload.throughput;
- throughput *= context.intersection_curve_shadow_transparency(nullptr, object, prim, u);
+ throughput *= context.intersection_curve_shadow_transparency(nullptr, object, prim, type, u);
payload.throughput = throughput;
payload.num_hits += 1;
diff --git a/intern/cycles/kernel/device/oneapi/compat.h b/intern/cycles/kernel/device/oneapi/compat.h
index 8ae40b0612e..dfaec65130c 100644
--- a/intern/cycles/kernel/device/oneapi/compat.h
+++ b/intern/cycles/kernel/device/oneapi/compat.h
@@ -55,18 +55,6 @@
#define ccl_gpu_kernel(block_num_threads, thread_num_registers)
#define ccl_gpu_kernel_threads(block_num_threads)
-#ifdef WITH_ONEAPI_SYCL_HOST_ENABLED
-# define KG_ND_ITEMS \
- kg->nd_item_local_id_0 = item.get_local_id(0); \
- kg->nd_item_local_range_0 = item.get_local_range(0); \
- kg->nd_item_group_0 = item.get_group(0); \
- kg->nd_item_group_range_0 = item.get_group_range(0); \
- kg->nd_item_global_id_0 = item.get_global_id(0); \
- kg->nd_item_global_range_0 = item.get_global_range(0);
-#else
-# define KG_ND_ITEMS
-#endif
-
#define ccl_gpu_kernel_signature(name, ...) \
void oneapi_kernel_##name(KernelGlobalsGPU *ccl_restrict kg, \
size_t kernel_global_size, \
@@ -76,8 +64,7 @@ void oneapi_kernel_##name(KernelGlobalsGPU *ccl_restrict kg, \
(kg); \
cgh.parallel_for<class kernel_##name>( \
sycl::nd_range<1>(kernel_global_size, kernel_local_size), \
- [=](sycl::nd_item<1> item) { \
- KG_ND_ITEMS
+ [=](sycl::nd_item<1> item) {
#define ccl_gpu_kernel_postfix \
}); \
@@ -95,31 +82,17 @@ void oneapi_kernel_##name(KernelGlobalsGPU *ccl_restrict kg, \
} ccl_gpu_kernel_lambda_pass((ONEAPIKernelContext *)kg)
/* GPU thread, block, grid size and index */
-#ifndef WITH_ONEAPI_SYCL_HOST_ENABLED
-# define ccl_gpu_thread_idx_x (sycl::ext::oneapi::experimental::this_nd_item<1>().get_local_id(0))
-# define ccl_gpu_block_dim_x (sycl::ext::oneapi::experimental::this_nd_item<1>().get_local_range(0))
-# define ccl_gpu_block_idx_x (sycl::ext::oneapi::experimental::this_nd_item<1>().get_group(0))
-# define ccl_gpu_grid_dim_x (sycl::ext::oneapi::experimental::this_nd_item<1>().get_group_range(0))
-# define ccl_gpu_warp_size (sycl::ext::oneapi::experimental::this_sub_group().get_local_range()[0])
-# define ccl_gpu_thread_mask(thread_warp) uint(0xFFFFFFFF >> (ccl_gpu_warp_size - thread_warp))
-
-# define ccl_gpu_global_id_x() (sycl::ext::oneapi::experimental::this_nd_item<1>().get_global_id(0))
-# define ccl_gpu_global_size_x() (sycl::ext::oneapi::experimental::this_nd_item<1>().get_global_range(0))
-#else
-# define ccl_gpu_thread_idx_x (kg->nd_item_local_id_0)
-# define ccl_gpu_block_dim_x (kg->nd_item_local_range_0)
-# define ccl_gpu_block_idx_x (kg->nd_item_group_0)
-# define ccl_gpu_grid_dim_x (kg->nd_item_group_range_0)
-# define ccl_gpu_warp_size (sycl::ext::oneapi::experimental::this_sub_group().get_local_range()[0])
-# define ccl_gpu_thread_mask(thread_warp) uint(0xFFFFFFFF >> (ccl_gpu_warp_size - thread_warp))
-
-# define ccl_gpu_global_id_x() (kg->nd_item_global_id_0)
-# define ccl_gpu_global_size_x() (kg->nd_item_global_range_0)
-#endif
+#define ccl_gpu_thread_idx_x (sycl::ext::oneapi::experimental::this_nd_item<1>().get_local_id(0))
+#define ccl_gpu_block_dim_x (sycl::ext::oneapi::experimental::this_nd_item<1>().get_local_range(0))
+#define ccl_gpu_block_idx_x (sycl::ext::oneapi::experimental::this_nd_item<1>().get_group(0))
+#define ccl_gpu_grid_dim_x (sycl::ext::oneapi::experimental::this_nd_item<1>().get_group_range(0))
+#define ccl_gpu_warp_size (sycl::ext::oneapi::experimental::this_sub_group().get_local_range()[0])
+#define ccl_gpu_thread_mask(thread_warp) uint(0xFFFFFFFF >> (ccl_gpu_warp_size - thread_warp))
+#define ccl_gpu_global_id_x() (sycl::ext::oneapi::experimental::this_nd_item<1>().get_global_id(0))
+#define ccl_gpu_global_size_x() (sycl::ext::oneapi::experimental::this_nd_item<1>().get_global_range(0))
/* GPU warp synchronization */
-
#define ccl_gpu_syncthreads() sycl::ext::oneapi::experimental::this_nd_item<1>().barrier()
#define ccl_gpu_local_syncthreads() sycl::ext::oneapi::experimental::this_nd_item<1>().barrier(sycl::access::fence_space::local_space)
#ifdef __SYCL_DEVICE_ONLY__
diff --git a/intern/cycles/kernel/device/oneapi/globals.h b/intern/cycles/kernel/device/oneapi/globals.h
index d60f4f135ba..116620eb725 100644
--- a/intern/cycles/kernel/device/oneapi/globals.h
+++ b/intern/cycles/kernel/device/oneapi/globals.h
@@ -23,15 +23,6 @@ typedef struct KernelGlobalsGPU {
#undef KERNEL_DATA_ARRAY
IntegratorStateGPU *integrator_state;
const KernelData *__data;
-#ifdef WITH_ONEAPI_SYCL_HOST_ENABLED
- size_t nd_item_local_id_0;
- size_t nd_item_local_range_0;
- size_t nd_item_group_0;
- size_t nd_item_group_range_0;
-
- size_t nd_item_global_id_0;
- size_t nd_item_global_range_0;
-#endif
} KernelGlobalsGPU;
typedef ccl_global KernelGlobalsGPU *ccl_restrict KernelGlobals;
diff --git a/intern/cycles/kernel/device/oneapi/kernel.cpp b/intern/cycles/kernel/device/oneapi/kernel.cpp
index 40e0b1f0b2b..525ae288f0c 100644
--- a/intern/cycles/kernel/device/oneapi/kernel.cpp
+++ b/intern/cycles/kernel/device/oneapi/kernel.cpp
@@ -8,7 +8,7 @@
# include <map>
# include <set>
-# include <CL/sycl.hpp>
+# include <sycl/sycl.hpp>
# include "kernel/device/oneapi/compat.h"
# include "kernel/device/oneapi/globals.h"
@@ -144,6 +144,10 @@ size_t oneapi_kernel_preferred_local_size(SyclQueue *queue,
bool oneapi_load_kernels(SyclQueue *queue_, const uint requested_features)
{
+# ifdef SYCL_SKIP_KERNELS_PRELOAD
+ (void)queue_;
+ (void)requested_features;
+# else
assert(queue_);
sycl::queue *queue = reinterpret_cast<sycl::queue *>(queue_);
@@ -175,7 +179,7 @@ bool oneapi_load_kernels(SyclQueue *queue_, const uint requested_features)
sycl::kernel_bundle<sycl::bundle_state::input> one_kernel_bundle =
sycl::get_kernel_bundle<sycl::bundle_state::input>(queue->get_context(), {kernel_id});
- sycl::build(one_kernel_bundle, {queue->get_device()}, sycl::property::queue::in_order());
+ sycl::build(one_kernel_bundle);
}
}
catch (sycl::exception const &e) {
@@ -184,7 +188,7 @@ bool oneapi_load_kernels(SyclQueue *queue_, const uint requested_features)
}
return false;
}
-
+# endif
return true;
}
@@ -226,13 +230,6 @@ bool oneapi_enqueue_kernel(KernelContext *kernel_context,
/* NOTE(@nsirgien): As for now non-uniform work-groups don't work on most oneAPI devices,
* we extend work size to fit uniformity requirements. */
global_size = groups_count * local_size;
-
-# ifdef WITH_ONEAPI_SYCL_HOST_ENABLED
- if (queue->get_device().is_host()) {
- global_size = 1;
- local_size = 1;
- }
-# endif
}
/* Let the compiler throw an error if there are any kernels missing in this implementation. */
diff --git a/intern/cycles/kernel/device/optix/bvh.h b/intern/cycles/kernel/device/optix/bvh.h
index fb9907709ce..6d81b44660c 100644
--- a/intern/cycles/kernel/device/optix/bvh.h
+++ b/intern/cycles/kernel/device/optix/bvh.h
@@ -202,7 +202,7 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit()
/* Always use baked shadow transparency for curves. */
if (type & PRIMITIVE_CURVE) {
float throughput = __uint_as_float(optixGetPayload_1());
- throughput *= intersection_curve_shadow_transparency(nullptr, object, prim, u);
+ throughput *= intersection_curve_shadow_transparency(nullptr, object, prim, type, u);
optixSetPayload_1(__float_as_uint(throughput));
optixSetPayload_2(uint16_pack_to_uint(num_recorded_hits, num_hits + 1));
diff --git a/intern/cycles/kernel/types.h b/intern/cycles/kernel/types.h
index 8f7cfd19169..24c5a6a4540 100644
--- a/intern/cycles/kernel/types.h
+++ b/intern/cycles/kernel/types.h
@@ -490,6 +490,7 @@ enum PanoramaType {
PANORAMA_FISHEYE_EQUISOLID = 2,
PANORAMA_MIRRORBALL = 3,
PANORAMA_FISHEYE_LENS_POLYNOMIAL = 4,
+ PANORAMA_EQUIANGULAR_CUBEMAP_FACE = 5,
PANORAMA_NUM_TYPES,
};
diff --git a/intern/cycles/scene/camera.cpp b/intern/cycles/scene/camera.cpp
index 240e5d9c128..255dd320ec7 100644
--- a/intern/cycles/scene/camera.cpp
+++ b/intern/cycles/scene/camera.cpp
@@ -84,6 +84,7 @@ NODE_DEFINE(Camera)
static NodeEnum panorama_type_enum;
panorama_type_enum.insert("equirectangular", PANORAMA_EQUIRECTANGULAR);
+ panorama_type_enum.insert("equiangular_cubemap_face", PANORAMA_EQUIANGULAR_CUBEMAP_FACE);
panorama_type_enum.insert("mirrorball", PANORAMA_MIRRORBALL);
panorama_type_enum.insert("fisheye_equidistant", PANORAMA_FISHEYE_EQUIDISTANT);
panorama_type_enum.insert("fisheye_equisolid", PANORAMA_FISHEYE_EQUISOLID);
diff --git a/intern/cycles/util/transform_inverse.h b/intern/cycles/util/transform_inverse.h
index b72bbc5f680..bb410a6daef 100644
--- a/intern/cycles/util/transform_inverse.h
+++ b/intern/cycles/util/transform_inverse.h
@@ -11,7 +11,7 @@ CCL_NAMESPACE_BEGIN
ccl_device_forceinline float3 transform_inverse_cross(const float3 a, const float3 b)
{
-#ifdef __AVX2__
+#if defined(__AVX2__) && defined(__KERNEL_SSE2__)
const ssef sse_a = (const __m128 &)a;
const ssef sse_b = (const __m128 &)b;
const ssef r = shuffle<1, 2, 0, 3>(
diff --git a/intern/cycles/util/types_float3.h b/intern/cycles/util/types_float3.h
index 87c6b1d3654..34430945c38 100644
--- a/intern/cycles/util/types_float3.h
+++ b/intern/cycles/util/types_float3.h
@@ -10,7 +10,12 @@
CCL_NAMESPACE_BEGIN
#ifndef __KERNEL_NATIVE_VECTOR_TYPES__
+# ifdef __KERNEL_ONEAPI__
+/* Define float3 as packed for oneAPI. */
+struct float3
+# else
struct ccl_try_align(16) float3
+# endif
{
# ifdef __KERNEL_GPU__
/* Compact structure for GPU. */
diff --git a/intern/ffmpeg/tests/ffmpeg_codecs.cc b/intern/ffmpeg/tests/ffmpeg_codecs.cc
index d0c40736884..e5c33202417 100644
--- a/intern/ffmpeg/tests/ffmpeg_codecs.cc
+++ b/intern/ffmpeg/tests/ffmpeg_codecs.cc
@@ -130,6 +130,7 @@ FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_DVVIDEO, AV_PIX_FMT_YUV420P)
FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_MPEG1VIDEO, AV_PIX_FMT_YUV420P)
FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_MPEG2VIDEO, AV_PIX_FMT_YUV420P)
FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_FLV1, AV_PIX_FMT_YUV420P)
+FFMPEG_TEST_VCODEC_ID(AV_CODEC_ID_AV1, AV_PIX_FMT_YUV420P)
/* Audio codecs */
@@ -149,6 +150,12 @@ FFMPEG_TEST_VCODEC_NAME(libx264, AV_PIX_FMT_YUV420P)
FFMPEG_TEST_VCODEC_NAME(libvpx, AV_PIX_FMT_YUV420P)
FFMPEG_TEST_VCODEC_NAME(libopenjpeg, AV_PIX_FMT_YUV420P)
FFMPEG_TEST_VCODEC_NAME(libxvid, AV_PIX_FMT_YUV420P)
+/* aom's AV1 encoder is "libaom-av1". FFMPEG_TEST_VCODEC_NAME(libaom-av1, ...)
+ * will not work because the dash will not work with the test macro. */
+TEST(ffmpeg, libaom_av1_AV_PIX_FMT_YUV420P)
+{
+ EXPECT_TRUE(test_codec_video_by_name("libaom-av1", AV_PIX_FMT_YUV420P));
+}
FFMPEG_TEST_ACODEC_NAME(libvorbis, AV_SAMPLE_FMT_FLTP)
FFMPEG_TEST_ACODEC_NAME(libopus, AV_SAMPLE_FMT_FLT)
FFMPEG_TEST_ACODEC_NAME(libmp3lame, AV_SAMPLE_FMT_FLTP)
diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt
index 2f8ea1f9065..ea21d831b0c 100644
--- a/intern/ghost/CMakeLists.txt
+++ b/intern/ghost/CMakeLists.txt
@@ -268,7 +268,7 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
if(WITH_GHOST_WAYLAND_DYNLOAD)
list(APPEND INC_SYS
- ../../intern/wayland_dynload/extern
+ ../wayland_dynload/extern
)
list(APPEND LIB
bf_intern_wayland_dynload
@@ -372,14 +372,22 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
generate_protocol_bindings(
"${WAYLAND_PROTOCOLS_DIR}/unstable/relative-pointer/relative-pointer-unstable-v1.xml"
)
+ # Pointer-gestures (multi-touch).
+ generate_protocol_bindings(
+ "${WAYLAND_PROTOCOLS_DIR}/unstable/pointer-gestures/pointer-gestures-unstable-v1.xml"
+ )
# Tablet.
generate_protocol_bindings(
"${WAYLAND_PROTOCOLS_DIR}/unstable/tablet/tablet-unstable-v2.xml"
)
-
- add_definitions(-DWITH_GHOST_WAYLAND)
+ # Primary-selection.
+ generate_protocol_bindings(
+ "${WAYLAND_PROTOCOLS_DIR}/unstable/primary-selection/primary-selection-unstable-v1.xml"
+ )
unset(INC_DST)
+
+ add_definitions(-DWITH_GHOST_WAYLAND)
endif()
if(WITH_INPUT_NDOF)
diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h
index 7fda535a7ac..62984c762c1 100644
--- a/intern/ghost/GHOST_C-api.h
+++ b/intern/ghost/GHOST_C-api.h
@@ -162,7 +162,6 @@ extern void GHOST_GetAllDisplayDimensions(GHOST_SystemHandle systemhandle,
* \param height: The height the window.
* \param state: The state of the window when opened.
* \param is_dialog: Stay on top of parent window, no icon in taskbar, can't be minimized.
- * \param type: The type of drawing context installed in this window.
* \param glSettings: Misc OpenGL options.
* \return A handle to the new window ( == NULL if creation failed).
*/
@@ -175,7 +174,6 @@ extern GHOST_WindowHandle GHOST_CreateWindow(GHOST_SystemHandle systemhandle,
uint32_t height,
GHOST_TWindowState state,
bool is_dialog,
- GHOST_TDrawingContextType type,
GHOST_GLSettings glSettings);
/**
diff --git a/intern/ghost/GHOST_ISystem.h b/intern/ghost/GHOST_ISystem.h
index 9fb94ed1525..edaeca1e159 100644
--- a/intern/ghost/GHOST_ISystem.h
+++ b/intern/ghost/GHOST_ISystem.h
@@ -118,9 +118,11 @@ class GHOST_ISystem {
/**
* Creates the one and only system.
* \param verbose: report back-ends that were attempted no back-end could be loaded.
+ * \param background: loading the system for background rendering (no visible windows).
* \return An indication of success.
*/
- static GHOST_TSuccess createSystem(bool verbose);
+
+ static GHOST_TSuccess createSystem(bool verbose, bool background);
static GHOST_TSuccess createSystemBackground();
/**
@@ -232,7 +234,6 @@ class GHOST_ISystem {
* \param width: The width the window.
* \param height: The height the window.
* \param state: The state of the window when opened.
- * \param type: The type of drawing context installed in this window.
* \param glSettings: Misc OpenGL settings.
* \param exclusive: Use to show the window on top and ignore others (used full-screen).
* \param is_dialog: Stay on top of parent window, no icon in taskbar, can't be minimized.
@@ -245,7 +246,6 @@ class GHOST_ISystem {
uint32_t width,
uint32_t height,
GHOST_TWindowState state,
- GHOST_TDrawingContextType type,
GHOST_GLSettings glSettings,
const bool exclusive = false,
const bool is_dialog = false,
diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h
index 2645ce448b0..db4eeff3122 100644
--- a/intern/ghost/GHOST_Types.h
+++ b/intern/ghost/GHOST_Types.h
@@ -60,10 +60,6 @@ typedef struct {
int hot_spot[2];
} GHOST_CursorBitmapRef;
-typedef struct {
- int flags;
-} GHOST_GLSettings;
-
typedef enum {
GHOST_glStereoVisual = (1 << 0),
GHOST_glDebugContext = (1 << 1),
@@ -157,6 +153,9 @@ typedef enum {
#ifdef WIN32
GHOST_kDrawingContextTypeD3D,
#endif
+#ifdef __APPLE__
+ GHOST_kDrawingContextTypeMetal,
+#endif
} GHOST_TDrawingContextType;
typedef enum {
@@ -598,6 +597,11 @@ typedef struct {
uint32_t frequency;
} GHOST_DisplaySetting;
+typedef struct {
+ int flags;
+ GHOST_TDrawingContextType context_type;
+} GHOST_GLSettings;
+
typedef enum {
/** Axis that cursor grab will wrap. */
GHOST_kDebugDefault = (1 << 1),
diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp
index 158e979cdf2..0c595b27148 100644
--- a/intern/ghost/intern/GHOST_C-api.cpp
+++ b/intern/ghost/intern/GHOST_C-api.cpp
@@ -24,7 +24,7 @@
GHOST_SystemHandle GHOST_CreateSystem(void)
{
- GHOST_ISystem::createSystem(true);
+ GHOST_ISystem::createSystem(true, false);
GHOST_ISystem *system = GHOST_ISystem::getSystem();
return (GHOST_SystemHandle)system;
@@ -161,7 +161,6 @@ GHOST_WindowHandle GHOST_CreateWindow(GHOST_SystemHandle systemhandle,
uint32_t height,
GHOST_TWindowState state,
bool is_dialog,
- GHOST_TDrawingContextType type,
GHOST_GLSettings glSettings)
{
GHOST_ISystem *system = (GHOST_ISystem *)systemhandle;
@@ -172,7 +171,6 @@ GHOST_WindowHandle GHOST_CreateWindow(GHOST_SystemHandle systemhandle,
width,
height,
state,
- type,
glSettings,
false,
is_dialog,
diff --git a/intern/ghost/intern/GHOST_ContextCGL.h b/intern/ghost/intern/GHOST_ContextCGL.h
index 130b926f25c..d19fffffb43 100644
--- a/intern/ghost/intern/GHOST_ContextCGL.h
+++ b/intern/ghost/intern/GHOST_ContextCGL.h
@@ -30,7 +30,8 @@ class GHOST_ContextCGL : public GHOST_Context {
GHOST_ContextCGL(bool stereoVisual,
NSView *metalView,
CAMetalLayer *metalLayer,
- NSOpenGLView *openglView);
+ NSOpenGLView *openglView,
+ GHOST_TDrawingContextType type);
/**
* Destructor.
diff --git a/intern/ghost/intern/GHOST_ContextCGL.mm b/intern/ghost/intern/GHOST_ContextCGL.mm
index ff53ecdbbba..9dad337a5d6 100644
--- a/intern/ghost/intern/GHOST_ContextCGL.mm
+++ b/intern/ghost/intern/GHOST_ContextCGL.mm
@@ -46,8 +46,10 @@ int GHOST_ContextCGL::s_sharedCount = 0;
GHOST_ContextCGL::GHOST_ContextCGL(bool stereoVisual,
NSView *metalView,
CAMetalLayer *metalLayer,
- NSOpenGLView *openGLView)
+ NSOpenGLView *openGLView,
+ GHOST_TDrawingContextType type)
: GHOST_Context(stereoVisual),
+ m_useMetalForRendering(type == GHOST_kDrawingContextTypeMetal),
m_metalView(metalView),
m_metalLayer(metalLayer),
m_metalCmdQueue(nil),
diff --git a/intern/ghost/intern/GHOST_ContextGLX.cpp b/intern/ghost/intern/GHOST_ContextGLX.cpp
index 93708983f37..d9f2df21ee0 100644
--- a/intern/ghost/intern/GHOST_ContextGLX.cpp
+++ b/intern/ghost/intern/GHOST_ContextGLX.cpp
@@ -140,7 +140,7 @@ GHOST_TSuccess GHOST_ContextGLX::initializeDrawingContext()
/* End Inline GLEW. */
/* -------------------------------------------------------------------- */
#else
- /* Important to initialize only glxew (_not_ GLEW),
+ /* Important to initialize only GLXEW (_not_ GLEW),
* since this breaks w/ Mesa's `swrast`, see: T46431. */
glxewInit();
#endif /* USE_GLXEW_INIT_WORKAROUND */
diff --git a/intern/ghost/intern/GHOST_Debug.h b/intern/ghost/intern/GHOST_Debug.h
index ec1a0b34be6..64eff7f9aed 100644
--- a/intern/ghost/intern/GHOST_Debug.h
+++ b/intern/ghost/intern/GHOST_Debug.h
@@ -15,29 +15,41 @@
# endif
#endif
-#if defined(WITH_GHOST_DEBUG) || (!defined(NDEBUG))
-# include <iostream>
-# include <stdio.h> //for printf()
-#endif // WITH_GHOST_DEBUG
+#include <iostream>
+#include <stdio.h> /* For `printf()`. */
#if defined(WITH_GHOST_DEBUG)
# define GHOST_PRINT(x) \
{ \
std::cout << x; \
} \
- (void)0
+ ((void)0)
# define GHOST_PRINTF(x, ...) \
{ \
printf(x, __VA_ARGS__); \
} \
- (void)0
+ ((void)0)
#else
-# define GHOST_PRINT(x)
-# define GHOST_PRINTF(x, ...)
+/* Expand even when `WITH_GHOST_DEBUG` is disabled to prevent expressions
+ * becoming invalid even when the option is disable. */
+# define GHOST_PRINT(x) \
+ { \
+ if (false) { \
+ std::cout << x; \
+ } \
+ } \
+ ((void)0)
+# define GHOST_PRINTF(x, ...) \
+ { \
+ if (false) { \
+ printf(x, __VA_ARGS__); \
+ } \
+ } \
+ ((void)0)
+
#endif /* `!defined(WITH_GHOST_DEBUG)` */
#ifdef WITH_ASSERT_ABORT
-# include <stdio.h> //for fprintf()
# include <stdlib.h> //for abort()
# define GHOST_ASSERT(x, info) \
{ \
@@ -48,7 +60,7 @@
abort(); \
} \
} \
- (void)0
+ ((void)0)
/* Assert in non-release builds too. */
#elif defined(WITH_GHOST_DEBUG) || (!defined(NDEBUG))
# define GHOST_ASSERT(x, info) \
@@ -59,7 +71,7 @@
GHOST_PRINT("\n"); \
} \
} \
- (void)0
+ ((void)0)
#else /* `defined(WITH_GHOST_DEBUG) || (!defined(NDEBUG))` */
# define GHOST_ASSERT(x, info) ((void)0)
#endif /* `defined(WITH_GHOST_DEBUG) || (!defined(NDEBUG))` */
diff --git a/intern/ghost/intern/GHOST_ISystem.cpp b/intern/ghost/intern/GHOST_ISystem.cpp
index 8e2859ca1e1..696848ce623 100644
--- a/intern/ghost/intern/GHOST_ISystem.cpp
+++ b/intern/ghost/intern/GHOST_ISystem.cpp
@@ -34,7 +34,7 @@ const char *GHOST_ISystem::m_system_backend_id = nullptr;
GHOST_TBacktraceFn GHOST_ISystem::m_backtrace_fn = nullptr;
-GHOST_TSuccess GHOST_ISystem::createSystem(bool verbose)
+GHOST_TSuccess GHOST_ISystem::createSystem(bool verbose, [[maybe_unused]] bool background)
{
/* When GHOST fails to start, report the back-ends that were attempted.
* A Verbose argument could be supported in printing isn't always desired. */
@@ -61,7 +61,7 @@ GHOST_TSuccess GHOST_ISystem::createSystem(bool verbose)
if (has_wayland_libraries) {
backends_attempted[backends_attempted_num++] = "WAYLAND";
try {
- m_system = new GHOST_SystemWayland();
+ m_system = new GHOST_SystemWayland(background);
}
catch (const std::runtime_error &) {
delete m_system;
@@ -99,7 +99,7 @@ GHOST_TSuccess GHOST_ISystem::createSystem(bool verbose)
if (has_wayland_libraries) {
backends_attempted[backends_attempted_num++] = "WAYLAND";
try {
- m_system = new GHOST_SystemWayland();
+ m_system = new GHOST_SystemWayland(background);
}
catch (const std::runtime_error &) {
delete m_system;
@@ -160,7 +160,7 @@ GHOST_TSuccess GHOST_ISystem::createSystemBackground()
if (!m_system) {
#if !defined(WITH_HEADLESS)
/* Try to create a off-screen render surface with the graphical systems. */
- success = createSystem(false);
+ success = createSystem(false, true);
if (success) {
return success;
}
diff --git a/intern/ghost/intern/GHOST_ImeWin32.h b/intern/ghost/intern/GHOST_ImeWin32.h
index 85c8ed7b4bd..cb6d8a770cf 100644
--- a/intern/ghost/intern/GHOST_ImeWin32.h
+++ b/intern/ghost/intern/GHOST_ImeWin32.h
@@ -266,7 +266,7 @@ class GHOST_ImeWin32 {
* Parameters
* * window_handle [in] (HWND)
* Represents the window handle of the caller.
- * * caret_rect [in] (const gfx::Rect&)
+ * * caret_rect [in] (`const gfx::Rect&`)
* Represent the rectangle of the input caret.
* This rectangle is used for controlling the positions of IME windows.
* * complete [in] (bool)
diff --git a/intern/ghost/intern/GHOST_NDOFManager.cpp b/intern/ghost/intern/GHOST_NDOFManager.cpp
index f4c726c7450..5484da82a18 100644
--- a/intern/ghost/intern/GHOST_NDOFManager.cpp
+++ b/intern/ghost/intern/GHOST_NDOFManager.cpp
@@ -12,11 +12,14 @@
#include <climits>
#include <cmath>
-#include <cstdio> /* For error/info reporting. */
#include <cstring> /* For memory functions. */
-/* Printable version of each GHOST_TProgress value. */
-static const char *progress_string[] = {
+/* -------------------------------------------------------------------- */
+/** \name NDOF Enum Strings
+ * \{ */
+
+/* Printable values for #GHOST_TProgress enum (keep aligned). */
+static const char *ndof_progress_string[] = {
"not started",
"starting",
"in progress",
@@ -24,41 +27,30 @@ static const char *progress_string[] = {
"finished",
};
+/* Printable values for #NDOF_ButtonT enum (keep aligned) */
static const char *ndof_button_names[] = {
- /* used internally, never sent */
- "NDOF_BUTTON_NONE",
- /* these two are available from any 3Dconnexion device */
+ /* Exclude `NDOF_BUTTON_NONE` (-1). */
"NDOF_BUTTON_MENU",
"NDOF_BUTTON_FIT",
- /* standard views */
"NDOF_BUTTON_TOP",
"NDOF_BUTTON_BOTTOM",
"NDOF_BUTTON_LEFT",
"NDOF_BUTTON_RIGHT",
"NDOF_BUTTON_FRONT",
"NDOF_BUTTON_BACK",
- /* more views */
"NDOF_BUTTON_ISO1",
"NDOF_BUTTON_ISO2",
- /* 90 degree rotations */
"NDOF_BUTTON_ROLL_CW",
"NDOF_BUTTON_ROLL_CCW",
"NDOF_BUTTON_SPIN_CW",
"NDOF_BUTTON_SPIN_CCW",
"NDOF_BUTTON_TILT_CW",
"NDOF_BUTTON_TILT_CCW",
- /* device control */
"NDOF_BUTTON_ROTATE",
"NDOF_BUTTON_PANZOOM",
"NDOF_BUTTON_DOMINANT",
"NDOF_BUTTON_PLUS",
"NDOF_BUTTON_MINUS",
- /* keyboard emulation */
- "NDOF_BUTTON_ESC",
- "NDOF_BUTTON_ALT",
- "NDOF_BUTTON_SHIFT",
- "NDOF_BUTTON_CTRL",
- /* general-purpose buttons */
"NDOF_BUTTON_1",
"NDOF_BUTTON_2",
"NDOF_BUTTON_3",
@@ -69,18 +61,47 @@ static const char *ndof_button_names[] = {
"NDOF_BUTTON_8",
"NDOF_BUTTON_9",
"NDOF_BUTTON_10",
- /* more general-purpose buttons */
"NDOF_BUTTON_A",
"NDOF_BUTTON_B",
"NDOF_BUTTON_C",
- /* the end */
- "NDOF_BUTTON_LAST",
+ "NDOF_BUTTON_V1",
+ "NDOF_BUTTON_V2",
+ "NDOF_BUTTON_V3",
+ /* Keyboard emulation. */
+ "NDOF_BUTTON_ESC",
+ "NDOF_BUTTON_ENTER",
+ "NDOF_BUTTON_DELETE",
+ "NDOF_BUTTON_TAB",
+ "NDOF_BUTTON_SPACE",
+ "NDOF_BUTTON_ALT",
+ "NDOF_BUTTON_SHIFT",
+ "NDOF_BUTTON_CTRL",
};
+static const char *ndof_device_names[] = {
+ "UnknownDevice",
+ "SpaceNavigator",
+ "SpaceExplorer",
+ "SpacePilotPro",
+ "SpaceMousePro",
+ "SpaceMouseWireless",
+ "SpaceMouseProWireless",
+ "SpaceMouseEnterprise",
+ "SpacePilot",
+ "Spaceball5000",
+ "SpaceTraveler",
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name NDOF Button Maps
+ * \{ */
+
/* Shared by the latest 3Dconnexion hardware
* SpacePilotPro uses all of these
* smaller devices use only some, based on button mask. */
-static const NDOF_ButtonT Modern3Dx_HID_map[] = {
+static const NDOF_ButtonT ndof_HID_map_Modern3Dx[] = {
NDOF_BUTTON_MENU, NDOF_BUTTON_FIT, NDOF_BUTTON_TOP, NDOF_BUTTON_LEFT,
NDOF_BUTTON_RIGHT, NDOF_BUTTON_FRONT, NDOF_BUTTON_BOTTOM, NDOF_BUTTON_BACK,
NDOF_BUTTON_ROLL_CW, NDOF_BUTTON_ROLL_CCW, NDOF_BUTTON_ISO1, NDOF_BUTTON_ISO2,
@@ -90,7 +111,7 @@ static const NDOF_ButtonT Modern3Dx_HID_map[] = {
NDOF_BUTTON_SHIFT, NDOF_BUTTON_CTRL, NDOF_BUTTON_ROTATE, NDOF_BUTTON_PANZOOM,
NDOF_BUTTON_DOMINANT, NDOF_BUTTON_PLUS, NDOF_BUTTON_MINUS};
-static const NDOF_ButtonT SpaceExplorer_HID_map[] = {
+static const NDOF_ButtonT ndof_HID_map_SpaceExplorer[] = {
NDOF_BUTTON_1,
NDOF_BUTTON_2,
NDOF_BUTTON_TOP,
@@ -108,9 +129,8 @@ static const NDOF_ButtonT SpaceExplorer_HID_map[] = {
NDOF_BUTTON_ROTATE,
};
-/* This is the older SpacePilot (sans Pro)
- * thanks to polosson for info about this device. */
-static const NDOF_ButtonT SpacePilot_HID_map[] = {
+/* This is the older SpacePilot (sans Pro). */
+static const NDOF_ButtonT ndof_HID_map_SpacePilot[] = {
NDOF_BUTTON_1, NDOF_BUTTON_2, NDOF_BUTTON_3, NDOF_BUTTON_4,
NDOF_BUTTON_5, NDOF_BUTTON_6, NDOF_BUTTON_TOP, NDOF_BUTTON_LEFT,
NDOF_BUTTON_RIGHT, NDOF_BUTTON_FRONT, NDOF_BUTTON_ESC, NDOF_BUTTON_ALT,
@@ -119,7 +139,7 @@ static const NDOF_ButtonT SpacePilot_HID_map[] = {
NDOF_BUTTON_NONE /* the CONFIG button -- what does it do? */
};
-static const NDOF_ButtonT Generic_HID_map[] = {
+static const NDOF_ButtonT ndof_HID_map_Generic[] = {
NDOF_BUTTON_1,
NDOF_BUTTON_2,
NDOF_BUTTON_3,
@@ -134,27 +154,70 @@ static const NDOF_ButtonT Generic_HID_map[] = {
NDOF_BUTTON_C,
};
-static const int genericButtonCount = ARRAY_SIZE(Generic_HID_map);
+/* Values taken from: https://github.com/FreeSpacenav/spacenavd/wiki/Device-button-names */
+static const NDOF_ButtonT ndof_HID_map_SpaceMouseEnterprise[] = {
+ NDOF_BUTTON_1, /* (0) */
+ NDOF_BUTTON_2, /* (1) */
+ NDOF_BUTTON_3, /* (2) */
+ NDOF_BUTTON_4, /* (3) */
+ NDOF_BUTTON_5, /* (4) */
+ NDOF_BUTTON_6, /* (5) */
+ NDOF_BUTTON_7, /* (6) */
+ NDOF_BUTTON_8, /* (7) */
+ NDOF_BUTTON_9, /* (8) */
+ NDOF_BUTTON_A, /* Labeled "10" (9). */
+ NDOF_BUTTON_B, /* Labeled "11" (10). */
+ NDOF_BUTTON_C, /* Labeled "12" (11). */
+ NDOF_BUTTON_MENU, /* (12). */
+ NDOF_BUTTON_FIT, /* (13). */
+ NDOF_BUTTON_TOP, /* (14). */
+ NDOF_BUTTON_RIGHT, /* (15). */
+ NDOF_BUTTON_FRONT, /* (16). */
+ NDOF_BUTTON_ROLL_CW, /* (17). */
+ NDOF_BUTTON_ESC, /* (18). */
+ NDOF_BUTTON_ALT, /* (19). */
+ NDOF_BUTTON_SHIFT, /* (20). */
+ NDOF_BUTTON_CTRL, /* (21). */
+ NDOF_BUTTON_ROTATE, /* Labeled "Lock Rotate" (22). */
+ NDOF_BUTTON_ENTER, /* Labeled "Enter" (23). */
+ NDOF_BUTTON_DELETE, /* (24). */
+ NDOF_BUTTON_TAB, /* (25). */
+ NDOF_BUTTON_SPACE, /* (26). */
+ NDOF_BUTTON_V1, /* Labeled "V1" (27). */
+ NDOF_BUTTON_V2, /* Labeled "V2" (28). */
+ NDOF_BUTTON_V3, /* Labeled "V3" (29). */
+ NDOF_BUTTON_ISO1, /* Labeled "ISO1" (30). */
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name NDOF Manager Class
+ * \{ */
+
+static const int genericButtonCount = ARRAY_SIZE(ndof_HID_map_Generic);
GHOST_NDOFManager::GHOST_NDOFManager(GHOST_System &sys)
- : m_system(sys),
- m_deviceType(NDOF_UnknownDevice), /* Each platform has its own device detection code. */
- m_buttonCount(genericButtonCount),
- m_buttonMask(0),
- m_hidMap(Generic_HID_map),
- m_buttons(0),
- m_motionTime(0),
- m_prevMotionTime(0),
- m_motionState(GHOST_kNotStarted),
- m_motionEventPending(false),
- m_deadZone(0.0f)
+ : system_(sys),
+ device_type_(NDOF_UnknownDevice), /* Each platform has its own device detection code. */
+ hid_map_button_num_(genericButtonCount),
+ hid_map_button_mask_(0),
+ hid_map_(ndof_HID_map_Generic),
+ button_depressed_(0),
+ motion_time_(0),
+ motion_time_prev_(0),
+ motion_state_(GHOST_kNotStarted),
+ motion_event_pending_(false),
+ motion_dead_zone_(0.0f)
{
/* To avoid the rare situation where one triple is updated and
* the other is not, initialize them both here: */
- memset(m_translation, 0, sizeof(m_translation));
- memset(m_rotation, 0, sizeof(m_rotation));
+ memset(translation_, 0, sizeof(translation_));
+ memset(rotation_, 0, sizeof(rotation_));
}
+/** \} */
+
/* -------------------------------------------------------------------- */
/** \name NDOF Device Setup
* \{ */
@@ -166,16 +229,16 @@ bool GHOST_NDOFManager::setDevice(ushort vendor_id, ushort product_id)
{
/* Call this function until it returns true
* it's a good idea to stop calling it after that, as it will "forget"
- * whichever device it already found */
+ * whichever device it already found. */
/* Default to safe generic behavior for "unknown" devices
* unidentified devices will emit motion events like normal
* rogue buttons do nothing by default, but can be customized by the user. */
- m_deviceType = NDOF_UnknownDevice;
- m_hidMap = Generic_HID_map;
- m_buttonCount = genericButtonCount;
- m_buttonMask = 0;
+ device_type_ = NDOF_UnknownDevice;
+ hid_map_ = ndof_HID_map_Generic;
+ hid_map_button_num_ = genericButtonCount;
+ hid_map_button_mask_ = 0;
/* "mystery device" owners can help build a HID_map for their hardware
* A few users have already contributed information about several older devices
@@ -185,96 +248,102 @@ bool GHOST_NDOFManager::setDevice(ushort vendor_id, ushort product_id)
case 0x046D: /* Logitech (3Dconnexion was a subsidiary). */
switch (product_id) {
/* -- current devices -- */
- case 0xC626: /* full-size SpaceNavigator */
- case 0xC628: /* the "for Notebooks" one */
- puts("ndof: using SpaceNavigator");
- m_deviceType = NDOF_SpaceNavigator;
- m_buttonCount = 2;
- m_hidMap = Modern3Dx_HID_map;
+ case 0xC626: /* Full-size SpaceNavigator. */
+ case 0xC628: /* The "for Notebooks" one. */
+ {
+ device_type_ = NDOF_SpaceNavigator;
+ hid_map_button_num_ = 2;
+ hid_map_ = ndof_HID_map_Modern3Dx;
break;
- case 0xC627:
- puts("ndof: using SpaceExplorer");
- m_deviceType = NDOF_SpaceExplorer;
- m_buttonCount = 15;
- m_hidMap = SpaceExplorer_HID_map;
+ }
+ case 0xC627: {
+ device_type_ = NDOF_SpaceExplorer;
+ hid_map_button_num_ = 15;
+ hid_map_ = ndof_HID_map_SpaceExplorer;
break;
- case 0xC629:
- puts("ndof: using SpacePilot Pro");
- m_deviceType = NDOF_SpacePilotPro;
- m_buttonCount = 31;
- m_hidMap = Modern3Dx_HID_map;
+ }
+ case 0xC629: {
+ device_type_ = NDOF_SpacePilotPro;
+ hid_map_button_num_ = 31;
+ hid_map_ = ndof_HID_map_Modern3Dx;
break;
- case 0xC62B:
- puts("ndof: using SpaceMouse Pro");
- m_deviceType = NDOF_SpaceMousePro;
- m_buttonCount = 27;
- /* ^^ actually has 15 buttons, but their HID codes range from 0 to 26 */
- m_buttonMask = 0x07C0F137;
- m_hidMap = Modern3Dx_HID_map;
+ }
+ case 0xC62B: {
+ device_type_ = NDOF_SpaceMousePro;
+ hid_map_button_num_ = 27; /* 15 physical buttons, but HID codes range from 0 to 26. */
+ hid_map_button_mask_ = 0x07C0F137;
+ hid_map_ = ndof_HID_map_Modern3Dx;
break;
+ }
/* -- older devices -- */
- case 0xC625:
- puts("ndof: using SpacePilot");
- m_deviceType = NDOF_SpacePilot;
- m_buttonCount = 21;
- m_hidMap = SpacePilot_HID_map;
+ case 0xC625: {
+ device_type_ = NDOF_SpacePilot;
+ hid_map_button_num_ = 21;
+ hid_map_ = ndof_HID_map_SpacePilot;
break;
- case 0xC621:
- puts("ndof: using Spaceball 5000");
- m_deviceType = NDOF_Spaceball5000;
- m_buttonCount = 12;
+ }
+ case 0xC621: {
+ device_type_ = NDOF_Spaceball5000;
+ hid_map_button_num_ = 12;
break;
- case 0xC623:
- puts("ndof: using SpaceTraveler");
- m_deviceType = NDOF_SpaceTraveler;
- m_buttonCount = 8;
+ }
+ case 0xC623: {
+ device_type_ = NDOF_SpaceTraveler;
+ hid_map_button_num_ = 8;
break;
-
- default:
- printf("ndof: unknown Logitech product %04hx\n", product_id);
+ }
+ default: {
+ CLOG_INFO(LOG, 2, "unknown Logitech product %04hx", product_id);
+ }
}
break;
- case 0x256F: /* 3Dconnexion */
+ case 0x256F: /* 3Dconnexion. */
switch (product_id) {
case 0xC62E: /* Plugged in. */
case 0xC62F: /* Wireless. */
- puts("ndof: using SpaceMouse Wireless");
- m_deviceType = NDOF_SpaceMouseWireless;
- m_buttonCount = 2;
- m_hidMap = Modern3Dx_HID_map;
+ case 0xC658: /* Wireless (3DConnexion Universal Wireless Receiver in WIN32), see T82412. */
+ {
+ device_type_ = NDOF_SpaceMouseWireless;
+ hid_map_button_num_ = 2;
+ hid_map_ = ndof_HID_map_Modern3Dx;
break;
+ }
case 0xC631: /* Plugged in. */
case 0xC632: /* Wireless. */
- puts("ndof: using SpaceMouse Pro Wireless");
- m_deviceType = NDOF_SpaceMouseProWireless;
- m_buttonCount = 27;
- /* ^^ actually has 15 buttons, but their HID codes range from 0 to 26. */
- m_buttonMask = 0x07C0F137;
- m_hidMap = Modern3Dx_HID_map;
+ {
+ device_type_ = NDOF_SpaceMouseProWireless;
+ hid_map_button_num_ = 27; /* 15 physical buttons, but HID codes range from 0 to 26. */
+ hid_map_button_mask_ = 0x07C0F137;
+ hid_map_ = ndof_HID_map_Modern3Dx;
break;
- case 0xC633:
- puts("ndof: using SpaceMouse Enterprise");
- m_deviceType = NDOF_SpaceMouseEnterprise;
- m_buttonCount = 31;
- m_hidMap = Modern3Dx_HID_map;
+ }
+ case 0xC633: {
+ device_type_ = NDOF_SpaceMouseEnterprise;
+ hid_map_button_num_ = 31;
+ hid_map_ = ndof_HID_map_SpaceMouseEnterprise;
break;
-
- default:
- printf("ndof: unknown 3Dconnexion product %04hx\n", product_id);
+ }
+ default: {
+ CLOG_INFO(LOG, 2, "unknown 3Dconnexion product %04hx", product_id);
+ }
}
break;
default:
- printf("ndof: unknown device %04hx:%04hx\n", vendor_id, product_id);
+ CLOG_INFO(LOG, 2, "unknown device %04hx:%04hx", vendor_id, product_id);
+ }
+
+ if (device_type_ != NDOF_UnknownDevice) {
+ CLOG_INFO(LOG, 2, "using %s", ndof_device_names[device_type_]);
}
- if (m_buttonMask == 0) {
- m_buttonMask = int(~(UINT_MAX << m_buttonCount));
+ if (hid_map_button_mask_ == 0) {
+ hid_map_button_mask_ = int(~(UINT_MAX << hid_map_button_num_));
}
- CLOG_INFO(LOG, 2, "%d buttons -> hex:%X", m_buttonCount, (uint)m_buttonMask);
+ CLOG_INFO(LOG, 2, "%d buttons -> hex:%X", hid_map_button_num_, (uint)hid_map_button_mask_);
- return m_deviceType != NDOF_UnknownDevice;
+ return device_type_ != NDOF_UnknownDevice;
}
#undef LOG
@@ -287,16 +356,16 @@ bool GHOST_NDOFManager::setDevice(ushort vendor_id, ushort product_id)
void GHOST_NDOFManager::updateTranslation(const int t[3], uint64_t time)
{
- memcpy(m_translation, t, sizeof(m_translation));
- m_motionTime = time;
- m_motionEventPending = true;
+ memcpy(translation_, t, sizeof(translation_));
+ motion_time_ = time;
+ motion_event_pending_ = true;
}
void GHOST_NDOFManager::updateRotation(const int r[3], uint64_t time)
{
- memcpy(m_rotation, r, sizeof(m_rotation));
- m_motionTime = time;
- m_motionEventPending = true;
+ memcpy(rotation_, r, sizeof(rotation_));
+ motion_time_ = time;
+ motion_event_pending_ = true;
}
/** \} */
@@ -314,6 +383,18 @@ static GHOST_TKey ghost_map_keyboard_from_ndof_buttom(const NDOF_ButtonT button)
case NDOF_BUTTON_ESC: {
return GHOST_kKeyEsc;
}
+ case NDOF_BUTTON_ENTER: {
+ return GHOST_kKeyEnter;
+ }
+ case NDOF_BUTTON_DELETE: {
+ return GHOST_kKeyDelete;
+ }
+ case NDOF_BUTTON_TAB: {
+ return GHOST_kKeyTab;
+ }
+ case NDOF_BUTTON_SPACE: {
+ return GHOST_kKeySpace;
+ }
case NDOF_BUTTON_ALT: {
return GHOST_kKeyLeftAlt;
}
@@ -334,7 +415,7 @@ void GHOST_NDOFManager::sendButtonEvent(NDOF_ButtonT button,
uint64_t time,
GHOST_IWindow *window)
{
- GHOST_ASSERT(button > NDOF_BUTTON_NONE && button < NDOF_BUTTON_LAST,
+ GHOST_ASSERT(button > NDOF_BUTTON_NONE && button < NDOF_BUTTON_NUM,
"rogue button trying to escape NDOF manager");
GHOST_EventNDOFButton *event = new GHOST_EventNDOFButton(time, window);
@@ -343,7 +424,7 @@ void GHOST_NDOFManager::sendButtonEvent(NDOF_ButtonT button,
data->action = press ? GHOST_kPress : GHOST_kRelease;
data->button = button;
- m_system.pushEvent(event);
+ system_.pushEvent(event);
}
void GHOST_NDOFManager::sendKeyEvent(GHOST_TKey key,
@@ -354,21 +435,21 @@ void GHOST_NDOFManager::sendKeyEvent(GHOST_TKey key,
GHOST_TEventType type = press ? GHOST_kEventKeyDown : GHOST_kEventKeyUp;
GHOST_EventKey *event = new GHOST_EventKey(time, type, window, key, false);
- m_system.pushEvent(event);
+ system_.pushEvent(event);
}
void GHOST_NDOFManager::updateButton(int button_number, bool press, uint64_t time)
{
- if (button_number >= m_buttonCount) {
+ if (button_number >= hid_map_button_num_) {
CLOG_INFO(LOG,
2,
"button=%d, press=%d (out of range %d, ignoring!)",
button_number,
(int)press,
- m_buttonCount);
+ hid_map_button_num_);
return;
}
- const NDOF_ButtonT button = m_hidMap[button_number];
+ const NDOF_ButtonT button = hid_map_[button_number];
if (button == NDOF_BUTTON_NONE) {
CLOG_INFO(
LOG, 2, "button=%d, press=%d (mapped to none, ignoring!)", button_number, (int)press);
@@ -382,7 +463,7 @@ void GHOST_NDOFManager::updateButton(int button_number, bool press, uint64_t tim
(int)press,
ndof_button_names[button]);
- GHOST_IWindow *window = m_system.getWindowManager()->getActiveWindow();
+ GHOST_IWindow *window = system_.getWindowManager()->getActiveWindow();
const GHOST_TKey key = ghost_map_keyboard_from_ndof_buttom(button);
if (key != GHOST_kKeyUnknown) {
sendKeyEvent(key, press, time, window);
@@ -393,20 +474,20 @@ void GHOST_NDOFManager::updateButton(int button_number, bool press, uint64_t tim
int mask = 1 << button_number;
if (press) {
- m_buttons |= mask; /* Set this button's bit. */
+ button_depressed_ |= mask; /* Set this button's bit. */
}
else {
- m_buttons &= ~mask; /* Clear this button's bit. */
+ button_depressed_ &= ~mask; /* Clear this button's bit. */
}
}
void GHOST_NDOFManager::updateButtons(int button_bits, uint64_t time)
{
- button_bits &= m_buttonMask; /* Discard any "garbage" bits. */
+ button_bits &= hid_map_button_mask_; /* Discard any "garbage" bits. */
- int diff = m_buttons ^ button_bits;
+ int diff = button_depressed_ ^ button_bits;
- for (int button_number = 0; button_number < m_buttonCount; ++button_number) {
+ for (int button_number = 0; button_number < hid_map_button_num_; ++button_number) {
int mask = 1 << button_number;
if (diff & mask) {
@@ -433,7 +514,7 @@ void GHOST_NDOFManager::setDeadZone(float dz)
/* Negative values don't make sense, so clamp at zero. */
dz = 0.0f;
}
- m_deadZone = dz;
+ motion_dead_zone_ = dz;
/* Warn the rogue user/developer about high dead-zone, but allow it. */
CLOG_INFO(LOG, 2, "dead zone set to %.2f%s", dz, (dz > 0.5f) ? " (unexpectedly high)" : "");
@@ -458,20 +539,20 @@ static bool nearHomePosition(GHOST_TEventNDOFMotionData *ndof, float threshold)
bool GHOST_NDOFManager::sendMotionEvent()
{
- if (!m_motionEventPending) {
+ if (!motion_event_pending_) {
return false;
}
- m_motionEventPending = false; /* Any pending motion is handled right now. */
+ motion_event_pending_ = false; /* Any pending motion is handled right now. */
- GHOST_IWindow *window = m_system.getWindowManager()->getActiveWindow();
+ GHOST_IWindow *window = system_.getWindowManager()->getActiveWindow();
if (window == nullptr) {
- m_motionState = GHOST_kNotStarted; /* Avoid large `dt` times when changing windows. */
+ motion_state_ = GHOST_kNotStarted; /* Avoid large `dt` times when changing windows. */
return false; /* Delivery will fail, so don't bother sending. */
}
- GHOST_EventNDOFMotion *event = new GHOST_EventNDOFMotion(m_motionTime, window);
+ GHOST_EventNDOFMotion *event = new GHOST_EventNDOFMotion(motion_time_, window);
GHOST_TEventNDOFMotionData *data = (GHOST_TEventNDOFMotionData *)event->getData();
/* Scale axis values here to normalize them to around +/- 1
@@ -479,26 +560,26 @@ bool GHOST_NDOFManager::sendMotionEvent()
const float scale = 1.0f / 350.0f; /* 3Dconnexion devices send +/- 350 usually */
- data->tx = scale * m_translation[0];
- data->ty = scale * m_translation[1];
- data->tz = scale * m_translation[2];
+ data->tx = scale * translation_[0];
+ data->ty = scale * translation_[1];
+ data->tz = scale * translation_[2];
- data->rx = scale * m_rotation[0];
- data->ry = scale * m_rotation[1];
- data->rz = scale * m_rotation[2];
- data->dt = 0.001f * (m_motionTime - m_prevMotionTime); /* In seconds. */
- m_prevMotionTime = m_motionTime;
+ data->rx = scale * rotation_[0];
+ data->ry = scale * rotation_[1];
+ data->rz = scale * rotation_[2];
+ data->dt = 0.001f * (motion_time_ - motion_time_prev_); /* In seconds. */
+ motion_time_prev_ = motion_time_;
- bool weHaveMotion = !nearHomePosition(data, m_deadZone);
+ bool weHaveMotion = !nearHomePosition(data, motion_dead_zone_);
/* Determine what kind of motion event to send `(Starting, InProgress, Finishing)`
* and where that leaves this NDOF manager `(NotStarted, InProgress, Finished)`. */
- switch (m_motionState) {
+ switch (motion_state_) {
case GHOST_kNotStarted:
case GHOST_kFinished: {
if (weHaveMotion) {
data->progress = GHOST_kStarting;
- m_motionState = GHOST_kInProgress;
+ motion_state_ = GHOST_kInProgress;
/* Previous motion time will be ancient, so just make up a reasonable time delta. */
data->dt = 0.0125f;
}
@@ -517,7 +598,7 @@ bool GHOST_NDOFManager::sendMotionEvent()
}
else {
data->progress = GHOST_kFinishing;
- m_motionState = GHOST_kFinished;
+ motion_state_ = GHOST_kFinished;
}
break;
}
@@ -538,21 +619,21 @@ bool GHOST_NDOFManager::sendMotionEvent()
data->ry,
data->rz,
data->dt,
- progress_string[data->progress]);
+ ndof_progress_string[data->progress]);
#else
/* Raw values, may be useful for debugging. */
CLOG_INFO(LOG,
2,
"motion sent, T=(%d,%d,%d) R=(%d,%d,%d) status=%s",
- m_translation[0],
- m_translation[1],
- m_translation[2],
- m_rotation[0],
- m_rotation[1],
- m_rotation[2],
- progress_string[data->progress]);
+ translation_[0],
+ translation_[1],
+ translation_[2],
+ rotation_[0],
+ rotation_[1],
+ rotation_[2],
+ ndof_progress_string[data->progress]);
#endif
- m_system.pushEvent(event);
+ system_.pushEvent(event);
return true;
}
diff --git a/intern/ghost/intern/GHOST_NDOFManager.h b/intern/ghost/intern/GHOST_NDOFManager.h
index 73c1b17f891..2d5bba14aa4 100644
--- a/intern/ghost/intern/GHOST_NDOFManager.h
+++ b/intern/ghost/intern/GHOST_NDOFManager.h
@@ -9,7 +9,7 @@
#include "GHOST_System.h"
typedef enum {
- NDOF_UnknownDevice,
+ NDOF_UnknownDevice = 0,
/* Current devices. */
NDOF_SpaceNavigator,
@@ -29,8 +29,8 @@ typedef enum {
/* NDOF device button event types */
typedef enum {
- /* Used internally, never sent. */
- NDOF_BUTTON_NONE = 0,
+ /* Used internally, never sent or used as an index. */
+ NDOF_BUTTON_NONE = -1,
/* These two are available from any 3Dconnexion device. */
NDOF_BUTTON_MENU,
NDOF_BUTTON_FIT,
@@ -58,11 +58,6 @@ typedef enum {
NDOF_BUTTON_DOMINANT,
NDOF_BUTTON_PLUS,
NDOF_BUTTON_MINUS,
- /* Keyboard emulation. */
- NDOF_BUTTON_ESC,
- NDOF_BUTTON_ALT,
- NDOF_BUTTON_SHIFT,
- NDOF_BUTTON_CTRL,
/* General-purpose buttons.
* Users can assign functions via keymap editor. */
NDOF_BUTTON_1,
@@ -79,8 +74,20 @@ typedef enum {
NDOF_BUTTON_A,
NDOF_BUTTON_B,
NDOF_BUTTON_C,
- /* The end. */
- NDOF_BUTTON_LAST
+ /* Store Views. */
+ NDOF_BUTTON_V1,
+ NDOF_BUTTON_V2,
+ NDOF_BUTTON_V3,
+ /* Keyboard emulation. */
+ NDOF_BUTTON_ESC,
+ NDOF_BUTTON_ENTER,
+ NDOF_BUTTON_DELETE,
+ NDOF_BUTTON_TAB,
+ NDOF_BUTTON_SPACE,
+ NDOF_BUTTON_ALT,
+ NDOF_BUTTON_SHIFT,
+ NDOF_BUTTON_CTRL,
+#define NDOF_BUTTON_NUM (NDOF_BUTTON_CTRL + 1)
} NDOF_ButtonT;
class GHOST_NDOFManager {
@@ -140,25 +147,25 @@ class GHOST_NDOFManager {
bool sendMotionEvent();
protected:
- GHOST_System &m_system;
+ GHOST_System &system_;
private:
void sendButtonEvent(NDOF_ButtonT, bool press, uint64_t time, GHOST_IWindow *);
void sendKeyEvent(GHOST_TKey, bool press, uint64_t time, GHOST_IWindow *);
- NDOF_DeviceT m_deviceType;
- int m_buttonCount;
- int m_buttonMask;
- const NDOF_ButtonT *m_hidMap;
+ NDOF_DeviceT device_type_;
+ int hid_map_button_num_;
+ int hid_map_button_mask_;
+ const NDOF_ButtonT *hid_map_;
- int m_translation[3];
- int m_rotation[3];
- int m_buttons; /* Bit field. */
+ int translation_[3];
+ int rotation_[3];
+ int button_depressed_; /* Bit field. */
- uint64_t m_motionTime; /* In milliseconds. */
- uint64_t m_prevMotionTime; /* Time of most recent motion event sent. */
+ uint64_t motion_time_; /* In milliseconds. */
+ uint64_t motion_time_prev_; /* Time of most recent motion event sent. */
- GHOST_TProgress m_motionState;
- bool m_motionEventPending;
- float m_deadZone; /* Discard motion with each component < this. */
+ GHOST_TProgress motion_state_;
+ bool motion_event_pending_;
+ float motion_dead_zone_; /* Discard motion with each component < this. */
};
diff --git a/intern/ghost/intern/GHOST_NDOFManagerUnix.cpp b/intern/ghost/intern/GHOST_NDOFManagerUnix.cpp
index 94bf0337371..0ccf4dc9bfb 100644
--- a/intern/ghost/intern/GHOST_NDOFManagerUnix.cpp
+++ b/intern/ghost/intern/GHOST_NDOFManagerUnix.cpp
@@ -10,7 +10,7 @@
#define SPNAV_SOCK_PATH "/var/run/spnav.sock"
GHOST_NDOFManagerUnix::GHOST_NDOFManagerUnix(GHOST_System &sys)
- : GHOST_NDOFManager(sys), m_available(false)
+ : GHOST_NDOFManager(sys), available_(false)
{
if (access(SPNAV_SOCK_PATH, F_OK) != 0) {
#ifdef DEBUG
@@ -20,7 +20,7 @@ GHOST_NDOFManagerUnix::GHOST_NDOFManagerUnix(GHOST_System &sys)
#endif
}
else if (spnav_open() != -1) {
- m_available = true;
+ available_ = true;
/* determine exactly which device (if any) is plugged in */
@@ -45,14 +45,14 @@ GHOST_NDOFManagerUnix::GHOST_NDOFManagerUnix(GHOST_System &sys)
GHOST_NDOFManagerUnix::~GHOST_NDOFManagerUnix()
{
- if (m_available) {
+ if (available_) {
spnav_close();
}
}
bool GHOST_NDOFManagerUnix::available()
{
- return m_available;
+ return available_;
}
/*
@@ -74,7 +74,7 @@ bool GHOST_NDOFManagerUnix::processEvents()
{
bool anyProcessed = false;
- if (m_available) {
+ if (available_) {
spnav_event e;
#ifdef USE_FINISH_GLITCH_WORKAROUND
@@ -85,7 +85,7 @@ bool GHOST_NDOFManagerUnix::processEvents()
switch (e.type) {
case SPNAV_EVENT_MOTION: {
/* convert to blender view coords */
- uint64_t now = m_system.getMilliSeconds();
+ uint64_t now = system_.getMilliSeconds();
const int t[3] = {int(e.motion.x), int(e.motion.y), int(-e.motion.z)};
const int r[3] = {int(-e.motion.rx), int(-e.motion.ry), int(e.motion.rz)};
@@ -97,7 +97,7 @@ bool GHOST_NDOFManagerUnix::processEvents()
break;
}
case SPNAV_EVENT_BUTTON:
- uint64_t now = m_system.getMilliSeconds();
+ uint64_t now = system_.getMilliSeconds();
updateButton(e.button.bnum, e.button.press, now);
break;
}
@@ -106,7 +106,7 @@ bool GHOST_NDOFManagerUnix::processEvents()
#ifdef USE_FINISH_GLITCH_WORKAROUND
if (motion_test_prev == true && motion_test == false) {
- uint64_t now = m_system.getMilliSeconds();
+ uint64_t now = system_.getMilliSeconds();
const int v[3] = {0, 0, 0};
updateTranslation(v, now);
diff --git a/intern/ghost/intern/GHOST_NDOFManagerUnix.h b/intern/ghost/intern/GHOST_NDOFManagerUnix.h
index fd603e3cb54..2b98fad974f 100644
--- a/intern/ghost/intern/GHOST_NDOFManagerUnix.h
+++ b/intern/ghost/intern/GHOST_NDOFManagerUnix.h
@@ -15,5 +15,5 @@ class GHOST_NDOFManagerUnix : public GHOST_NDOFManager {
bool processEvents();
private:
- bool m_available;
+ bool available_;
};
diff --git a/intern/ghost/intern/GHOST_System.cpp b/intern/ghost/intern/GHOST_System.cpp
index 94d021fd822..670ede35989 100644
--- a/intern/ghost/intern/GHOST_System.cpp
+++ b/intern/ghost/intern/GHOST_System.cpp
@@ -384,6 +384,7 @@ GHOST_TSuccess GHOST_System::createFullScreenWindow(GHOST_Window **window,
if (stereoVisual) {
glSettings.flags |= GHOST_glStereoVisual;
}
+ glSettings.context_type = GHOST_kDrawingContextTypeOpenGL;
/* NOTE: don't use #getCurrentDisplaySetting() because on X11 we may
* be zoomed in and the desktop may be bigger than the viewport. */
GHOST_ASSERT(m_displayManager,
@@ -395,7 +396,6 @@ GHOST_TSuccess GHOST_System::createFullScreenWindow(GHOST_Window **window,
settings.xPixels,
settings.yPixels,
GHOST_kWindowStateNormal,
- GHOST_kDrawingContextTypeOpenGL,
glSettings,
true /* exclusive */);
return (*window == nullptr) ? GHOST_kFailure : GHOST_kSuccess;
diff --git a/intern/ghost/intern/GHOST_System.h b/intern/ghost/intern/GHOST_System.h
index 810f828a8a1..924a4bff790 100644
--- a/intern/ghost/intern/GHOST_System.h
+++ b/intern/ghost/intern/GHOST_System.h
@@ -76,7 +76,7 @@ class GHOST_System : public GHOST_ISystem {
GHOST_ITimerTask *installTimer(uint64_t delay,
uint64_t interval,
GHOST_TimerProcPtr timerProc,
- GHOST_TUserDataPtr userData = NULL);
+ GHOST_TUserDataPtr userData = nullptr);
/**
* Removes a timer.
diff --git a/intern/ghost/intern/GHOST_SystemCocoa.h b/intern/ghost/intern/GHOST_SystemCocoa.h
index dbb41c7fddf..0211694aad4 100644
--- a/intern/ghost/intern/GHOST_SystemCocoa.h
+++ b/intern/ghost/intern/GHOST_SystemCocoa.h
@@ -77,7 +77,6 @@ class GHOST_SystemCocoa : public GHOST_System {
* \param width: The width the window.
* \param height: The height the window.
* \param state: The state of the window when opened.
- * \param type: The type of drawing context installed in this window.
* \param glSettings: Misc OpenGL settings.
* \param exclusive: Use to show the window on top and ignore others (used full-screen).
* \param parentWindow: Parent (embedder) window.
@@ -89,7 +88,6 @@ class GHOST_SystemCocoa : public GHOST_System {
uint32_t width,
uint32_t height,
GHOST_TWindowState state,
- GHOST_TDrawingContextType type,
GHOST_GLSettings glSettings,
const bool exclusive = false,
const bool is_dialog = false,
diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm
index bfa90114e4c..a1016dd4843 100644
--- a/intern/ghost/intern/GHOST_SystemCocoa.mm
+++ b/intern/ghost/intern/GHOST_SystemCocoa.mm
@@ -689,7 +689,6 @@ GHOST_IWindow *GHOST_SystemCocoa::createWindow(const char *title,
uint32_t width,
uint32_t height,
GHOST_TWindowState state,
- GHOST_TDrawingContextType type,
GHOST_GLSettings glSettings,
const bool exclusive,
const bool is_dialog,
@@ -719,7 +718,7 @@ GHOST_IWindow *GHOST_SystemCocoa::createWindow(const char *title,
width,
height,
state,
- type,
+ glSettings.context_type,
glSettings.flags & GHOST_glStereoVisual,
glSettings.flags & GHOST_glDebugContext,
is_dialog,
@@ -751,7 +750,7 @@ GHOST_IWindow *GHOST_SystemCocoa::createWindow(const char *title,
*/
GHOST_IContext *GHOST_SystemCocoa::createOffscreenContext(GHOST_GLSettings glSettings)
{
- GHOST_Context *context = new GHOST_ContextCGL(false, NULL, NULL, NULL);
+ GHOST_Context *context = new GHOST_ContextCGL(false, NULL, NULL, NULL, glSettings.context_type);
if (context->initializeDrawingContext())
return context;
else
diff --git a/intern/ghost/intern/GHOST_SystemHeadless.h b/intern/ghost/intern/GHOST_SystemHeadless.h
index b02a82fc9eb..66af65f763d 100644
--- a/intern/ghost/intern/GHOST_SystemHeadless.h
+++ b/intern/ghost/intern/GHOST_SystemHeadless.h
@@ -142,7 +142,6 @@ class GHOST_SystemHeadless : public GHOST_System {
uint32_t width,
uint32_t height,
GHOST_TWindowState state,
- GHOST_TDrawingContextType type,
GHOST_GLSettings glSettings,
const bool /*exclusive*/,
const bool /*is_dialog*/,
@@ -155,7 +154,7 @@ class GHOST_SystemHeadless : public GHOST_System {
height,
state,
parentWindow,
- type,
+ glSettings.context_type,
((glSettings.flags & GHOST_glStereoVisual) != 0));
}
diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp
index ad5c4dc85fb..9174664adc4 100644
--- a/intern/ghost/intern/GHOST_SystemSDL.cpp
+++ b/intern/ghost/intern/GHOST_SystemSDL.cpp
@@ -42,7 +42,6 @@ GHOST_IWindow *GHOST_SystemSDL::createWindow(const char *title,
uint32_t width,
uint32_t height,
GHOST_TWindowState state,
- GHOST_TDrawingContextType type,
GHOST_GLSettings glSettings,
const bool exclusive,
const bool /* is_dialog */,
@@ -57,7 +56,7 @@ GHOST_IWindow *GHOST_SystemSDL::createWindow(const char *title,
width,
height,
state,
- type,
+ glSettings.context_type,
((glSettings.flags & GHOST_glStereoVisual) != 0),
exclusive,
parentWindow);
diff --git a/intern/ghost/intern/GHOST_SystemSDL.h b/intern/ghost/intern/GHOST_SystemSDL.h
index bee277ba674..385bfb841e3 100644
--- a/intern/ghost/intern/GHOST_SystemSDL.h
+++ b/intern/ghost/intern/GHOST_SystemSDL.h
@@ -71,7 +71,6 @@ class GHOST_SystemSDL : public GHOST_System {
uint32_t width,
uint32_t height,
GHOST_TWindowState state,
- GHOST_TDrawingContextType type,
GHOST_GLSettings glSettings,
const bool exclusive = false,
const bool is_dialog = false,
diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp
index 04c7e103bde..67270d26ed3 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.cpp
+++ b/intern/ghost/intern/GHOST_SystemWayland.cpp
@@ -10,6 +10,7 @@
#include "GHOST_EventCursor.h"
#include "GHOST_EventDragnDrop.h"
#include "GHOST_EventKey.h"
+#include "GHOST_EventTrackpad.h"
#include "GHOST_EventWheel.h"
#include "GHOST_PathUtils.h"
#include "GHOST_TimerManager.h"
@@ -50,6 +51,8 @@
/* Generated by `wayland-scanner`. */
#include <pointer-constraints-unstable-v1-client-protocol.h>
+#include <pointer-gestures-unstable-v1-client-protocol.h>
+#include <primary-selection-unstable-v1-client-protocol.h>
#include <relative-pointer-unstable-v1-client-protocol.h>
#include <tablet-unstable-v2-client-protocol.h>
#include <xdg-output-unstable-v1-client-protocol.h>
@@ -82,10 +85,23 @@ static void keyboard_handle_key_repeat_cancel(struct GWL_Seat *seat);
static void output_handle_done(void *data, struct wl_output *wl_output);
+static void gwl_seat_capability_pointer_disable(GWL_Seat *seat);
+static void gwl_seat_capability_keyboard_disable(GWL_Seat *seat);
+static void gwl_seat_capability_touch_disable(GWL_Seat *seat);
+
+static bool gwl_registry_entry_remove_by_name(GWL_Display *display,
+ uint32_t name,
+ int *r_interface_slot);
+static void gwl_registry_entry_remove_all(GWL_Display *display);
+
+struct GWL_RegistryHandler;
+static int gwl_registry_handler_interface_slot_max();
+static int gwl_registry_handler_interface_slot_from_string(const char *interface);
+static const struct GWL_RegistryHandler *gwl_registry_handler_from_interface_slot(
+ int interface_slot);
+
/* -------------------------------------------------------------------- */
-/** \name Local Defines
- *
- * Control local functionality, compositors specific workarounds.
+/** \name Workaround Compositor Specific Bugs
* \{ */
/**
@@ -116,11 +132,20 @@ static bool use_gnome_confine_hack = false;
* This define could be removed without changing any functionality,
* it just means GNOME users will see verbose warning messages that alert them about
* a known problem that needs to be fixed up-stream.
+ *
+ * This has been fixed for GNOME 43. Keep the workaround until support for gnome 42 is dropped.
* See: https://gitlab.gnome.org/GNOME/mutter/-/issues/2457
*/
#define USE_GNOME_KEYBOARD_SUPPRESS_WARNING
/**
+ * KDE (plasma 5.26.1) has a bug where the cursor surface needs to be committed
+ * (via `wl_surface_commit`) when it was hidden and is being set to visible again, see: T102048.
+ * See: https://bugs.kde.org/show_bug.cgi?id=461001
+ */
+#define USE_KDE_TABLET_HIDDEN_CURSOR_HACK
+
+/**
* When GNOME is found, require `libdecor`.
* This is a hack because it seems there is no way to check if the compositor supports
* server side decorations when initializing WAYLAND.
@@ -129,6 +154,20 @@ static bool use_gnome_confine_hack = false;
# define USE_GNOME_NEEDS_LIBDECOR_HACK
#endif
+/* -------------------------------------------------------------------- */
+/** \name Local Defines
+ *
+ * Control local functionality, compositors specific workarounds.
+ * \{ */
+
+/**
+ * Fix short-cut part of keyboard reading code not properly handling some keys, see: T102194.
+ * \note This is similar to X11 workaround by the same name, see: T47228.
+ */
+#define USE_NON_LATIN_KB_WORKAROUND
+
+#define WL_NAME_UNSET uint32_t(-1)
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -140,8 +179,7 @@ static bool use_gnome_confine_hack = false;
* \{ */
/**
- * The event codes are used to
- * to differentiate from which mouse button an event comes from.
+ * The event codes are used to differentiate from which mouse button an event comes from.
*/
#define BTN_LEFT 0x110
#define BTN_RIGHT 0x111
@@ -154,9 +192,14 @@ static bool use_gnome_confine_hack = false;
/**
* Tablet events.
+ *
+ * \note Gnome/GTK swap middle/right, where the same application in X11 will swap the middle/right
+ * mouse button when running under WAYLAND. KDE doesn't do this, and according to artists
+ * at the Blender studio, having the button closest to the nib be MMB is preferable,
+ * so use this as a default. If needs be - swapping these could be a preference.
*/
-#define BTN_STYLUS 0x14b /* Use as right-mouse. */
-#define BTN_STYLUS2 0x14c /* Use as middle-mouse. */
+#define BTN_STYLUS 0x14b /* Use as middle-mouse. */
+#define BTN_STYLUS2 0x14c /* Use as right-mouse. */
/* NOTE(@campbellbarton): Map to an additional button (not sure which hardware uses this). */
#define BTN_STYLUS3 0x149
@@ -165,6 +208,19 @@ static bool use_gnome_confine_hack = false;
*/
#define KEY_GRAVE 41
+#ifdef USE_NON_LATIN_KB_WORKAROUND
+# define KEY_1 2
+# define KEY_2 3
+# define KEY_3 4
+# define KEY_4 5
+# define KEY_5 6
+# define KEY_6 7
+# define KEY_7 8
+# define KEY_8 9
+# define KEY_9 10
+# define KEY_0 11
+#endif
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -190,48 +246,76 @@ struct GWL_ModifierInfo {
};
static const GWL_ModifierInfo g_modifier_info_table[MOD_INDEX_NUM] = {
- [MOD_INDEX_SHIFT] =
- {
- .display_name = "Shift",
- .xkb_id = XKB_MOD_NAME_SHIFT,
- .key_l = GHOST_kKeyLeftShift,
- .key_r = GHOST_kKeyRightShift,
- .mod_l = GHOST_kModifierKeyLeftShift,
- .mod_r = GHOST_kModifierKeyRightShift,
- },
- [MOD_INDEX_ALT] =
- {
- .display_name = "Alt",
- .xkb_id = XKB_MOD_NAME_ALT,
- .key_l = GHOST_kKeyLeftAlt,
- .key_r = GHOST_kKeyRightAlt,
- .mod_l = GHOST_kModifierKeyLeftAlt,
- .mod_r = GHOST_kModifierKeyRightAlt,
- },
- [MOD_INDEX_CTRL] =
- {
- .display_name = "Control",
- .xkb_id = XKB_MOD_NAME_CTRL,
- .key_l = GHOST_kKeyLeftControl,
- .key_r = GHOST_kKeyRightControl,
- .mod_l = GHOST_kModifierKeyLeftControl,
- .mod_r = GHOST_kModifierKeyRightControl,
- },
- [MOD_INDEX_OS] =
- {
- .display_name = "OS",
- .xkb_id = XKB_MOD_NAME_LOGO,
- .key_l = GHOST_kKeyLeftOS,
- .key_r = GHOST_kKeyRightOS,
- .mod_l = GHOST_kModifierKeyLeftOS,
- .mod_r = GHOST_kModifierKeyRightOS,
- },
+ /* MOD_INDEX_SHIFT */
+ {
+ /* display_name */ "Shift",
+ /* xkb_id */ XKB_MOD_NAME_SHIFT,
+ /* key_l */ GHOST_kKeyLeftShift,
+ /* key_r */ GHOST_kKeyRightShift,
+ /* mod_l */ GHOST_kModifierKeyLeftShift,
+ /* mod_r */ GHOST_kModifierKeyRightShift,
+ },
+ /* MOD_INDEX_ALT */
+ {
+ /* display_name */ "Alt",
+ /* xkb_id */ XKB_MOD_NAME_ALT,
+ /* key_l */ GHOST_kKeyLeftAlt,
+ /* key_r */ GHOST_kKeyRightAlt,
+ /* mod_l */ GHOST_kModifierKeyLeftAlt,
+ /* mod_r */ GHOST_kModifierKeyRightAlt,
+ },
+ /* MOD_INDEX_CTRL */
+ {
+ /* display_name */ "Control",
+ /* xkb_id */ XKB_MOD_NAME_CTRL,
+ /* key_l */ GHOST_kKeyLeftControl,
+ /* key_r */ GHOST_kKeyRightControl,
+ /* mod_l */ GHOST_kModifierKeyLeftControl,
+ /* mod_r */ GHOST_kModifierKeyRightControl,
+ },
+ /* MOD_INDEX_OS */
+ {
+ /* display_name */ "OS",
+ /* xkb_id */ XKB_MOD_NAME_LOGO,
+ /* key_l */ GHOST_kKeyLeftOS,
+ /* key_r */ GHOST_kKeyRightOS,
+ /* mod_l */ GHOST_kModifierKeyLeftOS,
+ /* mod_r */ GHOST_kModifierKeyRightOS,
+ },
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Internal #GWL_SimpleBuffer Type
+ * \{ */
+
+struct GWL_SimpleBuffer {
+ /** Constant data, but may be freed. */
+ const char *data = nullptr;
+ size_t data_size = 0;
};
+static void gwl_simple_buffer_free_data(GWL_SimpleBuffer *buffer)
+{
+ free(const_cast<char *>(buffer->data));
+ buffer->data = nullptr;
+ buffer->data_size = 0;
+}
+
+static void gwl_simple_buffer_set_from_string(GWL_SimpleBuffer *buffer, const char *str)
+{
+ free(const_cast<char *>(buffer->data));
+ buffer->data_size = strlen(str);
+ char *data = static_cast<char *>(malloc(buffer->data_size));
+ std::memcpy(data, str, buffer->data_size);
+ buffer->data = data;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Private Types & Defines
+/** \name Internal #GWL_Cursor Type
* \{ */
/**
@@ -249,16 +333,26 @@ struct GWL_Cursor {
* the hardware cursor is used.
*/
bool is_hardware = true;
+ /** When true, a custom image is used to display the cursor (stored in `wl_image`). */
bool is_custom = false;
- struct wl_surface *wl_surface = nullptr;
+ struct wl_surface *wl_surface_cursor = nullptr;
struct wl_buffer *wl_buffer = nullptr;
struct wl_cursor_image wl_image = {0};
struct wl_cursor_theme *wl_theme = nullptr;
void *custom_data = nullptr;
+ /** The size of `custom_data` in bytes. */
size_t custom_data_size = 0;
- int size = 0;
+ /**
+ * The name of the theme (loaded by DBUS, depends on #WITH_GHOST_WAYLAND_DBUS).
+ * When disabled, leave as an empty string and the default theme will be used.
+ */
std::string theme_name;
-
+ /**
+ * The size of the cursor (when looking up a cursor theme).
+ * This must be scaled by the maximum output scale when passing to wl_cursor_theme_load.
+ * See #update_cursor_scale.
+ * */
+ int theme_size = 0;
int custom_scale = 1;
};
@@ -269,6 +363,7 @@ struct GWL_Cursor {
*/
struct GWL_TabletTool {
struct GWL_Seat *seat = nullptr;
+ /** Tablets have a separate cursor to the 'pointer', this surface is used for cursor drawing. */
struct wl_surface *wl_surface_cursor = nullptr;
/** Used to delay clearing tablet focused wl_surface until the frame is handled. */
bool proximity = false;
@@ -276,23 +371,55 @@ struct GWL_TabletTool {
GHOST_TabletData data = GHOST_TABLET_DATA_NONE;
};
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Internal #GWL_DataOffer Type
+ * \{ */
+
+/**
+ * Data storage used for clipboard paste & drag-and-drop.
+ */
struct GWL_DataOffer {
- std::unordered_set<std::string> types;
- uint32_t source_actions = 0;
- uint32_t dnd_action = 0;
struct wl_data_offer *id = nullptr;
- std::atomic<bool> in_use = false;
+ std::unordered_set<std::string> types;
+
struct {
+ /**
+ * Prevents freeing after #wl_data_device_listener.leave,
+ * before #wl_data_device_listener.drop.
+ */
+ bool in_use = false;
+ /**
+ * Bit-mask with available drop options.
+ * #WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY, #WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE.. etc.
+ * The application that initializes the drag may set these depending on modifiers held
+ * \note when dragging begins. Currently ghost doesn't make use of these.
+ */
+ enum wl_data_device_manager_dnd_action source_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
+ enum wl_data_device_manager_dnd_action action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
/** Compatible with #GWL_Seat.xy coordinates. */
wl_fixed_t xy[2] = {0, 0};
} dnd;
};
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Internal #GWL_DataSource Type
+ * \{ */
+
struct GWL_DataSource {
- struct wl_data_source *data_source = nullptr;
- char *buffer_out = nullptr;
+ struct wl_data_source *wl_source = nullptr;
+ GWL_SimpleBuffer buffer_out;
};
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Internal #GWL_Seat Type (#wl_seat wrapper & associated types)
+ * \{ */
+
/**
* Data used to implement client-side key-repeat.
*
@@ -352,12 +479,58 @@ struct GWL_SeatStatePointer {
* The wl_surface last used with this pointing device
* (events with this pointing device will be sent here).
*/
- struct wl_surface *wl_surface = nullptr;
+ struct wl_surface *wl_surface_window = nullptr;
GHOST_Buttons buttons = GHOST_Buttons();
};
/**
+ * Scroll state, applying to pointer (not tablet) events.
+ * Otherwise this would be part of #GWL_SeatStatePointer.
+ */
+struct GWL_SeatStatePointerScroll {
+ /** Smooth scrolling (handled & reset with pointer "frame" callback). */
+ wl_fixed_t smooth_xy[2] = {0, 0};
+ /** Discrete scrolling (handled & reset with pointer "frame" callback). */
+ int32_t discrete_xy[2] = {0, 0};
+ /** The source of scroll event. */
+ enum wl_pointer_axis_source axis_source = WL_POINTER_AXIS_SOURCE_WHEEL;
+};
+
+/**
+ * Utility struct to access rounded values from a scaled `wl_fixed_t`,
+ * without loosing information.
+ *
+ * As the rounded result is rounded to a lower precision integer,
+ * the high precision value is accumulated and converted to an integer to
+ * prevent the accumulation of rounded values giving an inaccurate result.
+ *
+ * \note This is simple but doesn't read well when expanded multiple times inline.
+ */
+struct GWL_ScaledFixedT {
+ wl_fixed_t value = 0;
+ wl_fixed_t factor = 1;
+};
+
+static int gwl_scaled_fixed_t_add_and_calc_rounded_delta(GWL_ScaledFixedT *sf,
+ const wl_fixed_t add)
+{
+ const int result_prev = wl_fixed_to_int(sf->value * sf->factor);
+ sf->value += add;
+ const int result_curr = wl_fixed_to_int(sf->value * sf->factor);
+ return result_curr - result_prev;
+}
+
+/**
+ * Gesture state.
+ * This is needed so the gesture values can be converted to deltas.
+ */
+struct GWL_SeatStatePointerGesture_Pinch {
+ GWL_ScaledFixedT scale;
+ GWL_ScaledFixedT rotation;
+};
+
+/**
* State of the keyboard (in #GWL_Seat).
*/
struct GWL_SeatStateKeyboard {
@@ -368,7 +541,7 @@ struct GWL_SeatStateKeyboard {
* The wl_surface last used with this pointing device
* (events with this pointing device will be sent here).
*/
- struct wl_surface *wl_surface = nullptr;
+ struct wl_surface *wl_surface_window = nullptr;
};
/**
@@ -376,48 +549,110 @@ struct GWL_SeatStateKeyboard {
*
* Needed as #GWL_Seat.xkb_state doesn't store which modifier keys are held.
*/
-struct WGL_KeyboardDepressedState {
+struct GWL_KeyboardDepressedState {
int16_t mods[GHOST_KEY_MODIFIER_NUM] = {0};
};
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
-struct WGL_LibDecor_System {
+struct GWL_LibDecor_System {
struct libdecor *context = nullptr;
};
-static void wgl_libdecor_system_destroy(WGL_LibDecor_System *decor)
+static void gwl_libdecor_system_destroy(GWL_LibDecor_System *decor)
{
if (decor->context) {
libdecor_unref(decor->context);
+ decor->context = nullptr;
}
delete decor;
}
#endif
-struct WGL_XDG_Decor_System {
+struct GWL_XDG_Decor_System {
struct xdg_wm_base *shell = nullptr;
+ uint32_t shell_name = WL_NAME_UNSET;
+
struct zxdg_decoration_manager_v1 *manager = nullptr;
+ uint32_t manager_name = WL_NAME_UNSET;
};
-static void wgl_xdg_decor_system_destroy(WGL_XDG_Decor_System *decor)
+static void gwl_xdg_decor_system_destroy(struct GWL_Display *display, GWL_XDG_Decor_System *decor)
{
if (decor->manager) {
- zxdg_decoration_manager_v1_destroy(decor->manager);
+ gwl_registry_entry_remove_by_name(display, decor->manager_name, nullptr);
+ GHOST_ASSERT(decor->manager == nullptr, "Internal registry error");
}
if (decor->shell) {
- xdg_wm_base_destroy(decor->shell);
+ gwl_registry_entry_remove_by_name(display, decor->shell_name, nullptr);
+ GHOST_ASSERT(decor->shell == nullptr, "Internal registry error");
}
delete decor;
}
+struct GWL_PrimarySelection_DataOffer {
+ struct zwp_primary_selection_offer_v1 *id = nullptr;
+
+ std::unordered_set<std::string> types;
+};
+
+struct GWL_PrimarySelection_DataSource {
+ struct zwp_primary_selection_source_v1 *wp_source = nullptr;
+ GWL_SimpleBuffer buffer_out;
+};
+
+/** Primary selection support. */
+struct GWL_PrimarySelection {
+
+ GWL_PrimarySelection_DataSource *data_source = nullptr;
+ std::mutex data_source_mutex;
+
+ GWL_PrimarySelection_DataOffer *data_offer = nullptr;
+ std::mutex data_offer_mutex;
+};
+
+static void gwl_primary_selection_discard_offer(GWL_PrimarySelection *primary)
+{
+ if (primary->data_offer == nullptr) {
+ return;
+ }
+ zwp_primary_selection_offer_v1_destroy(primary->data_offer->id);
+ delete primary->data_offer;
+ primary->data_offer = nullptr;
+}
+
+static void gwl_primary_selection_discard_source(GWL_PrimarySelection *primary)
+{
+ GWL_PrimarySelection_DataSource *data_source = primary->data_source;
+ if (data_source == nullptr) {
+ return;
+ }
+ gwl_simple_buffer_free_data(&data_source->buffer_out);
+ if (data_source->wp_source) {
+ zwp_primary_selection_source_v1_destroy(data_source->wp_source);
+ }
+ delete primary->data_source;
+ primary->data_source = nullptr;
+}
+
struct GWL_Seat {
GHOST_SystemWayland *system = nullptr;
std::string name;
struct wl_seat *wl_seat = nullptr;
struct wl_pointer *wl_pointer = nullptr;
+ struct wl_touch *wl_touch = nullptr;
struct wl_keyboard *wl_keyboard = nullptr;
- struct zwp_tablet_seat_v2 *tablet_seat = nullptr;
+ struct zwp_tablet_seat_v2 *wp_tablet_seat = nullptr;
+
+#ifdef ZWP_POINTER_GESTURE_HOLD_V1_INTERFACE
+ struct zwp_pointer_gesture_hold_v1 *wp_pointer_gesture_hold = nullptr;
+#endif
+#ifdef ZWP_POINTER_GESTURE_PINCH_V1_INTERFACE
+ struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch = nullptr;
+#endif
+#ifdef ZWP_POINTER_GESTURE_SWIPE_V1_INTERFACE
+ struct zwp_pointer_gesture_swipe_v1 *wp_pointer_gesture_swipe = nullptr;
+#endif
/** All currently active tablet tools (needed for changing the cursor). */
std::unordered_set<zwp_tablet_tool_v2 *> tablet_tools;
@@ -426,6 +661,8 @@ struct GWL_Seat {
uint32_t cursor_source_serial = 0;
GWL_SeatStatePointer pointer;
+ GWL_SeatStatePointerScroll pointer_scroll;
+ GWL_SeatStatePointerGesture_Pinch pointer_gesture_pinch;
/** Mostly this can be interchanged with `pointer` however it can't be locked/confined. */
GWL_SeatStatePointer tablet;
@@ -440,9 +677,9 @@ struct GWL_Seat {
struct GWL_Cursor cursor;
- struct zwp_relative_pointer_v1 *relative_pointer = nullptr;
- struct zwp_locked_pointer_v1 *locked_pointer = nullptr;
- struct zwp_confined_pointer_v1 *confined_pointer = nullptr;
+ struct zwp_relative_pointer_v1 *wp_relative_pointer = nullptr;
+ struct zwp_locked_pointer_v1 *wp_locked_pointer = nullptr;
+ struct zwp_confined_pointer_v1 *wp_confined_pointer = nullptr;
struct xkb_context *xkb_context = nullptr;
@@ -451,14 +688,24 @@ struct GWL_Seat {
* Keep a state with no modifiers active, use for symbol lookups.
*/
struct xkb_state *xkb_state_empty = nullptr;
+
+ /**
+ * Keep a state with shift enabled, use to access predictable number access for AZERTY keymaps.
+ * If shift is not supported by the key-map, this is set to NULL.
+ */
+ struct xkb_state *xkb_state_empty_with_shift = nullptr;
/**
* Keep a state with number-lock enabled, use to access predictable key-pad symbols.
* If number-lock is not supported by the key-map, this is set to NULL.
*/
struct xkb_state *xkb_state_empty_with_numlock = nullptr;
+#ifdef USE_NON_LATIN_KB_WORKAROUND
+ bool xkb_use_non_latin_workaround = false;
+#endif
+
/** Keys held matching `xkb_state`. */
- struct WGL_KeyboardDepressedState key_depressed;
+ struct GWL_KeyboardDepressedState key_depressed;
#ifdef USE_GNOME_KEYBOARD_SUPPRESS_WARNING
struct {
@@ -483,9 +730,9 @@ struct GWL_Seat {
GHOST_ITimerTask *timer = nullptr;
} key_repeat;
- struct wl_surface *wl_surface_focus_dnd = nullptr;
+ struct wl_surface *wl_surface_window_focus_dnd = nullptr;
- struct wl_data_device *data_device = nullptr;
+ struct wl_data_device *wl_data_device = nullptr;
/** Drag & Drop. */
struct GWL_DataOffer *data_offer_dnd = nullptr;
std::mutex data_offer_dnd_mutex;
@@ -497,209 +744,434 @@ struct GWL_Seat {
struct GWL_DataSource *data_source = nullptr;
std::mutex data_source_mutex;
+ struct zwp_primary_selection_device_v1 *wp_primary_selection_device = nullptr;
+ struct GWL_PrimarySelection primary_selection;
+
/** Last device that was active. */
uint32_t data_source_serial = 0;
};
+static GWL_SeatStatePointer *gwl_seat_state_pointer_active(GWL_Seat *seat)
+{
+ if (seat->pointer.serial == seat->cursor_source_serial) {
+ return &seat->pointer;
+ }
+ if (seat->tablet.serial == seat->cursor_source_serial) {
+ return &seat->tablet;
+ }
+ return nullptr;
+}
+
+static GWL_SeatStatePointer *gwl_seat_state_pointer_from_cursor_surface(
+ GWL_Seat *seat, const wl_surface *wl_surface)
+{
+ if (ghost_wl_surface_own_cursor_pointer(wl_surface)) {
+ return &seat->pointer;
+ }
+ if (ghost_wl_surface_own_cursor_tablet(wl_surface)) {
+ return &seat->tablet;
+ }
+ GHOST_ASSERT(0, "Surface found without pointer/tablet tag");
+ return nullptr;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Internal #GWL_Display Type (#wl_display & #wl_compositor wrapper)
+ * \{ */
+
+struct GWL_RegistryEntry;
+
struct GWL_Display {
GHOST_SystemWayland *system = nullptr;
+ /**
+ * True when initializing registration, while updating all other entries wont cause problems,
+ * it will preform many redundant update calls.
+ */
+ bool registry_skip_update_all = false;
+
+ /** Registry entries, kept to allow updating & removal at run-time. */
+ struct GWL_RegistryEntry *registry_entry = nullptr;
+
+ struct wl_registry *wl_registry = nullptr;
struct wl_display *wl_display = nullptr;
struct wl_compositor *wl_compositor = nullptr;
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
- WGL_LibDecor_System *libdecor = nullptr;
+ GWL_LibDecor_System *libdecor = nullptr;
bool libdecor_required = false;
#endif
- WGL_XDG_Decor_System *xdg_decor = nullptr;
+ GWL_XDG_Decor_System *xdg_decor = nullptr;
struct zxdg_output_manager_v1 *xdg_output_manager = nullptr;
struct wl_shm *wl_shm = nullptr;
std::vector<GWL_Output *> outputs;
std::vector<GWL_Seat *> seats;
+ /**
+ * Support a single active seat at once, this isn't an exact or correct mapping from WAYLAND.
+ * Only allow input from different seats, not full concurrent multi-seat support.
+ *
+ * The main purpose of having an active seat is an alternative from always using the first
+ * seat which prevents events from any other seat.
+ *
+ * NOTE(@campbellbarton): This could be extended and developed further extended to support
+ * an active seat per window (for e.g.), basic support is sufficient for now as currently isn't
+ * a widely used feature.
+ */
+ int seats_active_index = 0;
- struct wl_data_device_manager *data_device_manager = nullptr;
- struct zwp_tablet_manager_v2 *tablet_manager = nullptr;
- struct zwp_relative_pointer_manager_v1 *relative_pointer_manager = nullptr;
- struct zwp_pointer_constraints_v1 *pointer_constraints = nullptr;
-};
-
-#undef LOG
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Private Utility Functions
- * \{ */
-
-static GHOST_WindowManager *window_manager = nullptr;
+ /* Managers. */
+ struct wl_data_device_manager *wl_data_device_manager = nullptr;
+ struct zwp_tablet_manager_v2 *wp_tablet_manager = nullptr;
+ struct zwp_relative_pointer_manager_v1 *wp_relative_pointer_manager = nullptr;
+ struct zwp_primary_selection_device_manager_v1 *wp_primary_selection_device_manager = nullptr;
-/** Check this lock before accessing #GHOST_SystemWayland::clipboard_ from a thread. */
-static std::mutex system_clipboard_mutex;
+ struct zwp_pointer_constraints_v1 *wp_pointer_constraints = nullptr;
+ struct zwp_pointer_gestures_v1 *wp_pointer_gestures = nullptr;
+};
/**
- * Callback for WAYLAND to run when there is an error.
+ * Free the #GWL_Display and it's related members.
*
- * \note It's useful to set a break-point on this function as some errors are fatal
- * (for all intents and purposes) but don't crash the process.
+ * \note This may run on a partially initialized struct,
+ * so it can't be assumed all members are set.
*/
-static void ghost_wayland_log_handler(const char *msg, va_list arg)
+static void gwl_display_destroy(GWL_Display *display)
{
- fprintf(stderr, "GHOST/Wayland: ");
- vfprintf(stderr, msg, arg); /* Includes newline. */
+ /* For typical WAYLAND use this will always be set.
+ * However when WAYLAND isn't running, this will early-exit and be null. */
+ if (display->wl_registry) {
+ wl_registry_destroy(display->wl_registry);
+ display->wl_registry = nullptr;
+ }
- GHOST_TBacktraceFn backtrace_fn = GHOST_ISystem::getBacktraceFn();
- if (backtrace_fn) {
- backtrace_fn(stderr); /* Includes newline. */
+ /* Unregister items in reverse order. */
+ gwl_registry_entry_remove_all(display);
+
+#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+ if (use_libdecor) {
+ if (display->libdecor) {
+ gwl_libdecor_system_destroy(display->libdecor);
+ display->libdecor = nullptr;
+ }
+ }
+ else
+#endif
+ {
+ if (display->xdg_decor) {
+ gwl_xdg_decor_system_destroy(display, display->xdg_decor);
+ display->xdg_decor = nullptr;
+ }
}
-}
-static GWL_SeatStatePointer *seat_state_pointer_active(GWL_Seat *seat)
-{
- if (seat->pointer.serial == seat->cursor_source_serial) {
- return &seat->pointer;
+ if (eglGetDisplay) {
+ ::eglTerminate(eglGetDisplay(EGLNativeDisplayType(display->wl_display)));
}
- if (seat->tablet.serial == seat->cursor_source_serial) {
- return &seat->tablet;
+
+ if (display->wl_display) {
+ wl_display_disconnect(display->wl_display);
}
- return nullptr;
+
+ delete display;
}
-static GWL_SeatStatePointer *seat_state_pointer_from_cursor_surface(GWL_Seat *seat,
- const wl_surface *wl_surface)
+static int gwl_display_seat_index(GWL_Display *display, const GWL_Seat *seat)
{
- if (ghost_wl_surface_own_cursor_pointer(wl_surface)) {
- return &seat->pointer;
- }
- if (ghost_wl_surface_own_cursor_tablet(wl_surface)) {
- return &seat->tablet;
- }
- GHOST_ASSERT(0, "Surface found without pointer/tablet tag");
- return nullptr;
+ std::vector<GWL_Seat *>::iterator iter = std::find(
+ display->seats.begin(), display->seats.end(), seat);
+ const int index = (iter != display->seats.cend()) ? std::distance(display->seats.begin(), iter) :
+ -1;
+ GHOST_ASSERT(index != -1, "invalid internal state");
+ return index;
}
-static void display_destroy(GWL_Display *display)
+static GWL_Seat *gwl_display_seat_active_get(const GWL_Display *display)
{
- if (display->data_device_manager) {
- wl_data_device_manager_destroy(display->data_device_manager);
+ if (UNLIKELY(display->seats.empty())) {
+ return nullptr;
}
+ return display->seats[display->seats_active_index];
+}
- if (display->tablet_manager) {
- zwp_tablet_manager_v2_destroy(display->tablet_manager);
+static bool gwl_display_seat_active_set(GWL_Display *display, const GWL_Seat *seat)
+{
+ if (UNLIKELY(display->seats.empty())) {
+ return false;
}
-
- for (GWL_Output *output : display->outputs) {
- wl_output_destroy(output->wl_output);
- delete output;
+ const int index = gwl_display_seat_index(display, seat);
+ if (index == display->seats_active_index) {
+ return false;
}
+ display->seats_active_index = index;
+ return true;
+}
- for (GWL_Seat *seat : display->seats) {
+/** \} */
- /* First handle members that require locking.
- * While highly unlikely, it's possible they are being used while this function runs. */
- {
- std::lock_guard lock{seat->data_source_mutex};
- if (seat->data_source) {
- free(seat->data_source->buffer_out);
- if (seat->data_source->data_source) {
- wl_data_source_destroy(seat->data_source->data_source);
- }
- delete seat->data_source;
- }
- }
+/* -------------------------------------------------------------------- */
+/** \name Internal #GWL_RegistryHandler
+ * \{ */
- {
- std::lock_guard lock{seat->data_offer_dnd_mutex};
- if (seat->data_offer_dnd) {
- wl_data_offer_destroy(seat->data_offer_dnd->id);
- delete seat->data_offer_dnd;
- }
- }
+struct GWL_RegisteryAdd_Params {
+ uint32_t name = 0;
+ /** Index within `gwl_registry_handlers`. */
+ int interface_slot = 0;
+ uint32_t version = 0;
+};
- {
- std::lock_guard lock{seat->data_offer_copy_paste_mutex};
- if (seat->data_offer_copy_paste) {
- wl_data_offer_destroy(seat->data_offer_copy_paste->id);
- delete seat->data_offer_copy_paste;
- }
- }
+/**
+ * Add callback for object registry.
+ * \note Any operations that depend on other interfaces being registered must be performed in the
+ * #GWL_RegistryHandler_UpdateFn callback as the order interfaces are added is out of our control.
+ *
+ * \param display: The display which holes a reference to the global object.
+ * \param params: Various arguments needed for registration.
+ */
+using GWL_RegistryHandler_AddFn = void (*)(GWL_Display *display,
+ const GWL_RegisteryAdd_Params *params);
- if (seat->data_device) {
- wl_data_device_release(seat->data_device);
- }
+struct GWL_RegisteryUpdate_Params {
+ uint32_t name = 0;
+ /** Index within `gwl_registry_handlers`. */
+ int interface_slot = 0;
+ uint32_t version = 0;
- if (seat->cursor.custom_data) {
- munmap(seat->cursor.custom_data, seat->cursor.custom_data_size);
- }
+ /** Set to #GWL_RegistryEntry.user_data. */
+ void *user_data = nullptr;
+};
- if (seat->wl_pointer) {
- if (seat->cursor.wl_surface) {
- wl_surface_destroy(seat->cursor.wl_surface);
- }
- if (seat->cursor.wl_theme) {
- wl_cursor_theme_destroy(seat->cursor.wl_theme);
- }
- if (seat->wl_pointer) {
- wl_pointer_destroy(seat->wl_pointer);
- }
- }
- if (seat->wl_keyboard) {
- if (seat->key_repeat.timer) {
- keyboard_handle_key_repeat_cancel(seat);
- }
- wl_keyboard_destroy(seat->wl_keyboard);
- }
+/**
+ * Optional update callback to refresh internal data when another interface has been added/removed.
+ *
+ * \param display: The display which holes a reference to the global object.
+ * \param params: Various arguments needed for updating.
+ */
+using GWL_RegistryHandler_UpdateFn = void (*)(GWL_Display *display,
+ const GWL_RegisteryUpdate_Params *params);
- /* Un-referencing checks for NULL case. */
- xkb_state_unref(seat->xkb_state);
- xkb_state_unref(seat->xkb_state_empty);
- xkb_state_unref(seat->xkb_state_empty_with_numlock);
+/**
+ * Remove callback for object registry.
+ * \param display: The display which holes a reference to the global object.
+ * \param user_data: Optional reference to a sub element of `display`,
+ * use for outputs or seats for e.g. when the display may hold multiple references.
+ * \param on_exit: Enabled when freeing on exit.
+ * When true the consistency of references between objects should be kept valid.
+ * Otherwise it can be assumed that all objects will be freed and none will be used again,
+ * so there is no need to ensure a valid state.
+ */
+using GWL_RegistryEntry_RemoveFn = void (*)(GWL_Display *display, void *user_data, bool on_exit);
+
+struct GWL_RegistryHandler {
+ /** Pointer to the name (not the name it's self), needed as the values aren't set on startup. */
+ const char *const *interface_p = nullptr;
+
+ /** Add the interface. */
+ GWL_RegistryHandler_AddFn add_fn = nullptr;
+ /** Optional update the interface (when other interfaces have been added/removed). */
+ GWL_RegistryHandler_UpdateFn update_fn = nullptr;
+ /** Remove the interface. */
+ GWL_RegistryEntry_RemoveFn remove_fn = nullptr;
+};
- xkb_context_unref(seat->xkb_context);
+/** \} */
- wl_seat_destroy(seat->wl_seat);
- delete seat;
- }
+/* -------------------------------------------------------------------- */
+/** \name Internal #GWL_RegistryEntry
+ * \{ */
- if (display->wl_shm) {
- wl_shm_destroy(display->wl_shm);
- }
+/**
+ * Registered global objects can be removed by the compositor,
+ * these entries are a registry of objects and callbacks to properly remove them.
+ * These are also used to remove all registered objects before exiting.
+ */
+struct GWL_RegistryEntry {
+ GWL_RegistryEntry *next = nullptr;
+ /**
+ * Optional pointer passed to `remove_fn`, typically the container in #GWL_Display
+ * in cases multiple instances of the same interface are supported.
+ */
+ void *user_data = nullptr;
+ /**
+ * A unique identifier used as a handle by `wl_registry_listener.global_remove`.
+ */
+ uint32_t name = WL_NAME_UNSET;
+ /**
+ * Version passed by the add callback.
+ */
+ uint32_t version;
+ /**
+ * The index in `gwl_registry_handlers`,
+ * useful for accessing the interface name (for logging for example).
+ */
+ int interface_slot = 0;
+};
- if (display->relative_pointer_manager) {
- zwp_relative_pointer_manager_v1_destroy(display->relative_pointer_manager);
- }
+static void gwl_registry_entry_add(GWL_Display *display,
+ const GWL_RegisteryAdd_Params *params,
+ void *user_data)
+{
+ GWL_RegistryEntry *reg = new GWL_RegistryEntry;
- if (display->pointer_constraints) {
- zwp_pointer_constraints_v1_destroy(display->pointer_constraints);
- }
+ reg->interface_slot = params->interface_slot;
+ reg->name = params->name;
+ reg->version = params->version;
+ reg->user_data = user_data;
+
+ reg->next = display->registry_entry;
+ display->registry_entry = reg;
+}
+
+static bool gwl_registry_entry_remove_by_name(GWL_Display *display,
+ uint32_t name,
+ int *r_interface_slot)
+{
+ GWL_RegistryEntry *reg = display->registry_entry;
+ GWL_RegistryEntry **reg_link_p = &display->registry_entry;
+ bool found = false;
- if (display->wl_compositor) {
- wl_compositor_destroy(display->wl_compositor);
+ if (r_interface_slot) {
+ *r_interface_slot = -1;
}
-#ifdef WITH_GHOST_WAYLAND_LIBDECOR
- if (use_libdecor) {
- if (display->libdecor) {
- wgl_libdecor_system_destroy(display->libdecor);
+ while (reg) {
+ if (reg->name == name) {
+ GWL_RegistryEntry *reg_next = reg->next;
+ const GWL_RegistryHandler *handler = gwl_registry_handler_from_interface_slot(
+ reg->interface_slot);
+ handler->remove_fn(display, reg->user_data, false);
+ if (r_interface_slot) {
+ *r_interface_slot = reg->interface_slot;
+ }
+ delete reg;
+ *reg_link_p = reg_next;
+ found = true;
+ break;
}
- }
- else
-#endif
- {
- if (display->xdg_decor) {
- wgl_xdg_decor_system_destroy(display->xdg_decor);
+ reg_link_p = &reg->next;
+ reg = reg->next;
+ }
+ return found;
+}
+
+static bool gwl_registry_entry_remove_by_interface_slot(GWL_Display *display,
+ int interface_slot,
+ bool on_exit)
+{
+ GWL_RegistryEntry *reg = display->registry_entry;
+ GWL_RegistryEntry **reg_link_p = &display->registry_entry;
+ bool found = false;
+
+ while (reg) {
+ if (reg->interface_slot == interface_slot) {
+ GWL_RegistryEntry *reg_next = reg->next;
+ const GWL_RegistryHandler *handler = gwl_registry_handler_from_interface_slot(
+ interface_slot);
+ handler->remove_fn(display, reg->user_data, on_exit);
+ delete reg;
+ *reg_link_p = reg_next;
+ reg = reg_next;
+ found = true;
+ continue;
}
+ reg_link_p = &reg->next;
+ reg = reg->next;
}
+ return found;
+}
- if (eglGetDisplay) {
- ::eglTerminate(eglGetDisplay(EGLNativeDisplayType(display->wl_display)));
+/**
+ * Remove all global objects (on exit).
+ */
+static void gwl_registry_entry_remove_all(GWL_Display *display)
+{
+ const bool on_exit = true;
+
+ /* NOTE(@campbellbarton): Free by slot instead of simply looping over
+ * `display->registry_entry` so the order of freeing is always predictable.
+ * Otherwise global objects would be feed in the order they are registered.
+ * While this works in my tests, it could cause difficult to reproduce bugs
+ * where lesser used compositors or changes to existing compositors could
+ * crash on exit based on the order of freeing objects is out of our control.
+ *
+ * To give a concrete example of how this could fail, it's possible removing
+ * a tablet interface could reference the pointer interface, or the output interface.
+ * Even though references between interfaces shouldn't be necessary in most cases
+ * when `on_exit` is enabled. */
+ int interface_slot = gwl_registry_handler_interface_slot_max();
+ while (interface_slot--) {
+ gwl_registry_entry_remove_by_interface_slot(display, interface_slot, on_exit);
}
- if (display->wl_display) {
- wl_display_disconnect(display->wl_display);
+ GHOST_ASSERT(display->registry_entry == nullptr, "Failed to remove all entries!");
+ display->registry_entry = nullptr;
+}
+
+/**
+ * Run GWL_RegistryHandler.update_fn an all registered interface instances.
+ * This is needed to refresh the state of interfaces that may reference other interfaces.
+ * Called when interfaces are added/removed.
+ *
+ * \param interface_slot_exclude: Skip updating slots of this type.
+ * Note that while harmless dependencies only exist between different types,
+ * so there is no reason to update all other outputs that an output was removed (for e.g.).
+ * Pass as -1 to update all slots.
+ *
+ * NOTE(@campbellbarton): Updating all other items on a single change is typically worth avoiding.
+ * In practice this isn't a problem as so there are so few elements in `display->registry_entry`,
+ * so few use update functions and adding/removal at runtime is rarely called (plugging/unplugging)
+ * hardware for e.g. So while it's possible to store dependency links to avoid unnecessary
+ * looping over data - it ends up being a non issue.
+ */
+static void gwl_registry_entry_update_all(GWL_Display *display, const int interface_slot_exclude)
+{
+ GHOST_ASSERT(interface_slot_exclude == -1 || (uint(interface_slot_exclude) <
+ uint(gwl_registry_handler_interface_slot_max())),
+ "Invalid exclude slot");
+
+ for (GWL_RegistryEntry *reg = display->registry_entry; reg; reg = reg->next) {
+ if (reg->interface_slot == interface_slot_exclude) {
+ continue;
+ }
+ const GWL_RegistryHandler *handler = gwl_registry_handler_from_interface_slot(
+ reg->interface_slot);
+ if (handler->update_fn == nullptr) {
+ continue;
+ }
+
+ GWL_RegisteryUpdate_Params params = {
+ .name = reg->name,
+ .interface_slot = reg->interface_slot,
+ .version = reg->version,
+
+ .user_data = reg->user_data,
+ };
+ handler->update_fn(display, &params);
}
+}
- delete display;
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Private Utility Functions
+ * \{ */
+
+/**
+ * Callback for WAYLAND to run when there is an error.
+ *
+ * \note It's useful to set a break-point on this function as some errors are fatal
+ * (for all intents and purposes) but don't crash the process.
+ */
+static void ghost_wayland_log_handler(const char *msg, va_list arg)
+{
+ fprintf(stderr, "GHOST/Wayland: ");
+ vfprintf(stderr, msg, arg); /* Includes newline. */
+
+ GHOST_TBacktraceFn backtrace_fn = GHOST_ISystem::getBacktraceFn();
+ if (backtrace_fn) {
+ backtrace_fn(stderr); /* Includes newline. */
+ }
}
static GHOST_TKey xkb_map_gkey(const xkb_keysym_t sym)
@@ -837,9 +1309,24 @@ static GHOST_TKey xkb_map_gkey_or_scan_code(const xkb_keysym_t sym, const uint32
return gkey;
}
-static GHOST_TTabletMode tablet_tool_map_type(enum zwp_tablet_tool_v2_type wl_tablet_tool_type)
+static int pointer_axis_as_index(const uint32_t axis)
+{
+ switch (axis) {
+ case WL_POINTER_AXIS_HORIZONTAL_SCROLL: {
+ return 0;
+ }
+ case WL_POINTER_AXIS_VERTICAL_SCROLL: {
+ return 1;
+ }
+ default: {
+ return -1;
+ }
+ }
+}
+
+static GHOST_TTabletMode tablet_tool_map_type(enum zwp_tablet_tool_v2_type wp_tablet_tool_type)
{
- switch (wl_tablet_tool_type) {
+ switch (wp_tablet_tool_type) {
case ZWP_TABLET_TOOL_V2_TYPE_ERASER: {
return GHOST_kTabletModeEraser;
}
@@ -854,13 +1341,13 @@ static GHOST_TTabletMode tablet_tool_map_type(enum zwp_tablet_tool_v2_type wl_ta
}
}
- GHOST_PRINT("unknown tablet tool: " << wl_tablet_tool_type << std::endl);
+ GHOST_PRINT("unknown tablet tool: " << wp_tablet_tool_type << std::endl);
return GHOST_kTabletModeStylus;
}
static const int default_cursor_size = 24;
-static const std::unordered_map<GHOST_TStandardCursor, const char *> cursors = {
+static const std::unordered_map<GHOST_TStandardCursor, const char *> ghost_wl_cursors = {
{GHOST_kStandardCursorDefault, "left_ptr"},
{GHOST_kStandardCursorRightArrow, "right_ptr"},
{GHOST_kStandardCursorLeftArrow, "left_ptr"},
@@ -901,23 +1388,23 @@ static const std::unordered_map<GHOST_TStandardCursor, const char *> cursors = {
{GHOST_kStandardCursorCopy, "copy"},
};
-static constexpr const char *mime_text_plain = "text/plain";
-static constexpr const char *mime_text_utf8 = "text/plain;charset=utf-8";
-static constexpr const char *mime_text_uri = "text/uri-list";
+static constexpr const char *ghost_wl_mime_text_plain = "text/plain";
+static constexpr const char *ghost_wl_mime_text_utf8 = "text/plain;charset=utf-8";
+static constexpr const char *ghost_wl_mime_text_uri = "text/uri-list";
-static const std::unordered_map<std::string, GHOST_TDragnDropTypes> mime_dnd = {
- {mime_text_plain, GHOST_kDragnDropTypeString},
- {mime_text_utf8, GHOST_kDragnDropTypeString},
- {mime_text_uri, GHOST_kDragnDropTypeFilenames},
+static const char *ghost_wl_mime_preference_order[] = {
+ ghost_wl_mime_text_uri,
+ ghost_wl_mime_text_utf8,
+ ghost_wl_mime_text_plain,
};
-
-static const std::vector<std::string> mime_preference_order = {
- mime_text_uri,
- mime_text_utf8,
- mime_text_plain,
+/* Aligned to `ghost_wl_mime_preference_order`. */
+static const GHOST_TDragnDropTypes ghost_wl_mime_preference_order_type[] = {
+ GHOST_kDragnDropTypeString,
+ GHOST_kDragnDropTypeString,
+ GHOST_kDragnDropTypeFilenames,
};
-static const std::vector<std::string> mime_send = {
+static const char *ghost_wl_mime_send[] = {
"UTF8_STRING",
"COMPOUND_TEXT",
"TEXT",
@@ -1045,9 +1532,9 @@ static void keyboard_depressed_state_key_event(GWL_Seat *seat,
}
static void keyboard_depressed_state_push_events_from_change(
- GWL_Seat *seat, const WGL_KeyboardDepressedState &key_depressed_prev)
+ GWL_Seat *seat, const GWL_KeyboardDepressedState &key_depressed_prev)
{
- GHOST_IWindow *win = ghost_wl_surface_user_data(seat->keyboard.wl_surface);
+ GHOST_IWindow *win = ghost_wl_surface_user_data(seat->keyboard.wl_surface_window);
GHOST_SystemWayland *system = seat->system;
/* Separate key up and down into separate passes so key down events always come after key up.
@@ -1134,7 +1621,7 @@ static void relative_pointer_handle_relative_motion(
const wl_fixed_t /*dy_unaccel*/)
{
GWL_Seat *seat = static_cast<GWL_Seat *>(data);
- if (wl_surface *wl_surface_focus = seat->pointer.wl_surface) {
+ if (wl_surface *wl_surface_focus = seat->pointer.wl_surface_window) {
CLOG_INFO(LOG, 2, "relative_motion");
GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
const wl_fixed_t scale = win->scale();
@@ -1167,7 +1654,7 @@ static CLG_LogRef LOG_WL_DATA_SOURCE = {"ghost.wl.handle.data_source"};
static void dnd_events(const GWL_Seat *const seat, const GHOST_TEventType event)
{
/* NOTE: `seat->data_offer_dnd_mutex` must already be locked. */
- if (wl_surface *wl_surface_focus = seat->wl_surface_focus_dnd) {
+ if (wl_surface *wl_surface_focus = seat->wl_surface_window_focus_dnd) {
GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
const wl_fixed_t scale = win->scale();
const int event_xy[2] = {
@@ -1176,40 +1663,144 @@ static void dnd_events(const GWL_Seat *const seat, const GHOST_TEventType event)
};
const uint64_t time = seat->system->getMilliSeconds();
- for (const std::string &type : mime_preference_order) {
- seat->system->pushEvent(new GHOST_EventDragnDrop(
- time, event, mime_dnd.at(type), win, UNPACK2(event_xy), nullptr));
+ for (size_t i = 0; i < ARRAY_SIZE(ghost_wl_mime_preference_order_type); i++) {
+ const GHOST_TDragnDropTypes type = ghost_wl_mime_preference_order_type[i];
+ seat->system->pushEvent(
+ new GHOST_EventDragnDrop(time, event, type, win, UNPACK2(event_xy), nullptr));
}
}
}
-static std::string read_pipe(GWL_DataOffer *data_offer,
- const std::string mime_receive,
- std::mutex *mutex)
+/**
+ * Read from `fd` into a buffer which is returned.
+ * \return the buffer or null on failure.
+ */
+static char *read_file_as_buffer(const int fd, const bool nil_terminate, size_t *r_len)
+{
+ struct ByteChunk {
+ ByteChunk *next;
+ char data[4096 - sizeof(ByteChunk *)];
+ };
+ ByteChunk *chunk_first = nullptr, **chunk_link_p = &chunk_first;
+ bool ok = true;
+ size_t len = 0;
+ while (true) {
+ ByteChunk *chunk = static_cast<typeof(chunk)>(malloc(sizeof(*chunk)));
+ if (UNLIKELY(chunk == nullptr)) {
+ CLOG_WARN(LOG, "unable to allocate chunk for file buffer");
+ ok = false;
+ break;
+ }
+ chunk->next = nullptr;
+ const ssize_t len_chunk = read(fd, chunk->data, sizeof(chunk->data));
+ if (len_chunk <= 0) {
+ if (UNLIKELY(len_chunk < 0)) {
+ CLOG_WARN(LOG, "error reading from pipe: %s", std::strerror(errno));
+ ok = false;
+ }
+ free(chunk);
+ break;
+ }
+ if (chunk_first == nullptr) {
+ chunk_first = chunk;
+ }
+ *chunk_link_p = chunk;
+ chunk_link_p = &chunk->next;
+ len += len_chunk;
+ }
+
+ char *buf = nullptr;
+ if (ok) {
+ buf = static_cast<char *>(malloc(len + (nil_terminate ? 1 : 0)));
+ if (UNLIKELY(buf == nullptr)) {
+ CLOG_WARN(LOG, "unable to allocate file buffer: %zu bytes", len);
+ ok = false;
+ }
+ }
+
+ if (ok) {
+ *r_len = len;
+ if (nil_terminate) {
+ buf[len] = '\0';
+ }
+ }
+ else {
+ *r_len = 0;
+ }
+
+ char *buf_stride = buf;
+ while (chunk_first) {
+ if (ok) {
+ const size_t len_chunk = std::min(len, sizeof(chunk_first->data));
+ memcpy(buf_stride, chunk_first->data, len_chunk);
+ buf_stride += len_chunk;
+ len -= len_chunk;
+ }
+ ByteChunk *chunk = chunk_first->next;
+ free(chunk_first);
+ chunk_first = chunk;
+ }
+
+ return buf;
+}
+
+static char *read_buffer_from_data_offer(GWL_DataOffer *data_offer,
+ const char *mime_receive,
+ std::mutex *mutex,
+ const bool nil_terminate,
+ size_t *r_len)
{
int pipefd[2];
- if (UNLIKELY(pipe(pipefd) != 0)) {
- return {};
+ const bool pipefd_ok = pipe(pipefd) == 0;
+ if (pipefd_ok) {
+ wl_data_offer_receive(data_offer->id, mime_receive, pipefd[1]);
+ close(pipefd[1]);
+ }
+ else {
+ CLOG_WARN(LOG, "error creating pipe: %s", std::strerror(errno));
}
- wl_data_offer_receive(data_offer->id, mime_receive.c_str(), pipefd[1]);
- close(pipefd[1]);
- data_offer->in_use.store(false);
+ /* Only for DND (A no-op to disable for clipboard data-offer). */
+ data_offer->dnd.in_use = false;
if (mutex) {
mutex->unlock();
}
/* WARNING: `data_offer` may be freed from now on. */
+ char *buf = nullptr;
+ if (pipefd_ok) {
+ buf = read_file_as_buffer(pipefd[0], nil_terminate, r_len);
+ close(pipefd[0]);
+ }
+ return buf;
+}
- std::string data;
- ssize_t len;
- char buffer[4096];
- while ((len = read(pipefd[0], buffer, sizeof(buffer))) > 0) {
- data.insert(data.end(), buffer, buffer + len);
+static char *read_buffer_from_primary_selection_offer(GWL_PrimarySelection_DataOffer *data_offer,
+ const char *mime_receive,
+ std::mutex *mutex,
+ const bool nil_terminate,
+ size_t *r_len)
+{
+ int pipefd[2];
+ const bool pipefd_ok = pipe(pipefd) == 0;
+ if (pipefd_ok) {
+ zwp_primary_selection_offer_v1_receive(data_offer->id, mime_receive, pipefd[1]);
+ close(pipefd[1]);
+ }
+ else {
+ CLOG_WARN(LOG, "error creating pipe: %s", std::strerror(errno));
}
- close(pipefd[0]);
- return data;
+ if (mutex) {
+ mutex->unlock();
+ }
+ /* WARNING: `data_offer` may be freed from now on. */
+ char *buf = nullptr;
+ if (pipefd_ok) {
+ buf = read_file_as_buffer(pipefd[0], nil_terminate, r_len);
+ close(pipefd[0]);
+ }
+ return buf;
}
/**
@@ -1231,20 +1822,33 @@ static void data_source_handle_send(void *data,
const int32_t fd)
{
GWL_Seat *seat = static_cast<GWL_Seat *>(data);
- std::lock_guard lock{seat->data_source_mutex};
CLOG_INFO(LOG, 2, "send");
- const char *const buffer = seat->data_source->buffer_out;
- if (write(fd, buffer, strlen(buffer)) < 0) {
- GHOST_PRINT("error writing to clipboard: " << std::strerror(errno) << std::endl);
- }
- close(fd);
+ auto write_file_fn = [](GWL_Seat *seat, const int fd) {
+ if (UNLIKELY(write(fd,
+ seat->data_source->buffer_out.data,
+ seat->data_source->buffer_out.data_size) < 0)) {
+ CLOG_WARN(LOG, "error writing to clipboard: %s", std::strerror(errno));
+ }
+ close(fd);
+ seat->data_source_mutex.unlock();
+ };
+
+ seat->data_source_mutex.lock();
+ std::thread write_thread(write_file_fn, seat, fd);
+ write_thread.detach();
}
-static void data_source_handle_cancelled(void * /*data*/, struct wl_data_source *wl_data_source)
+static void data_source_handle_cancelled(void *data, struct wl_data_source *wl_data_source)
{
CLOG_INFO(LOG, 2, "cancelled");
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+ GWL_DataSource *data_source = seat->data_source;
+ if (seat->data_source->wl_source == wl_data_source) {
+ data_source->wl_source = nullptr;
+ }
+
wl_data_source_destroy(wl_data_source);
}
@@ -1313,7 +1917,8 @@ static void data_offer_handle_offer(void *data,
const char *mime_type)
{
CLOG_INFO(LOG, 2, "offer (mime_type=%s)", mime_type);
- static_cast<GWL_DataOffer *>(data)->types.insert(mime_type);
+ GWL_DataOffer *data_offer = static_cast<GWL_DataOffer *>(data);
+ data_offer->types.insert(mime_type);
}
static void data_offer_handle_source_actions(void *data,
@@ -1321,7 +1926,8 @@ static void data_offer_handle_source_actions(void *data,
const uint32_t source_actions)
{
CLOG_INFO(LOG, 2, "source_actions (%u)", source_actions);
- static_cast<GWL_DataOffer *>(data)->source_actions = source_actions;
+ GWL_DataOffer *data_offer = static_cast<GWL_DataOffer *>(data);
+ data_offer->dnd.source_actions = (enum wl_data_device_manager_dnd_action)source_actions;
}
static void data_offer_handle_action(void *data,
@@ -1329,7 +1935,8 @@ static void data_offer_handle_action(void *data,
const uint32_t dnd_action)
{
CLOG_INFO(LOG, 2, "actions (%u)", dnd_action);
- static_cast<GWL_DataOffer *>(data)->dnd_action = dnd_action;
+ GWL_DataOffer *data_offer = static_cast<GWL_DataOffer *>(data);
+ data_offer->dnd.action = (enum wl_data_device_manager_dnd_action)dnd_action;
}
static const struct wl_data_offer_listener data_offer_listener = {
@@ -1381,7 +1988,7 @@ static void data_device_handle_enter(void *data,
seat->data_offer_dnd = static_cast<GWL_DataOffer *>(wl_data_offer_get_user_data(id));
GWL_DataOffer *data_offer = seat->data_offer_dnd;
- data_offer->in_use.store(true);
+ data_offer->dnd.in_use = true;
data_offer->dnd.xy[0] = x;
data_offer->dnd.xy[1] = y;
@@ -1390,11 +1997,15 @@ static void data_device_handle_enter(void *data,
WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE,
WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY);
- for (const std::string &type : mime_preference_order) {
- wl_data_offer_accept(id, serial, type.c_str());
+ for (size_t i = 0; i < ARRAY_SIZE(ghost_wl_mime_preference_order); i++) {
+ const char *type = ghost_wl_mime_preference_order[i];
+ wl_data_offer_accept(id, serial, type);
}
- seat->wl_surface_focus_dnd = wl_surface;
+ seat->wl_surface_window_focus_dnd = wl_surface;
+
+ seat->system->seat_active_set(seat);
+
dnd_events(seat, GHOST_kEventDraggingEntered);
}
@@ -1406,9 +2017,9 @@ static void data_device_handle_leave(void *data, struct wl_data_device * /*wl_da
CLOG_INFO(LOG, 2, "leave");
dnd_events(seat, GHOST_kEventDraggingExited);
- seat->wl_surface_focus_dnd = nullptr;
+ seat->wl_surface_window_focus_dnd = nullptr;
- if (seat->data_offer_dnd && !seat->data_offer_dnd->in_use.load()) {
+ if (seat->data_offer_dnd && !seat->data_offer_dnd->dnd.in_use) {
wl_data_offer_destroy(seat->data_offer_dnd->id);
delete seat->data_offer_dnd;
seat->data_offer_dnd = nullptr;
@@ -1439,23 +2050,35 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat
GWL_DataOffer *data_offer = seat->data_offer_dnd;
- const std::string mime_receive = *std::find_first_of(mime_preference_order.begin(),
- mime_preference_order.end(),
- data_offer->types.begin(),
- data_offer->types.end());
+ /* Use a blank string for `mime_receive` to prevent crashes, although could also be `nullptr`.
+ * Failure to set this to a known type just means the file won't have any special handling.
+ * GHOST still generates a dropped file event.
+ * NOTE: this string can be compared with `mime_text_plain`, `mime_text_uri` etc...
+ * as the this always points to the same values. */
+ const char *mime_receive = "";
+ for (size_t i = 0; i < ARRAY_SIZE(ghost_wl_mime_preference_order); i++) {
+ const char *type = ghost_wl_mime_preference_order[i];
+ if (data_offer->types.count(type)) {
+ mime_receive = type;
+ break;
+ }
+ }
- CLOG_INFO(LOG, 2, "drop mime_recieve=%s", mime_receive.c_str());
+ CLOG_INFO(LOG, 2, "drop mime_recieve=%s", mime_receive);
auto read_uris_fn = [](GWL_Seat *const seat,
GWL_DataOffer *data_offer,
- wl_surface *wl_surface,
- const std::string mime_receive) {
+ wl_surface *wl_surface_window,
+ const char *mime_receive) {
const wl_fixed_t xy[2] = {UNPACK2(data_offer->dnd.xy)};
- const std::string data = read_pipe(data_offer, mime_receive, nullptr);
+ size_t data_buf_len = 0;
+ const char *data_buf = read_buffer_from_data_offer(
+ data_offer, mime_receive, nullptr, false, &data_buf_len);
+ std::string data = data_buf ? std::string(data_buf, data_buf_len) : "";
+ free(const_cast<char *>(data_buf));
- CLOG_INFO(
- LOG, 2, "drop_read_uris mime_receive=%s, data=%s", mime_receive.c_str(), data.c_str());
+ CLOG_INFO(LOG, 2, "drop_read_uris mime_receive=%s, data=%s", mime_receive, data.c_str());
wl_data_offer_finish(data_offer->id);
wl_data_offer_destroy(data_offer->id);
@@ -1468,13 +2091,13 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat
GHOST_SystemWayland *const system = seat->system;
- if (mime_receive == mime_text_uri) {
+ if (mime_receive == ghost_wl_mime_text_uri) {
static constexpr const char *file_proto = "file://";
/* NOTE: some applications CRLF (`\r\n`) GTK3 for e.g. & others don't `pcmanfm-qt`.
* So support both, once `\n` is found, strip the preceding `\r` if found. */
static constexpr const char *lf = "\n";
- GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface);
+ GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_window);
std::vector<std::string> uris;
size_t pos = 0;
@@ -1513,7 +2136,7 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat
wl_fixed_to_int(scale * xy[1]),
flist));
}
- else if (ELEM(mime_receive, mime_text_plain, mime_text_utf8)) {
+ else if (ELEM(mime_receive, ghost_wl_mime_text_plain, ghost_wl_mime_text_utf8)) {
/* TODO: enable use of internal functions 'txt_insert_buf' and
* 'text_update_edited' to behave like dropped text was pasted. */
CLOG_INFO(LOG, 2, "drop_read_uris_fn (text_plain, text_utf8), unhandled!");
@@ -1521,10 +2144,10 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat
wl_display_roundtrip(system->wl_display());
};
- /* Pass in `seat->wl_surface_focus_dnd` instead of accessing it from `seat` since the leave
- * callback (#data_device_handle_leave) will clear the value once this function starts. */
+ /* Pass in `seat->wl_surface_window_focus_dnd` instead of accessing it from `seat` since the
+ * leave callback (#data_device_handle_leave) will clear the value once this function starts. */
std::thread read_thread(
- read_uris_fn, seat, data_offer, seat->wl_surface_focus_dnd, mime_receive);
+ read_uris_fn, seat, data_offer, seat->wl_surface_window_focus_dnd, mime_receive);
read_thread.detach();
}
@@ -1554,30 +2177,6 @@ static void data_device_handle_selection(void *data,
/* Get new data offer. */
data_offer = static_cast<GWL_DataOffer *>(wl_data_offer_get_user_data(id));
seat->data_offer_copy_paste = data_offer;
-
- auto read_selection_fn = [](GWL_Seat *seat) {
- GHOST_SystemWayland *const system = seat->system;
- seat->data_offer_copy_paste_mutex.lock();
-
- GWL_DataOffer *data_offer = seat->data_offer_copy_paste;
- std::string mime_receive;
- for (const std::string type : {mime_text_utf8, mime_text_plain}) {
- if (data_offer->types.count(type)) {
- mime_receive = type;
- break;
- }
- }
- const std::string data = read_pipe(
- data_offer, mime_receive, &seat->data_offer_copy_paste_mutex);
-
- {
- std::lock_guard lock{system_clipboard_mutex};
- system->clipboard_set(data);
- }
- };
-
- std::thread read_thread(read_selection_fn, seat);
- read_thread.detach();
}
static const struct wl_data_device_listener data_device_listener = {
@@ -1631,7 +2230,7 @@ static CLG_LogRef LOG_WL_CURSOR_SURFACE = {"ghost.wl.handle.cursor_surface"};
static bool update_cursor_scale(GWL_Cursor &cursor,
wl_shm *shm,
GWL_SeatStatePointer *seat_state_pointer,
- wl_surface *wl_cursor_surface)
+ wl_surface *wl_surface_cursor)
{
int scale = 0;
for (const GWL_Output *output : seat_state_pointer->outputs) {
@@ -1643,10 +2242,11 @@ static bool update_cursor_scale(GWL_Cursor &cursor,
if (scale > 0 && seat_state_pointer->theme_scale != scale) {
seat_state_pointer->theme_scale = scale;
if (!cursor.is_custom) {
- wl_surface_set_buffer_scale(wl_cursor_surface, scale);
+ wl_surface_set_buffer_scale(wl_surface_cursor, scale);
}
wl_cursor_theme_destroy(cursor.wl_theme);
- cursor.wl_theme = wl_cursor_theme_load(cursor.theme_name.c_str(), scale * cursor.size, shm);
+ cursor.wl_theme = wl_cursor_theme_load(
+ cursor.theme_name.c_str(), scale * cursor.theme_size, shm);
return true;
}
return false;
@@ -1663,8 +2263,8 @@ static void cursor_surface_handle_enter(void *data,
CLOG_INFO(LOG, 2, "handle_enter");
GWL_Seat *seat = static_cast<GWL_Seat *>(data);
- GWL_SeatStatePointer *seat_state_pointer = seat_state_pointer_from_cursor_surface(seat,
- wl_surface);
+ GWL_SeatStatePointer *seat_state_pointer = gwl_seat_state_pointer_from_cursor_surface(
+ seat, wl_surface);
const GWL_Output *reg_output = ghost_wl_output_user_data(wl_output);
seat_state_pointer->outputs.insert(reg_output);
update_cursor_scale(seat->cursor, seat->system->wl_shm(), seat_state_pointer, wl_surface);
@@ -1681,8 +2281,8 @@ static void cursor_surface_handle_leave(void *data,
CLOG_INFO(LOG, 2, "handle_leave");
GWL_Seat *seat = static_cast<GWL_Seat *>(data);
- GWL_SeatStatePointer *seat_state_pointer = seat_state_pointer_from_cursor_surface(seat,
- wl_surface);
+ GWL_SeatStatePointer *seat_state_pointer = gwl_seat_state_pointer_from_cursor_surface(
+ seat, wl_surface);
const GWL_Output *reg_output = ghost_wl_output_user_data(wl_output);
seat_state_pointer->outputs.erase(reg_output);
update_cursor_scale(seat->cursor, seat->system->wl_shm(), seat_state_pointer, wl_surface);
@@ -1726,7 +2326,18 @@ static void pointer_handle_enter(void *data,
seat->pointer.serial = serial;
seat->pointer.xy[0] = surface_x;
seat->pointer.xy[1] = surface_y;
- seat->pointer.wl_surface = wl_surface;
+
+ /* Resetting scroll events is likely unnecessary,
+ * do this to avoid any possible problems as it's harmless. */
+ seat->pointer_scroll.smooth_xy[0] = 0;
+ seat->pointer_scroll.smooth_xy[1] = 0;
+ seat->pointer_scroll.discrete_xy[0] = 0;
+ seat->pointer_scroll.discrete_xy[1] = 0;
+ seat->pointer_scroll.axis_source = WL_POINTER_AXIS_SOURCE_WHEEL;
+
+ seat->pointer.wl_surface_window = wl_surface;
+
+ seat->system->seat_active_set(seat);
win->setCursorShape(win->getCursorShape());
@@ -1745,7 +2356,7 @@ static void pointer_handle_leave(void *data,
struct wl_surface *wl_surface)
{
/* First clear the `pointer.wl_surface`, since the window won't exist when closing the window. */
- static_cast<GWL_Seat *>(data)->pointer.wl_surface = nullptr;
+ static_cast<GWL_Seat *>(data)->pointer.wl_surface_window = nullptr;
if (wl_surface && ghost_wl_surface_own(wl_surface)) {
CLOG_INFO(LOG, 2, "leave");
GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface);
@@ -1766,7 +2377,7 @@ static void pointer_handle_motion(void *data,
seat->pointer.xy[0] = surface_x;
seat->pointer.xy[1] = surface_y;
- if (wl_surface *wl_surface_focus = seat->pointer.wl_surface) {
+ if (wl_surface *wl_surface_focus = seat->pointer.wl_surface_window) {
CLOG_INFO(LOG, 2, "motion");
GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
const wl_fixed_t scale = win->scale();
@@ -1830,31 +2441,99 @@ static void pointer_handle_button(void *data,
seat->data_source_serial = serial;
seat->pointer.buttons.set(ebutton, state == WL_POINTER_BUTTON_STATE_PRESSED);
- if (wl_surface *wl_surface_focus = seat->pointer.wl_surface) {
+ if (wl_surface *wl_surface_focus = seat->pointer.wl_surface_window) {
GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
seat->system->pushEvent(new GHOST_EventButton(
seat->system->getMilliSeconds(), etype, win, ebutton, GHOST_TABLET_DATA_NONE));
}
}
-static void pointer_handle_axis(void * /*data*/,
+static void pointer_handle_axis(void *data,
struct wl_pointer * /*wl_pointer*/,
const uint32_t /*time*/,
const uint32_t axis,
const wl_fixed_t value)
{
+ /* NOTE: this is used for touch based scrolling - or other input that doesn't scroll with
+ * discrete "steps". This allows supporting smooth-scrolling without "touch" gesture support. */
CLOG_INFO(LOG, 2, "axis (axis=%u, value=%d)", axis, value);
+ const int index = pointer_axis_as_index(axis);
+ if (UNLIKELY(index == -1)) {
+ return;
+ }
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+ seat->pointer_scroll.smooth_xy[index] = value;
}
-static void pointer_handle_frame(void * /*data*/, struct wl_pointer * /*wl_pointer*/)
+static void pointer_handle_frame(void *data, struct wl_pointer * /*wl_pointer*/)
{
CLOG_INFO(LOG, 2, "frame");
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+
+ /* Both discrete and smooth events may be set at once, never generate events for both
+ * as this will be handling the same event in to different ways.
+ * Prioritize discrete axis events for the mouse wheel, otherwise smooth scroll. */
+ if (seat->pointer_scroll.axis_source == WL_POINTER_AXIS_SOURCE_WHEEL) {
+ if (seat->pointer_scroll.discrete_xy[0]) {
+ seat->pointer_scroll.smooth_xy[0] = 0;
+ }
+ if (seat->pointer_scroll.discrete_xy[1]) {
+ seat->pointer_scroll.smooth_xy[1] = 0;
+ }
+ }
+ else {
+ if (seat->pointer_scroll.smooth_xy[0]) {
+ seat->pointer_scroll.discrete_xy[0] = 0;
+ }
+ if (seat->pointer_scroll.smooth_xy[1]) {
+ seat->pointer_scroll.discrete_xy[1] = 0;
+ }
+ }
+
+ /* Discrete X axis currently unsupported. */
+ if (seat->pointer_scroll.discrete_xy[1]) {
+ if (wl_surface *wl_surface_focus = seat->pointer.wl_surface_window) {
+ GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
+ const int32_t discrete = seat->pointer_scroll.discrete_xy[1];
+ seat->system->pushEvent(new GHOST_EventWheel(
+ seat->system->getMilliSeconds(), win, std::signbit(discrete) ? +1 : -1));
+ }
+ seat->pointer_scroll.discrete_xy[0] = 0;
+ seat->pointer_scroll.discrete_xy[1] = 0;
+ }
+
+ if (seat->pointer_scroll.smooth_xy[0] || seat->pointer_scroll.smooth_xy[1]) {
+ if (wl_surface *wl_surface_focus = seat->pointer.wl_surface_window) {
+ GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
+ const wl_fixed_t scale = win->scale();
+ seat->system->pushEvent(new GHOST_EventTrackpad(
+ seat->system->getMilliSeconds(),
+ win,
+ GHOST_kTrackpadEventScroll,
+ wl_fixed_to_int(scale * seat->pointer.xy[0]),
+ wl_fixed_to_int(scale * seat->pointer.xy[1]),
+ /* NOTE: scaling the delta doesn't seem necessary.
+ * NOTE: inverting delta gives correct results, see: QTBUG-85767.
+ * NOTE: the preference to invert scrolling (in GNOME at least)
+ * has already been applied so there is no need to read this preference. */
+ -wl_fixed_to_int(seat->pointer_scroll.smooth_xy[0]),
+ -wl_fixed_to_int(seat->pointer_scroll.smooth_xy[1]),
+ false));
+ }
+
+ seat->pointer_scroll.smooth_xy[0] = 0;
+ seat->pointer_scroll.smooth_xy[1] = 0;
+ }
+
+ seat->pointer_scroll.axis_source = WL_POINTER_AXIS_SOURCE_WHEEL;
}
-static void pointer_handle_axis_source(void * /*data*/,
+static void pointer_handle_axis_source(void *data,
struct wl_pointer * /*wl_pointer*/,
uint32_t axis_source)
{
CLOG_INFO(LOG, 2, "axis_source (axis_source=%u)", axis_source);
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+ seat->pointer_scroll.axis_source = (enum wl_pointer_axis_source)axis_source;
}
static void pointer_handle_axis_stop(void * /*data*/,
struct wl_pointer * /*wl_pointer*/,
@@ -1868,18 +2547,15 @@ static void pointer_handle_axis_discrete(void *data,
uint32_t axis,
int32_t discrete)
{
+ /* NOTE: a discrete axis are typically mouse wheel events.
+ * The non-discrete version of this function is used for touch-pad. */
CLOG_INFO(LOG, 2, "axis_discrete (axis=%u, discrete=%d)", axis, discrete);
-
- GWL_Seat *seat = static_cast<GWL_Seat *>(data);
- if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) {
+ const int index = pointer_axis_as_index(axis);
+ if (UNLIKELY(index == -1)) {
return;
}
-
- if (wl_surface *wl_surface_focus = seat->pointer.wl_surface) {
- GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
- seat->system->pushEvent(new GHOST_EventWheel(
- seat->system->getMilliSeconds(), win, std::signbit(discrete) ? +1 : -1));
- }
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+ seat->pointer_scroll.discrete_xy[index] = discrete;
}
static const struct wl_pointer_listener pointer_listener = {
@@ -1899,6 +2575,325 @@ static const struct wl_pointer_listener pointer_listener = {
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Listener (Pointer Gesture: Hold), #zwp_pointer_gesture_hold_v1_listener
+ * \{ */
+
+#ifdef ZWP_POINTER_GESTURE_HOLD_V1_INTERFACE
+static CLG_LogRef LOG_WL_POINTER_GESTURE_HOLD = {"ghost.wl.handle.pointer_gesture.hold"};
+# define LOG (&LOG_WL_POINTER_GESTURE_HOLD)
+
+static void gesture_hold_handle_begin(
+ void * /*data*/,
+ struct zwp_pointer_gesture_hold_v1 * /*zwp_pointer_gesture_hold_v1*/,
+ uint32_t /*serial*/,
+ uint32_t /*time*/,
+ struct wl_surface * /*surface*/,
+ uint32_t fingers)
+{
+ CLOG_INFO(LOG, 2, "begin (fingers=%u)", fingers);
+}
+
+static void gesture_hold_handle_end(
+ void * /*data*/,
+ struct zwp_pointer_gesture_hold_v1 * /*zwp_pointer_gesture_hold_v1*/,
+ uint32_t /*serial*/,
+ uint32_t /*time*/,
+ int32_t cancelled)
+{
+ CLOG_INFO(LOG, 2, "end (cancelled=%i)", cancelled);
+}
+
+static const struct zwp_pointer_gesture_hold_v1_listener gesture_hold_listener = {
+ gesture_hold_handle_begin,
+ gesture_hold_handle_end,
+};
+
+# undef LOG
+#endif /* ZWP_POINTER_GESTURE_HOLD_V1_INTERFACE */
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (Pointer Gesture: Pinch), #zwp_pointer_gesture_pinch_v1_listener
+ * \{ */
+
+#ifdef ZWP_POINTER_GESTURE_PINCH_V1_INTERFACE
+static CLG_LogRef LOG_WL_POINTER_GESTURE_PINCH = {"ghost.wl.handle.pointer_gesture.pinch"};
+# define LOG (&LOG_WL_POINTER_GESTURE_PINCH)
+
+static void gesture_pinch_handle_begin(void *data,
+ struct zwp_pointer_gesture_pinch_v1 * /*pinch*/,
+ uint32_t /*serial*/,
+ uint32_t /*time*/,
+ struct wl_surface * /*surface*/,
+ uint32_t fingers)
+{
+ CLOG_INFO(LOG, 2, "begin (fingers=%u)", fingers);
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+ /* Reset defaults. */
+ seat->pointer_gesture_pinch = GWL_SeatStatePointerGesture_Pinch{};
+
+ GHOST_WindowWayland *win = nullptr;
+ if (wl_surface *wl_surface_focus = seat->pointer.wl_surface_window) {
+ win = ghost_wl_surface_user_data(wl_surface_focus);
+ }
+ /* NOTE(@campbellbarton): Blender's use of track-pad coordinates is inconsistent and needs work.
+ * This isn't specific to WAYLAND, in practice they tend to work well enough in most cases.
+ * Some operators scale by the UI scale, some don't.
+ * Even this window scale is not correct because it doesn't account for:
+ * 1) Fractional window scale.
+ * 2) Blender's UI scale preference (which GHOST doesn't know about).
+ *
+ * If support for this were all that was needed it could be handled in GHOST,
+ * however as the operators are not even using coordinates compatible with each other,
+ * it would be better to resolve this by passing rotation & zoom levels directly,
+ * instead of attempting to handle them as cursor coordinates.
+ */
+ const wl_fixed_t win_scale = win ? win->scale() : 1;
+
+ /* NOTE(@campbellbarton): Scale factors match Blender's operators & default preferences.
+ * For these values to work correctly, operator logic will need to be changed not to scale input
+ * by the region size (as with 3D view zoom) or preference for 3D view orbit sensitivity.
+ *
+ * By working "correctly" I mean that a rotation action where the users fingers rotate to
+ * opposite locations should always rotate the viewport 180d, since users will expect the
+ * physical location of their fingers to match the viewport.
+ * Similarly with zoom, the scale value from the pinch action can be mapped to a zoom level
+ * although unlike rotation, an inexact mapping is less noticeable.
+ * Users may even prefer the zoom level to be scaled - which could be a preference. */
+ seat->pointer_gesture_pinch.scale.value = wl_fixed_from_int(1);
+ /* The value 300 matches a value used in clip & image zoom operators.
+ * It seems OK for the 3D view too. */
+ seat->pointer_gesture_pinch.scale.factor = 300 * win_scale;
+ /* The value 5 is used on macOS and roughly maps 1:1 with turntable rotation,
+ * although preferences can scale the sensitivity (which would be skipped ideally). */
+ seat->pointer_gesture_pinch.rotation.factor = 5 * win_scale;
+}
+
+static void gesture_pinch_handle_update(void *data,
+ struct zwp_pointer_gesture_pinch_v1 * /*pinch*/,
+ uint32_t /*time*/,
+ wl_fixed_t dx,
+ wl_fixed_t dy,
+ wl_fixed_t scale,
+ wl_fixed_t rotation)
+{
+ CLOG_INFO(LOG,
+ 2,
+ "update (dx=%.3f, dy=%.3f, scale=%.3f, rotation=%.3f)",
+ wl_fixed_to_double(dx),
+ wl_fixed_to_double(dy),
+ wl_fixed_to_double(scale),
+ wl_fixed_to_double(rotation));
+
+ GWL_Seat *seat = static_cast<GWL_Seat *>(data);
+
+ GHOST_WindowWayland *win = nullptr;
+
+ if (wl_surface *wl_surface_focus = seat->pointer.wl_surface_window) {
+ win = ghost_wl_surface_user_data(wl_surface_focus);
+ }
+
+ /* Scale defaults to `wl_fixed_from_int(1)` which may change while pinching.
+ * This needs to be converted to a delta. */
+ const wl_fixed_t scale_delta = scale - seat->pointer_gesture_pinch.scale.value;
+ const int scale_as_delta_px = gwl_scaled_fixed_t_add_and_calc_rounded_delta(
+ &seat->pointer_gesture_pinch.scale, scale_delta);
+
+ /* Rotation in degrees, unlike scale this is a delta. */
+ const int rotation_as_delta_px = gwl_scaled_fixed_t_add_and_calc_rounded_delta(
+ &seat->pointer_gesture_pinch.rotation, rotation);
+
+ if (win) {
+ const wl_fixed_t win_scale = win->scale();
+ const int32_t event_xy[2] = {
+ wl_fixed_to_int(win_scale * seat->pointer.xy[0]),
+ wl_fixed_to_int(win_scale * seat->pointer.xy[1]),
+ };
+ if (scale_as_delta_px) {
+ seat->system->pushEvent(new GHOST_EventTrackpad(seat->system->getMilliSeconds(),
+ win,
+ GHOST_kTrackpadEventMagnify,
+ event_xy[0],
+ event_xy[1],
+ scale_as_delta_px,
+ 0,
+ false));
+ }
+
+ if (rotation_as_delta_px) {
+ seat->system->pushEvent(new GHOST_EventTrackpad(seat->system->getMilliSeconds(),
+ win,
+ GHOST_kTrackpadEventRotate,
+ event_xy[0],
+ event_xy[1],
+ rotation_as_delta_px,
+ 0,
+ false));
+ }
+ }
+}
+
+static void gesture_pinch_handle_end(void * /*data*/,
+ struct zwp_pointer_gesture_pinch_v1 * /*pinch*/,
+ uint32_t /*serial*/,
+ uint32_t /*time*/,
+ int32_t cancelled)
+{
+ CLOG_INFO(LOG, 2, "end (cancelled=%i)", cancelled);
+}
+
+static const struct zwp_pointer_gesture_pinch_v1_listener gesture_pinch_listener = {
+ gesture_pinch_handle_begin,
+ gesture_pinch_handle_update,
+ gesture_pinch_handle_end,
+};
+
+# undef LOG
+#endif /* ZWP_POINTER_GESTURE_PINCH_V1_INTERFACE */
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (Pointer Gesture: Swipe), #zwp_pointer_gesture_swipe_v1
+ *
+ * \note In both Gnome-Shell & KDE this gesture isn't emitted at time of writing,
+ * instead, high resolution 2D #wl_pointer_listener.axis data is generated which works well.
+ * There may be some situations where WAYLAND compositors generate this gesture
+ * (swiping with 3+ fingers, for e.g.). So keep this to allow logging & testing gestures.
+ * \{ */
+
+#ifdef ZWP_POINTER_GESTURE_SWIPE_V1_INTERFACE
+static CLG_LogRef LOG_WL_POINTER_GESTURE_SWIPE = {"ghost.wl.handle.pointer_gesture.swipe"};
+# define LOG (&LOG_WL_POINTER_GESTURE_SWIPE)
+
+static void gesture_swipe_handle_begin(
+ void * /*data*/,
+ struct zwp_pointer_gesture_swipe_v1 * /*zwp_pointer_gesture_swipe_v1*/,
+ uint32_t /*serial*/,
+ uint32_t /*time*/,
+ struct wl_surface * /*surface*/,
+ uint32_t fingers)
+{
+ CLOG_INFO(LOG, 2, "begin (fingers=%u)", fingers);
+}
+
+static void gesture_swipe_handle_update(
+ void * /*data*/,
+ struct zwp_pointer_gesture_swipe_v1 * /*zwp_pointer_gesture_swipe_v1*/,
+ uint32_t /*time*/,
+ wl_fixed_t dx,
+ wl_fixed_t dy)
+{
+ CLOG_INFO(LOG, 2, "update (dx=%.3f, dy=%.3f)", wl_fixed_to_double(dx), wl_fixed_to_double(dy));
+}
+
+static void gesture_swipe_handle_end(
+ void * /*data*/,
+ struct zwp_pointer_gesture_swipe_v1 * /*zwp_pointer_gesture_swipe_v1*/,
+ uint32_t /*serial*/,
+ uint32_t /*time*/,
+ int32_t cancelled)
+{
+ CLOG_INFO(LOG, 2, "end (cancelled=%i)", cancelled);
+}
+
+static const struct zwp_pointer_gesture_swipe_v1_listener gesture_swipe_listener = {
+ gesture_swipe_handle_begin,
+ gesture_swipe_handle_update,
+ gesture_swipe_handle_end,
+};
+
+# undef LOG
+#endif /* ZWP_POINTER_GESTURE_SWIPE_V1_INTERFACE */
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (Touch Seat), #wl_touch_listener
+ *
+ * NOTE(@campbellbarton): It's not clear if this interface is used by popular compositors.
+ * It looks like GNOME/KDE only support `zwp_pointer_gestures_v1_interface`.
+ * If this isn't used anywhere, it could be removed.
+ * \{ */
+
+static CLG_LogRef LOG_WL_TOUCH = {"ghost.wl.handle.touch"};
+#define LOG (&LOG_WL_TOUCH)
+
+static void touch_seat_handle_down(void * /*data*/,
+ struct wl_touch * /*wl_touch*/,
+ uint32_t /*serial*/,
+ uint32_t /*time*/,
+ struct wl_surface * /*wl_surface*/,
+ int32_t /*id*/,
+ wl_fixed_t /*x*/,
+ wl_fixed_t /*y*/)
+{
+ CLOG_INFO(LOG, 2, "down");
+}
+
+static void touch_seat_handle_up(void * /*data*/,
+ struct wl_touch * /*wl_touch*/,
+ uint32_t /*serial*/,
+ uint32_t /*time*/,
+ int32_t /*id*/)
+{
+ CLOG_INFO(LOG, 2, "up");
+}
+
+static void touch_seat_handle_motion(void * /*data*/,
+ struct wl_touch * /*wl_touch*/,
+ uint32_t /*time*/,
+ int32_t /*id*/,
+ wl_fixed_t /*x*/,
+ wl_fixed_t /*y*/)
+{
+ CLOG_INFO(LOG, 2, "motion");
+}
+
+static void touch_seat_handle_frame(void * /*data*/, struct wl_touch * /*wl_touch*/)
+{
+ CLOG_INFO(LOG, 2, "frame");
+}
+
+static void touch_seat_handle_cancel(void * /*data*/, struct wl_touch * /*wl_touch*/)
+{
+
+ CLOG_INFO(LOG, 2, "cancel");
+}
+
+static void touch_seat_handle_shape(void * /*data*/,
+ struct wl_touch * /*touch*/,
+ int32_t /*id*/,
+ wl_fixed_t /*major*/,
+ wl_fixed_t /*minor*/)
+{
+ CLOG_INFO(LOG, 2, "shape");
+}
+
+static void touch_seat_handle_orientation(void * /*data*/,
+ struct wl_touch * /*touch*/,
+ int32_t /*id*/,
+ wl_fixed_t /*orientation*/)
+{
+ CLOG_INFO(LOG, 2, "orientation");
+}
+
+static const struct wl_touch_listener touch_seat_listener = {
+ touch_seat_handle_down,
+ touch_seat_handle_up,
+ touch_seat_handle_motion,
+ touch_seat_handle_frame,
+ touch_seat_handle_cancel,
+ touch_seat_handle_shape,
+ touch_seat_handle_orientation,
+};
+
+#undef LOG
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Listener (Tablet Tool), #zwp_tablet_tool_v2_listener
* \{ */
@@ -1983,11 +2978,13 @@ static void tablet_tool_handle_proximity_in(void *data,
GWL_Seat *seat = tablet_tool->seat;
seat->cursor_source_serial = serial;
- seat->tablet.wl_surface = wl_surface;
+ seat->tablet.wl_surface_window = wl_surface;
seat->tablet.serial = serial;
seat->data_source_serial = serial;
+ seat->system->seat_active_set(seat);
+
/* Update #GHOST_TabletData. */
GHOST_TabletData &td = tablet_tool->data;
/* Reset, to avoid using stale tilt/pressure. */
@@ -1996,7 +2993,7 @@ static void tablet_tool_handle_proximity_in(void *data,
/* In case pressure isn't supported. */
td.Pressure = 1.0f;
- GHOST_WindowWayland *win = ghost_wl_surface_user_data(seat->tablet.wl_surface);
+ GHOST_WindowWayland *win = ghost_wl_surface_user_data(seat->tablet.wl_surface_window);
win->activate();
@@ -2026,7 +3023,7 @@ static void tablet_tool_handle_down(void *data,
seat->data_source_serial = serial;
seat->tablet.buttons.set(ebutton, true);
- if (wl_surface *wl_surface_focus = seat->tablet.wl_surface) {
+ if (wl_surface *wl_surface_focus = seat->tablet.wl_surface_window) {
GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
seat->system->pushEvent(new GHOST_EventButton(
seat->system->getMilliSeconds(), etype, win, ebutton, tablet_tool->data));
@@ -2044,7 +3041,7 @@ static void tablet_tool_handle_up(void *data, struct zwp_tablet_tool_v2 * /*zwp_
seat->tablet.buttons.set(ebutton, false);
- if (wl_surface *wl_surface_focus = seat->tablet.wl_surface) {
+ if (wl_surface *wl_surface_focus = seat->tablet.wl_surface_window) {
GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
seat->system->pushEvent(new GHOST_EventButton(
seat->system->getMilliSeconds(), etype, win, ebutton, tablet_tool->data));
@@ -2129,7 +3126,7 @@ static void tablet_tool_handle_wheel(void *data,
GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(data);
GWL_Seat *seat = tablet_tool->seat;
- if (wl_surface *wl_surface_focus = seat->tablet.wl_surface) {
+ if (wl_surface *wl_surface_focus = seat->tablet.wl_surface_window) {
GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
seat->system->pushEvent(new GHOST_EventWheel(seat->system->getMilliSeconds(), win, clicks));
}
@@ -2158,10 +3155,10 @@ static void tablet_tool_handle_button(void *data,
GHOST_TButton ebutton = GHOST_kButtonMaskLeft;
switch (button) {
case BTN_STYLUS:
- ebutton = GHOST_kButtonMaskRight;
+ ebutton = GHOST_kButtonMaskMiddle;
break;
case BTN_STYLUS2:
- ebutton = GHOST_kButtonMaskMiddle;
+ ebutton = GHOST_kButtonMaskRight;
break;
case BTN_STYLUS3:
ebutton = GHOST_kButtonMaskButton4;
@@ -2171,7 +3168,7 @@ static void tablet_tool_handle_button(void *data,
seat->data_source_serial = serial;
seat->tablet.buttons.set(ebutton, state == WL_POINTER_BUTTON_STATE_PRESSED);
- if (wl_surface *wl_surface_focus = seat->tablet.wl_surface) {
+ if (wl_surface *wl_surface_focus = seat->tablet.wl_surface_window) {
GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
seat->system->pushEvent(new GHOST_EventButton(
seat->system->getMilliSeconds(), etype, win, ebutton, tablet_tool->data));
@@ -2187,7 +3184,7 @@ static void tablet_tool_handle_frame(void *data,
GWL_Seat *seat = tablet_tool->seat;
/* No need to check the surfaces origin, it's already known to be owned by GHOST. */
- if (wl_surface *wl_surface_focus = seat->tablet.wl_surface) {
+ if (wl_surface *wl_surface_focus = seat->tablet.wl_surface_window) {
GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
const wl_fixed_t scale = win->scale();
seat->system->pushEvent(new GHOST_EventCursor(seat->system->getMilliSeconds(),
@@ -2202,7 +3199,7 @@ static void tablet_tool_handle_frame(void *data,
}
if (tablet_tool->proximity == false) {
- seat->tablet.wl_surface = nullptr;
+ seat->tablet.wl_surface_window = nullptr;
}
}
@@ -2330,9 +3327,23 @@ static void keyboard_handle_keymap(void *data,
xkb_state_unref(seat->xkb_state_empty);
seat->xkb_state_empty = xkb_state_new(keymap);
+ for (int i = 0; i < MOD_INDEX_NUM; i++) {
+ const GWL_ModifierInfo &mod_info = g_modifier_info_table[i];
+ seat->xkb_keymap_mod_index[i] = xkb_keymap_mod_get_index(keymap, mod_info.xkb_id);
+ }
+
+ xkb_state_unref(seat->xkb_state_empty_with_shift);
+ seat->xkb_state_empty_with_shift = nullptr;
+ {
+ const xkb_mod_index_t mod_shift = seat->xkb_keymap_mod_index[MOD_INDEX_SHIFT];
+ if (mod_shift != XKB_MOD_INVALID) {
+ seat->xkb_state_empty_with_shift = xkb_state_new(keymap);
+ xkb_state_update_mask(seat->xkb_state_empty_with_shift, (1 << mod_shift), 0, 0, 0, 0, 0);
+ }
+ }
+
xkb_state_unref(seat->xkb_state_empty_with_numlock);
seat->xkb_state_empty_with_numlock = nullptr;
-
{
const xkb_mod_index_t mod2 = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_NUM);
const xkb_mod_index_t num = xkb_keymap_mod_get_index(keymap, "NumLock");
@@ -2343,10 +3354,21 @@ static void keyboard_handle_keymap(void *data,
}
}
- for (int i = 0; i < MOD_INDEX_NUM; i++) {
- const GWL_ModifierInfo &mod_info = g_modifier_info_table[i];
- seat->xkb_keymap_mod_index[i] = xkb_keymap_mod_get_index(keymap, mod_info.xkb_id);
+#ifdef USE_NON_LATIN_KB_WORKAROUND
+ seat->xkb_use_non_latin_workaround = false;
+ if (seat->xkb_state_empty_with_shift) {
+ seat->xkb_use_non_latin_workaround = true;
+ for (xkb_keycode_t key_code = KEY_1 + EVDEV_OFFSET; key_code <= KEY_0 + EVDEV_OFFSET;
+ key_code++) {
+ const xkb_keysym_t sym_test = xkb_state_key_get_one_sym(seat->xkb_state_empty_with_shift,
+ key_code);
+ if (!(sym_test >= XKB_KEY_0 && sym_test <= XKB_KEY_9)) {
+ seat->xkb_use_non_latin_workaround = false;
+ break;
+ }
+ }
}
+#endif
keyboard_depressed_state_reset(seat);
@@ -2372,12 +3394,14 @@ static void keyboard_handle_enter(void *data,
GWL_Seat *seat = static_cast<GWL_Seat *>(data);
seat->keyboard.serial = serial;
- seat->keyboard.wl_surface = wl_surface;
+ seat->keyboard.wl_surface_window = wl_surface;
+
+ seat->system->seat_active_set(seat);
/* If there are any keys held when activating the window,
* modifiers will be compared against the seat state,
* only enabling modifiers that were previously disabled. */
- WGL_KeyboardDepressedState key_depressed_prev = seat->key_depressed;
+ GWL_KeyboardDepressedState key_depressed_prev = seat->key_depressed;
keyboard_depressed_state_reset(seat);
uint32_t *key;
@@ -2415,7 +3439,7 @@ static void keyboard_handle_leave(void *data,
CLOG_INFO(LOG, 2, "leave");
GWL_Seat *seat = static_cast<GWL_Seat *>(data);
- seat->keyboard.wl_surface = nullptr;
+ seat->keyboard.wl_surface_window = nullptr;
/* Losing focus must stop repeating text. */
if (seat->key_repeat.timer) {
@@ -2435,6 +3459,8 @@ static void keyboard_handle_leave(void *data,
static xkb_keysym_t xkb_state_key_get_one_sym_without_modifiers(
struct xkb_state *xkb_state_empty,
struct xkb_state *xkb_state_empty_with_numlock,
+ struct xkb_state *xkb_state_empty_with_shift,
+ const bool xkb_use_non_latin_workaround,
const xkb_keycode_t key)
{
/* Use an empty keyboard state to access key symbol without modifiers. */
@@ -2448,11 +3474,30 @@ static xkb_keysym_t xkb_state_key_get_one_sym_without_modifiers(
/* Accounts for key-pad keys typically swapped for numbers when number-lock is enabled:
* `Home Left Up Right Down Prior Page_Up Next Page_Dow End Begin Insert Delete`. */
- if (xkb_state_empty_with_numlock && (sym >= XKB_KEY_KP_Home && sym <= XKB_KEY_KP_Delete)) {
- const xkb_keysym_t sym_test = xkb_state_key_get_one_sym(xkb_state_empty_with_numlock, key);
- if (sym_test != XKB_KEY_NoSymbol) {
- sym = sym_test;
+ if (sym >= XKB_KEY_KP_Home && sym <= XKB_KEY_KP_Delete) {
+ if (xkb_state_empty_with_numlock) {
+ const xkb_keysym_t sym_test = xkb_state_key_get_one_sym(xkb_state_empty_with_numlock, key);
+ if (sym_test != XKB_KEY_NoSymbol) {
+ sym = sym_test;
+ }
+ }
+ }
+ else {
+#ifdef USE_NON_LATIN_KB_WORKAROUND
+ if (key >= (KEY_1 + EVDEV_OFFSET) && key <= (KEY_0 + EVDEV_OFFSET)) {
+ if (xkb_state_empty_with_shift && xkb_use_non_latin_workaround) {
+ const xkb_keysym_t sym_test = xkb_state_key_get_one_sym(xkb_state_empty_with_shift, key);
+ if (sym_test != XKB_KEY_NoSymbol) {
+ /* Should never happen as enabling `xkb_use_non_latin_workaround` checks this. */
+ GHOST_ASSERT(sym_test >= XKB_KEY_0 && sym_test <= XKB_KEY_9, "Unexpected key");
+ sym = sym_test;
+ }
+ }
}
+#else
+ (void)xkb_state_empty_with_shift;
+ (void)xkb_use_non_latin_workaround;
+#endif
}
return sym;
@@ -2495,7 +3540,15 @@ static void keyboard_handle_key(void *data,
const xkb_keycode_t key_code = key + EVDEV_OFFSET;
const xkb_keysym_t sym = xkb_state_key_get_one_sym_without_modifiers(
- seat->xkb_state_empty, seat->xkb_state_empty_with_numlock, key_code);
+ seat->xkb_state_empty,
+ seat->xkb_state_empty_with_numlock,
+ seat->xkb_state_empty_with_shift,
+#ifdef USE_NON_LATIN_KB_WORKAROUND
+ seat->xkb_use_non_latin_workaround,
+#else
+ false,
+#endif
+ key_code);
if (sym == XKB_KEY_NoSymbol) {
CLOG_INFO(LOG, 2, "key (code=%d, state=%u, no symbol, skipped)", int(key_code), state);
return;
@@ -2576,7 +3629,7 @@ static void keyboard_handle_key(void *data,
keyboard_depressed_state_key_event(seat, gkey, etype);
- if (wl_surface *wl_surface_focus = seat->keyboard.wl_surface) {
+ if (wl_surface *wl_surface_focus = seat->keyboard.wl_surface_window) {
GHOST_IWindow *win = ghost_wl_surface_user_data(wl_surface_focus);
seat->system->pushEvent(
new GHOST_EventKey(seat->system->getMilliSeconds(), etype, win, gkey, false, utf8_buf));
@@ -2587,11 +3640,10 @@ static void keyboard_handle_key(void *data,
/* Start timer for repeating key, if applicable. */
if ((seat->key_repeat.rate > 0) && (etype == GHOST_kEventKeyDown) &&
xkb_keymap_key_repeats(xkb_state_get_keymap(seat->xkb_state), key_code)) {
- key_repeat_payload = new GWL_KeyRepeatPlayload({
- .seat = seat,
- .key_code = key_code,
- .key_data = {.gkey = gkey},
- });
+ key_repeat_payload = new GWL_KeyRepeatPlayload();
+ key_repeat_payload->seat = seat;
+ key_repeat_payload->key_code = key_code;
+ key_repeat_payload->key_data.gkey = gkey;
}
}
@@ -2601,7 +3653,7 @@ static void keyboard_handle_key(void *data,
task->getUserData());
GWL_Seat *seat = payload->seat;
- if (wl_surface *wl_surface_focus = seat->keyboard.wl_surface) {
+ if (wl_surface *wl_surface_focus = seat->keyboard.wl_surface_window) {
GHOST_IWindow *win = ghost_wl_surface_user_data(wl_surface_focus);
GHOST_SystemWayland *system = seat->system;
/* Calculate this value every time in case modifier keys are pressed. */
@@ -2681,12 +3733,285 @@ static const struct wl_keyboard_listener keyboard_listener = {
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Listener (Primary Selection Offer), #zwp_primary_selection_offer_v1_listener
+ * \{ */
+
+static CLG_LogRef LOG_WL_PRIMARY_SELECTION_OFFER = {"ghost.wl.handle.primary_selection_offer"};
+#define LOG (&LOG_WL_PRIMARY_SELECTION_OFFER)
+
+static void primary_selection_offer_offer(void *data,
+ struct zwp_primary_selection_offer_v1 *id,
+ const char *type)
+{
+ GWL_PrimarySelection_DataOffer *data_offer = static_cast<GWL_PrimarySelection_DataOffer *>(data);
+ if (data_offer->id != id) {
+ CLOG_INFO(LOG, 2, "offer: %p: offer for unknown selection %p of %s (skipped)", data, id, type);
+ return;
+ }
+
+ data_offer->types.insert(std::string(type));
+}
+
+static const struct zwp_primary_selection_offer_v1_listener primary_selection_offer_listener = {
+ primary_selection_offer_offer,
+};
+
+#undef LOG
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (Primary Selection Device), #zwp_primary_selection_device_v1_listener
+ * \{ */
+
+static CLG_LogRef LOG_WL_PRIMARY_SELECTION_DEVICE = {"ghost.wl.handle.primary_selection_device"};
+#define LOG (&LOG_WL_PRIMARY_SELECTION_DEVICE)
+
+static void primary_selection_device_handle_data_offer(
+ void * /*data*/,
+ struct zwp_primary_selection_device_v1 * /*zwp_primary_selection_device_v1*/,
+ struct zwp_primary_selection_offer_v1 *id)
+{
+ CLOG_INFO(LOG, 2, "data_offer");
+
+ GWL_PrimarySelection_DataOffer *data_offer = new GWL_PrimarySelection_DataOffer;
+ data_offer->id = id;
+ zwp_primary_selection_offer_v1_add_listener(id, &primary_selection_offer_listener, data_offer);
+}
+
+static void primary_selection_device_handle_selection(
+ void *data,
+ struct zwp_primary_selection_device_v1 * /*zwp_primary_selection_device_v1*/,
+ struct zwp_primary_selection_offer_v1 *id)
+{
+ GWL_PrimarySelection *primary = static_cast<GWL_PrimarySelection *>(data);
+
+ std::lock_guard lock{primary->data_offer_mutex};
+
+ /* Delete old data offer. */
+ if (primary->data_offer != nullptr) {
+ gwl_primary_selection_discard_offer(primary);
+ }
+
+ if (id == nullptr) {
+ CLOG_INFO(LOG, 2, "selection: (skipped)");
+ return;
+ }
+ CLOG_INFO(LOG, 2, "selection");
+ /* Get new data offer. */
+ GWL_PrimarySelection_DataOffer *data_offer = static_cast<GWL_PrimarySelection_DataOffer *>(
+ zwp_primary_selection_offer_v1_get_user_data(id));
+ primary->data_offer = data_offer;
+}
+
+static const struct zwp_primary_selection_device_v1_listener primary_selection_device_listener = {
+ primary_selection_device_handle_data_offer,
+ primary_selection_device_handle_selection,
+};
+
+#undef LOG
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (Primary Selection Source), #zwp_primary_selection_source_v1_listener
+ * \{ */
+
+static CLG_LogRef LOG_WL_PRIMARY_SELECTION_SOURCE = {"ghost.wl.handle.primary_selection_source"};
+#define LOG (&LOG_WL_PRIMARY_SELECTION_SOURCE)
+
+static void primary_selection_source_send(void *data,
+ struct zwp_primary_selection_source_v1 * /*source*/,
+ const char * /*mime_type*/,
+ int32_t fd)
+{
+ CLOG_INFO(LOG, 2, "send");
+
+ GWL_PrimarySelection *primary = static_cast<GWL_PrimarySelection *>(data);
+
+ auto write_file_fn = [](GWL_PrimarySelection *primary, const int fd) {
+ if (UNLIKELY(write(fd,
+ primary->data_source->buffer_out.data,
+ primary->data_source->buffer_out.data_size) < 0)) {
+ CLOG_WARN(LOG, "error writing to primary clipboard: %s", std::strerror(errno));
+ }
+ close(fd);
+ primary->data_source_mutex.unlock();
+ };
+
+ primary->data_source_mutex.lock();
+ std::thread write_thread(write_file_fn, primary, fd);
+ write_thread.detach();
+}
+
+static void primary_selection_source_cancelled(void *data,
+ struct zwp_primary_selection_source_v1 *source)
+{
+ CLOG_INFO(LOG, 2, "cancelled");
+
+ GWL_PrimarySelection *primary = static_cast<GWL_PrimarySelection *>(data);
+
+ if (source == primary->data_source->wp_source) {
+ gwl_primary_selection_discard_source(primary);
+ }
+}
+
+static const struct zwp_primary_selection_source_v1_listener primary_selection_source_listener = {
+ primary_selection_source_send,
+ primary_selection_source_cancelled,
+};
+
+#undef LOG
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Listener (Seat), #wl_seat_listener
* \{ */
static CLG_LogRef LOG_WL_SEAT = {"ghost.wl.handle.seat"};
#define LOG (&LOG_WL_SEAT)
+static void gwl_seat_capability_pointer_enable(GWL_Seat *seat)
+{
+ if (seat->wl_pointer) {
+ return;
+ }
+ seat->wl_pointer = wl_seat_get_pointer(seat->wl_seat);
+ seat->cursor.wl_surface_cursor = wl_compositor_create_surface(seat->system->wl_compositor());
+ seat->cursor.visible = true;
+ seat->cursor.wl_buffer = nullptr;
+ if (!get_cursor_settings(seat->cursor.theme_name, seat->cursor.theme_size)) {
+ seat->cursor.theme_name = std::string();
+ seat->cursor.theme_size = default_cursor_size;
+ }
+ wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, seat);
+
+ wl_surface_add_listener(seat->cursor.wl_surface_cursor, &cursor_surface_listener, seat);
+ ghost_wl_surface_tag_cursor_pointer(seat->cursor.wl_surface_cursor);
+
+ zwp_pointer_gestures_v1 *pointer_gestures = seat->system->wp_pointer_gestures();
+ if (pointer_gestures) {
+#ifdef ZWP_POINTER_GESTURE_HOLD_V1_INTERFACE
+ { /* Hold gesture. */
+ struct zwp_pointer_gesture_hold_v1 *gesture = zwp_pointer_gestures_v1_get_hold_gesture(
+ pointer_gestures, seat->wl_pointer);
+ zwp_pointer_gesture_hold_v1_set_user_data(gesture, seat);
+ zwp_pointer_gesture_hold_v1_add_listener(gesture, &gesture_hold_listener, seat);
+ seat->wp_pointer_gesture_hold = gesture;
+ }
+#endif
+#ifdef ZWP_POINTER_GESTURE_PINCH_V1_INTERFACE
+ { /* Pinch gesture. */
+ struct zwp_pointer_gesture_pinch_v1 *gesture = zwp_pointer_gestures_v1_get_pinch_gesture(
+ pointer_gestures, seat->wl_pointer);
+ zwp_pointer_gesture_pinch_v1_set_user_data(gesture, seat);
+ zwp_pointer_gesture_pinch_v1_add_listener(gesture, &gesture_pinch_listener, seat);
+ seat->wp_pointer_gesture_pinch = gesture;
+ }
+#endif
+#ifdef ZWP_POINTER_GESTURE_SWIPE_V1_INTERFACE
+ { /* Swipe gesture. */
+ struct zwp_pointer_gesture_swipe_v1 *gesture = zwp_pointer_gestures_v1_get_swipe_gesture(
+ pointer_gestures, seat->wl_pointer);
+ zwp_pointer_gesture_swipe_v1_set_user_data(gesture, seat);
+ zwp_pointer_gesture_swipe_v1_add_listener(gesture, &gesture_swipe_listener, seat);
+ seat->wp_pointer_gesture_swipe = gesture;
+ }
+#endif
+ }
+}
+
+static void gwl_seat_capability_pointer_disable(GWL_Seat *seat)
+{
+ if (!seat->wl_pointer) {
+ return;
+ }
+
+ zwp_pointer_gestures_v1 *pointer_gestures = seat->system->wp_pointer_gestures();
+ if (pointer_gestures) {
+#ifdef ZWP_POINTER_GESTURE_HOLD_V1_INTERFACE
+ { /* Hold gesture. */
+ struct zwp_pointer_gesture_hold_v1 **gesture_p = &seat->wp_pointer_gesture_hold;
+ if (*gesture_p) {
+ zwp_pointer_gesture_hold_v1_destroy(*gesture_p);
+ *gesture_p = nullptr;
+ }
+ }
+#endif
+#ifdef ZWP_POINTER_GESTURE_PINCH_V1_INTERFACE
+ { /* Pinch gesture. */
+ struct zwp_pointer_gesture_pinch_v1 **gesture_p = &seat->wp_pointer_gesture_pinch;
+ if (*gesture_p) {
+ zwp_pointer_gesture_pinch_v1_destroy(*gesture_p);
+ *gesture_p = nullptr;
+ }
+ }
+#endif
+#ifdef ZWP_POINTER_GESTURE_SWIPE_V1_INTERFACE
+ { /* Swipe gesture. */
+ struct zwp_pointer_gesture_swipe_v1 **gesture_p = &seat->wp_pointer_gesture_swipe;
+ if (*gesture_p) {
+ zwp_pointer_gesture_swipe_v1_destroy(*gesture_p);
+ *gesture_p = nullptr;
+ }
+ }
+#endif
+ }
+
+ if (seat->cursor.wl_surface_cursor) {
+ wl_surface_destroy(seat->cursor.wl_surface_cursor);
+ seat->cursor.wl_surface_cursor = nullptr;
+ }
+ if (seat->cursor.wl_theme) {
+ wl_cursor_theme_destroy(seat->cursor.wl_theme);
+ seat->cursor.wl_theme = nullptr;
+ }
+
+ wl_pointer_destroy(seat->wl_pointer);
+ seat->wl_pointer = nullptr;
+}
+
+static void gwl_seat_capability_keyboard_enable(GWL_Seat *seat)
+{
+ if (seat->wl_keyboard) {
+ return;
+ }
+ seat->wl_keyboard = wl_seat_get_keyboard(seat->wl_seat);
+ wl_keyboard_add_listener(seat->wl_keyboard, &keyboard_listener, seat);
+}
+
+static void gwl_seat_capability_keyboard_disable(GWL_Seat *seat)
+{
+ if (!seat->wl_keyboard) {
+ return;
+ }
+ if (seat->key_repeat.timer) {
+ keyboard_handle_key_repeat_cancel(seat);
+ }
+ wl_keyboard_destroy(seat->wl_keyboard);
+ seat->wl_keyboard = nullptr;
+}
+
+static void gwl_seat_capability_touch_enable(GWL_Seat *seat)
+{
+ if (seat->wl_touch) {
+ return;
+ }
+ seat->wl_touch = wl_seat_get_touch(seat->wl_seat);
+ wl_touch_set_user_data(seat->wl_touch, seat);
+ wl_touch_add_listener(seat->wl_touch, &touch_seat_listener, seat);
+}
+
+static void gwl_seat_capability_touch_disable(GWL_Seat *seat)
+{
+ if (!seat->wl_touch) {
+ return;
+ }
+ wl_touch_destroy(seat->wl_touch);
+ seat->wl_touch = nullptr;
+}
+
static void seat_handle_capabilities(void *data,
struct wl_seat *wl_seat,
const uint32_t capabilities)
@@ -2699,27 +4024,27 @@ static void seat_handle_capabilities(void *data,
(capabilities & WL_SEAT_CAPABILITY_TOUCH) != 0);
GWL_Seat *seat = static_cast<GWL_Seat *>(data);
- seat->wl_pointer = nullptr;
- seat->wl_keyboard = nullptr;
+ GHOST_ASSERT(seat->wl_seat == wl_seat, "Seat mismatch");
if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
- seat->wl_pointer = wl_seat_get_pointer(wl_seat);
- seat->cursor.wl_surface = wl_compositor_create_surface(seat->system->wl_compositor());
- seat->cursor.visible = true;
- seat->cursor.wl_buffer = nullptr;
- if (!get_cursor_settings(seat->cursor.theme_name, seat->cursor.size)) {
- seat->cursor.theme_name = std::string();
- seat->cursor.size = default_cursor_size;
- }
- wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, data);
-
- wl_surface_add_listener(seat->cursor.wl_surface, &cursor_surface_listener, data);
- ghost_wl_surface_tag_cursor_pointer(seat->cursor.wl_surface);
+ gwl_seat_capability_pointer_enable(seat);
+ }
+ else {
+ gwl_seat_capability_pointer_disable(seat);
}
if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
- seat->wl_keyboard = wl_seat_get_keyboard(wl_seat);
- wl_keyboard_add_listener(seat->wl_keyboard, &keyboard_listener, data);
+ gwl_seat_capability_keyboard_enable(seat);
+ }
+ else {
+ gwl_seat_capability_keyboard_disable(seat);
+ }
+
+ if (capabilities & WL_SEAT_CAPABILITY_TOUCH) {
+ gwl_seat_capability_touch_enable(seat);
+ }
+ else {
+ gwl_seat_capability_touch_disable(seat);
}
}
@@ -2929,11 +4254,17 @@ static void output_handle_done(void *data, struct wl_output * /*wl_output*/)
static void output_handle_scale(void *data, struct wl_output * /*wl_output*/, const int32_t factor)
{
CLOG_INFO(LOG, 2, "scale");
- static_cast<GWL_Output *>(data)->scale = factor;
+ GWL_Output *output = static_cast<GWL_Output *>(data);
+ output->scale = factor;
+ GHOST_WindowManager *window_manager = output->system->getWindowManager();
if (window_manager) {
for (GHOST_IWindow *iwin : window_manager->getWindows()) {
GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(iwin);
+ const std::vector<GWL_Output *> &outputs = win->outputs();
+ if (std::find(outputs.begin(), outputs.end(), output) == outputs.cend()) {
+ continue;
+ }
win->outputs_changed_update_scale();
}
}
@@ -3011,89 +4342,573 @@ static struct libdecor_interface libdecor_interface = {
static CLG_LogRef LOG_WL_REGISTRY = {"ghost.wl.handle.registry"};
#define LOG (&LOG_WL_REGISTRY)
-static void global_handle_add(void *data,
- struct wl_registry *wl_registry,
- const uint32_t name,
- const char *interface,
- const uint32_t version)
+/* #GWL_Display.wl_compositor */
+
+static void gwl_registry_compositor_add(GWL_Display *display,
+ const GWL_RegisteryAdd_Params *params)
+{
+ display->wl_compositor = static_cast<wl_compositor *>(
+ wl_registry_bind(display->wl_registry, params->name, &wl_compositor_interface, 3));
+ gwl_registry_entry_add(display, params, nullptr);
+}
+static void gwl_registry_compositor_remove(GWL_Display *display,
+ void * /*user_data*/,
+ const bool /*on_exit*/)
+{
+ struct wl_compositor **value_p = &display->wl_compositor;
+ wl_compositor_destroy(*value_p);
+ *value_p = nullptr;
+}
+
+/* #GWL_Display.xdg_decor.shell */
+
+static void gwl_registry_xdg_wm_base_add(GWL_Display *display,
+ const GWL_RegisteryAdd_Params *params)
+{
+ GWL_XDG_Decor_System &decor = *display->xdg_decor;
+ decor.shell = static_cast<xdg_wm_base *>(
+ wl_registry_bind(display->wl_registry, params->name, &xdg_wm_base_interface, 1));
+ xdg_wm_base_add_listener(decor.shell, &shell_listener, nullptr);
+ decor.shell_name = params->name;
+ gwl_registry_entry_add(display, params, nullptr);
+}
+static void gwl_registry_xdg_wm_base_remove(GWL_Display *display,
+ void * /*user_data*/,
+ const bool /*on_exit*/)
+{
+ GWL_XDG_Decor_System &decor = *display->xdg_decor;
+ struct xdg_wm_base **value_p = &decor.shell;
+ uint32_t *name_p = &decor.shell_name;
+ xdg_wm_base_destroy(*value_p);
+ *value_p = nullptr;
+ *name_p = WL_NAME_UNSET;
+}
+
+/* #GWL_Display.xdg_decor.manager */
+
+static void gwl_registry_xdg_decoration_manager_add(GWL_Display *display,
+ const GWL_RegisteryAdd_Params *params)
+{
+ GWL_XDG_Decor_System &decor = *display->xdg_decor;
+ decor.manager = static_cast<zxdg_decoration_manager_v1 *>(wl_registry_bind(
+ display->wl_registry, params->name, &zxdg_decoration_manager_v1_interface, 1));
+ decor.manager_name = params->name;
+ gwl_registry_entry_add(display, params, nullptr);
+}
+static void gwl_registry_xdg_decoration_manager_remove(GWL_Display *display,
+ void * /*user_data*/,
+ const bool /*on_exit*/)
+{
+ GWL_XDG_Decor_System &decor = *display->xdg_decor;
+ struct zxdg_decoration_manager_v1 **value_p = &decor.manager;
+ uint32_t *name_p = &decor.manager_name;
+ zxdg_decoration_manager_v1_destroy(*value_p);
+ *value_p = nullptr;
+ *name_p = WL_NAME_UNSET;
+}
+
+/* #GWL_Display.xdg_output_manager */
+
+static void gwl_registry_xdg_output_manager_add(GWL_Display *display,
+ const GWL_RegisteryAdd_Params *params)
+{
+ display->xdg_output_manager = static_cast<zxdg_output_manager_v1 *>(
+ wl_registry_bind(display->wl_registry, params->name, &zxdg_output_manager_v1_interface, 2));
+ gwl_registry_entry_add(display, params, nullptr);
+}
+static void gwl_registry_xdg_output_manager_remove(GWL_Display *display,
+ void * /*user_data*/,
+ const bool /*on_exit*/)
+{
+ struct zxdg_output_manager_v1 **value_p = &display->xdg_output_manager;
+ zxdg_output_manager_v1_destroy(*value_p);
+ *value_p = nullptr;
+}
+
+/* #GWL_Display.wl_output */
+
+static void gwl_registry_wl_output_add(GWL_Display *display, const GWL_RegisteryAdd_Params *params)
+{
+ GWL_Output *output = new GWL_Output;
+ output->system = display->system;
+ output->wl_output = static_cast<wl_output *>(
+ wl_registry_bind(display->wl_registry, params->name, &wl_output_interface, 2));
+ ghost_wl_output_tag(output->wl_output);
+ wl_output_set_user_data(output->wl_output, output);
+
+ display->outputs.push_back(output);
+ wl_output_add_listener(output->wl_output, &output_listener, output);
+ gwl_registry_entry_add(display, params, static_cast<void *>(output));
+}
+static void gwl_registry_wl_output_update(GWL_Display *display,
+ const GWL_RegisteryUpdate_Params *params)
{
- /* Log last since it can be noted if the interface was handled or not. */
- bool found = true;
-
- struct GWL_Display *display = static_cast<struct GWL_Display *>(data);
- if (STREQ(interface, wl_compositor_interface.name)) {
- display->wl_compositor = static_cast<wl_compositor *>(
- wl_registry_bind(wl_registry, name, &wl_compositor_interface, 3));
- }
- else if (STREQ(interface, xdg_wm_base_interface.name)) {
- WGL_XDG_Decor_System &decor = *display->xdg_decor;
- decor.shell = static_cast<xdg_wm_base *>(
- wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, 1));
- xdg_wm_base_add_listener(decor.shell, &shell_listener, nullptr);
- }
- else if (STREQ(interface, zxdg_decoration_manager_v1_interface.name)) {
- WGL_XDG_Decor_System &decor = *display->xdg_decor;
- decor.manager = static_cast<zxdg_decoration_manager_v1 *>(
- wl_registry_bind(wl_registry, name, &zxdg_decoration_manager_v1_interface, 1));
- }
- else if (STREQ(interface, zxdg_output_manager_v1_interface.name)) {
- display->xdg_output_manager = static_cast<zxdg_output_manager_v1 *>(
- wl_registry_bind(wl_registry, name, &zxdg_output_manager_v1_interface, 2));
- for (GWL_Output *output : display->outputs) {
+ GWL_Output *output = static_cast<GWL_Output *>(params->user_data);
+ if (display->xdg_output_manager) {
+ if (output->xdg_output == nullptr) {
output->xdg_output = zxdg_output_manager_v1_get_xdg_output(display->xdg_output_manager,
output->wl_output);
zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output);
}
}
- else if (STREQ(interface, wl_output_interface.name)) {
- GWL_Output *output = new GWL_Output;
- output->wl_output = static_cast<wl_output *>(
- wl_registry_bind(wl_registry, name, &wl_output_interface, 2));
- ghost_wl_output_tag(output->wl_output);
- wl_output_set_user_data(output->wl_output, output);
+ else {
+ output->xdg_output = nullptr;
+ }
+}
+static void gwl_registry_wl_output_remove(GWL_Display *display,
+ void *user_data,
+ const bool /*on_exit*/)
+{
+ /* While windows & cursors hold references to outputs, there is no need to manually remove
+ * these references as the compositor will remove references via #wl_surface_listener.leave. */
+ GWL_Output *output = static_cast<GWL_Output *>(user_data);
+ wl_output_destroy(output->wl_output);
+ std::vector<GWL_Output *>::iterator iter = std::find(
+ display->outputs.begin(), display->outputs.end(), output);
+ const int index = (iter != display->outputs.cend()) ?
+ std::distance(display->outputs.begin(), iter) :
+ -1;
+ GHOST_ASSERT(index != -1, "invalid internal state");
+ /* NOTE: always erase even when `on_exit` because `output->xdg_output` is cleared later. */
+ display->outputs.erase(display->outputs.begin() + index);
+ delete output;
+}
+
+/* #GWL_Display.seats */
+
+static void gwl_registry_wl_seat_add(GWL_Display *display, const GWL_RegisteryAdd_Params *params)
+{
+ GWL_Seat *seat = new GWL_Seat;
+ seat->system = display->system;
+ seat->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+ seat->data_source = new GWL_DataSource;
+ seat->wl_seat = static_cast<wl_seat *>(
+ wl_registry_bind(display->wl_registry, params->name, &wl_seat_interface, 5));
+ display->seats.push_back(seat);
+ wl_seat_add_listener(seat->wl_seat, &seat_listener, seat);
+ gwl_registry_entry_add(display, params, static_cast<void *>(seat));
+}
+static void gwl_registry_wl_seat_update(GWL_Display *display,
+ const GWL_RegisteryUpdate_Params *params)
+{
+ GWL_Seat *seat = static_cast<GWL_Seat *>(params->user_data);
+
+ /* Register data device per seat for IPC between WAYLAND clients. */
+ if (display->wl_data_device_manager) {
+ if (seat->wl_data_device == nullptr) {
+ seat->wl_data_device = wl_data_device_manager_get_data_device(
+ display->wl_data_device_manager, seat->wl_seat);
+ wl_data_device_add_listener(seat->wl_data_device, &data_device_listener, seat);
+ }
+ }
+ else {
+ seat->wl_data_device = nullptr;
+ }
- display->outputs.push_back(output);
- wl_output_add_listener(output->wl_output, &output_listener, output);
+ if (display->wp_tablet_manager) {
+ if (seat->wp_tablet_seat == nullptr) {
+ seat->wp_tablet_seat = zwp_tablet_manager_v2_get_tablet_seat(display->wp_tablet_manager,
+ seat->wl_seat);
+ zwp_tablet_seat_v2_add_listener(seat->wp_tablet_seat, &tablet_seat_listener, seat);
+ }
+ }
+ else {
+ seat->wp_tablet_seat = nullptr;
+ }
- if (display->xdg_output_manager) {
- output->xdg_output = zxdg_output_manager_v1_get_xdg_output(display->xdg_output_manager,
- output->wl_output);
- zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output);
+ if (display->wp_primary_selection_device_manager) {
+ if (seat->wp_primary_selection_device == nullptr) {
+ seat->wp_primary_selection_device = zwp_primary_selection_device_manager_v1_get_device(
+ display->wp_primary_selection_device_manager, seat->wl_seat);
+
+ zwp_primary_selection_device_v1_add_listener(seat->wp_primary_selection_device,
+ &primary_selection_device_listener,
+ &seat->primary_selection);
+ }
+ }
+ else {
+ seat->wp_primary_selection_device = nullptr;
+ }
+}
+static void gwl_registry_wl_seat_remove(GWL_Display *display, void *user_data, const bool on_exit)
+{
+ GWL_Seat *seat = static_cast<GWL_Seat *>(user_data);
+
+ /* First handle members that require locking.
+ * While highly unlikely, it's possible they are being used while this function runs. */
+ {
+ std::lock_guard lock{seat->data_source_mutex};
+ if (seat->data_source) {
+ gwl_simple_buffer_free_data(&seat->data_source->buffer_out);
+ if (seat->data_source->wl_source) {
+ wl_data_source_destroy(seat->data_source->wl_source);
+ }
+ delete seat->data_source;
+ }
+ }
+
+ {
+ std::lock_guard lock{seat->data_offer_dnd_mutex};
+ if (seat->data_offer_dnd) {
+ wl_data_offer_destroy(seat->data_offer_dnd->id);
+ delete seat->data_offer_dnd;
+ }
+ }
+
+ {
+ std::lock_guard lock{seat->data_offer_copy_paste_mutex};
+ if (seat->data_offer_copy_paste) {
+ wl_data_offer_destroy(seat->data_offer_copy_paste->id);
+ delete seat->data_offer_copy_paste;
}
}
- else if (STREQ(interface, wl_seat_interface.name)) {
- GWL_Seat *seat = new GWL_Seat;
- seat->system = display->system;
- seat->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
- seat->data_source = new GWL_DataSource;
- seat->wl_seat = static_cast<wl_seat *>(
- wl_registry_bind(wl_registry, name, &wl_seat_interface, 5));
- display->seats.push_back(seat);
- wl_seat_add_listener(seat->wl_seat, &seat_listener, seat);
+
+ {
+ GWL_PrimarySelection *primary = &seat->primary_selection;
+ std::lock_guard lock{primary->data_offer_mutex};
+ gwl_primary_selection_discard_offer(primary);
}
- else if (STREQ(interface, wl_shm_interface.name)) {
- display->wl_shm = static_cast<wl_shm *>(
- wl_registry_bind(wl_registry, name, &wl_shm_interface, 1));
+
+ {
+ GWL_PrimarySelection *primary = &seat->primary_selection;
+ std::lock_guard lock{primary->data_source_mutex};
+ gwl_primary_selection_discard_source(primary);
}
- else if (STREQ(interface, wl_data_device_manager_interface.name)) {
- display->data_device_manager = static_cast<wl_data_device_manager *>(
- wl_registry_bind(wl_registry, name, &wl_data_device_manager_interface, 3));
+
+ if (seat->wp_primary_selection_device) {
+ zwp_primary_selection_device_v1_destroy(seat->wp_primary_selection_device);
}
- else if (STREQ(interface, zwp_tablet_manager_v2_interface.name)) {
- display->tablet_manager = static_cast<zwp_tablet_manager_v2 *>(
- wl_registry_bind(wl_registry, name, &zwp_tablet_manager_v2_interface, 1));
+
+ if (seat->wl_data_device) {
+ wl_data_device_release(seat->wl_data_device);
}
- else if (STREQ(interface, zwp_relative_pointer_manager_v1_interface.name)) {
- display->relative_pointer_manager = static_cast<zwp_relative_pointer_manager_v1 *>(
- wl_registry_bind(wl_registry, name, &zwp_relative_pointer_manager_v1_interface, 1));
+
+ if (seat->cursor.custom_data) {
+ munmap(seat->cursor.custom_data, seat->cursor.custom_data_size);
}
- else if (STREQ(interface, zwp_pointer_constraints_v1_interface.name)) {
- display->pointer_constraints = static_cast<zwp_pointer_constraints_v1 *>(
- wl_registry_bind(wl_registry, name, &zwp_pointer_constraints_v1_interface, 1));
+
+ /* Disable all capabilities as a way to free:
+ * - `seat.wl_pointer` (and related cursor variables).
+ * - `seat.wl_touch`.
+ * - `seat.wl_keyboard`.
+ */
+ gwl_seat_capability_pointer_disable(seat);
+ gwl_seat_capability_keyboard_disable(seat);
+ gwl_seat_capability_touch_disable(seat);
+
+ /* Un-referencing checks for NULL case. */
+ xkb_state_unref(seat->xkb_state);
+ xkb_state_unref(seat->xkb_state_empty);
+ xkb_state_unref(seat->xkb_state_empty_with_shift);
+ xkb_state_unref(seat->xkb_state_empty_with_numlock);
+
+ xkb_context_unref(seat->xkb_context);
+
+ /* Remove the seat. */
+ wl_seat_destroy(seat->wl_seat);
+
+ std::vector<GWL_Seat *>::iterator iter = std::find(
+ display->seats.begin(), display->seats.end(), seat);
+ const int index = (iter != display->seats.cend()) ? std::distance(display->seats.begin(), iter) :
+ -1;
+ GHOST_ASSERT(index != -1, "invalid internal state");
+
+ if (!on_exit) {
+ if (display->seats_active_index >= index) {
+ display->seats_active_index -= 1;
+ }
+ display->seats.erase(display->seats.begin() + index);
}
- else {
- found = false;
+ delete seat;
+}
+
+/* #GWL_Display.wl_shm */
+static void gwl_registry_wl_shm_add(GWL_Display *display, const GWL_RegisteryAdd_Params *params)
+{
+ display->wl_shm = static_cast<wl_shm *>(
+ wl_registry_bind(display->wl_registry, params->name, &wl_shm_interface, 1));
+ gwl_registry_entry_add(display, params, nullptr);
+}
+static void gwl_registry_wl_shm_remove(GWL_Display *display,
+ void * /*user_data*/,
+ const bool /*on_exit*/)
+{
+ struct wl_shm **value_p = &display->wl_shm;
+ wl_shm_destroy(*value_p);
+ *value_p = nullptr;
+}
+
+/* #GWL_Display.wl_data_device_manager */
+
+static void gwl_registry_wl_data_device_manager_add(GWL_Display *display,
+ const GWL_RegisteryAdd_Params *params)
+{
+ display->wl_data_device_manager = static_cast<wl_data_device_manager *>(
+ wl_registry_bind(display->wl_registry, params->name, &wl_data_device_manager_interface, 3));
+ gwl_registry_entry_add(display, params, nullptr);
+}
+static void gwl_registry_wl_data_device_manager_remove(GWL_Display *display,
+ void * /*user_data*/,
+ const bool /*on_exit*/)
+{
+ struct wl_data_device_manager **value_p = &display->wl_data_device_manager;
+ wl_data_device_manager_destroy(*value_p);
+ *value_p = nullptr;
+}
+
+/* #GWL_Display.wp_tablet_manager */
+
+static void gwl_registry_wp_tablet_manager_add(GWL_Display *display,
+ const GWL_RegisteryAdd_Params *params)
+{
+ display->wp_tablet_manager = static_cast<zwp_tablet_manager_v2 *>(
+ wl_registry_bind(display->wl_registry, params->name, &zwp_tablet_manager_v2_interface, 1));
+ gwl_registry_entry_add(display, params, nullptr);
+}
+static void gwl_registry_wp_tablet_manager_remove(GWL_Display *display,
+ void * /*user_data*/,
+ const bool /*on_exit*/)
+{
+ struct zwp_tablet_manager_v2 **value_p = &display->wp_tablet_manager;
+ zwp_tablet_manager_v2_destroy(*value_p);
+ *value_p = nullptr;
+}
+
+/* #GWL_Display.wp_relative_pointer_manager */
+
+static void gwl_registry_wp_relative_pointer_manager_add(GWL_Display *display,
+ const GWL_RegisteryAdd_Params *params)
+{
+ display->wp_relative_pointer_manager = static_cast<zwp_relative_pointer_manager_v1 *>(
+ wl_registry_bind(
+ display->wl_registry, params->name, &zwp_relative_pointer_manager_v1_interface, 1));
+ gwl_registry_entry_add(display, params, nullptr);
+}
+static void gwl_registry_wp_relative_pointer_manager_remove(GWL_Display *display,
+ void * /*user_data*/,
+ const bool /*on_exit*/)
+{
+ struct zwp_relative_pointer_manager_v1 **value_p = &display->wp_relative_pointer_manager;
+ zwp_relative_pointer_manager_v1_destroy(*value_p);
+ *value_p = nullptr;
+}
+
+/* #GWL_Display.wp_pointer_constraints */
+
+static void gwl_registry_wp_pointer_constraints_add(GWL_Display *display,
+ const GWL_RegisteryAdd_Params *params)
+{
+ display->wp_pointer_constraints = static_cast<zwp_pointer_constraints_v1 *>(wl_registry_bind(
+ display->wl_registry, params->name, &zwp_pointer_constraints_v1_interface, 1));
+ gwl_registry_entry_add(display, params, nullptr);
+}
+static void gwl_registry_wp_pointer_constraints_remove(GWL_Display *display,
+ void * /*user_data*/,
+ const bool /*on_exit*/)
+{
+ struct zwp_pointer_constraints_v1 **value_p = &display->wp_pointer_constraints;
+ zwp_pointer_constraints_v1_destroy(*value_p);
+ *value_p = nullptr;
+}
+
+/* #GWL_Display.wp_pointer_gestures */
+
+static void gwl_registry_wp_pointer_gestures_add(GWL_Display *display,
+ const GWL_RegisteryAdd_Params *params)
+{
+ display->wp_pointer_gestures = static_cast<zwp_pointer_gestures_v1 *>(
+ wl_registry_bind(display->wl_registry, params->name, &zwp_pointer_gestures_v1_interface, 3));
+ gwl_registry_entry_add(display, params, nullptr);
+}
+static void gwl_registry_wp_pointer_gestures_remove(GWL_Display *display,
+ void * /*user_data*/,
+ const bool /*on_exit*/)
+{
+ struct zwp_pointer_gestures_v1 **value_p = &display->wp_pointer_gestures;
+ zwp_pointer_gestures_v1_destroy(*value_p);
+ *value_p = nullptr;
+}
+
+/* #GWL_Display.wp_primary_selection_device_manager */
+
+static void gwl_registry_wp_primary_selection_device_manager_add(
+ struct GWL_Display *display, const GWL_RegisteryAdd_Params *params)
+{
+ display->wp_primary_selection_device_manager =
+ static_cast<zwp_primary_selection_device_manager_v1 *>(
+ wl_registry_bind(display->wl_registry,
+ params->name,
+ &zwp_primary_selection_device_manager_v1_interface,
+ 1));
+ gwl_registry_entry_add(display, params, nullptr);
+}
+static void gwl_registry_wp_primary_selection_device_manager_remove(GWL_Display *display,
+ void * /*user_data*/,
+ const bool /*on_exit*/)
+{
+ struct zwp_primary_selection_device_manager_v1 **value_p =
+ &display->wp_primary_selection_device_manager;
+ zwp_primary_selection_device_manager_v1_destroy(*value_p);
+ *value_p = nullptr;
+}
+
+/**
+ * Map interfaces to initialization functions.
+ *
+ * \note This list also defines the order interfaces are removed.
+ * On exit interface removal runs from last to first to avoid potential bugs
+ * caused by undefined order of removal.
+ *
+ * In general fundamental, low level objects such as the compositor and shared memory
+ * should be declared earlier and other interfaces that may use them should be declared later.
+ */
+static const GWL_RegistryHandler gwl_registry_handlers[] = {
+ /* Low level interfaces. */
+ {
+ &wl_compositor_interface.name,
+ gwl_registry_compositor_add,
+ nullptr,
+ gwl_registry_compositor_remove,
+ },
+ {
+ &wl_shm_interface.name,
+ gwl_registry_wl_shm_add,
+ nullptr,
+ gwl_registry_wl_shm_remove,
+ },
+ {
+ &xdg_wm_base_interface.name,
+ gwl_registry_xdg_wm_base_add,
+ nullptr,
+ gwl_registry_xdg_wm_base_remove,
+ },
+ /* Managers. */
+ {
+ &zxdg_decoration_manager_v1_interface.name,
+ gwl_registry_xdg_decoration_manager_add,
+ nullptr,
+ gwl_registry_xdg_decoration_manager_remove,
+ },
+ {
+ &zxdg_output_manager_v1_interface.name,
+ gwl_registry_xdg_output_manager_add,
+ nullptr,
+ gwl_registry_xdg_output_manager_remove,
+ },
+ {
+ &wl_data_device_manager_interface.name,
+ gwl_registry_wl_data_device_manager_add,
+ nullptr,
+ gwl_registry_wl_data_device_manager_remove,
+ },
+ {
+ &zwp_primary_selection_device_manager_v1_interface.name,
+ gwl_registry_wp_primary_selection_device_manager_add,
+ nullptr,
+ gwl_registry_wp_primary_selection_device_manager_remove,
+ },
+ {
+ &zwp_tablet_manager_v2_interface.name,
+ gwl_registry_wp_tablet_manager_add,
+ nullptr,
+ gwl_registry_wp_tablet_manager_remove,
+ },
+ {
+ &zwp_relative_pointer_manager_v1_interface.name,
+ gwl_registry_wp_relative_pointer_manager_add,
+ nullptr,
+ gwl_registry_wp_relative_pointer_manager_remove,
+ },
+ /* Higher level interfaces. */
+ {
+ &zwp_pointer_constraints_v1_interface.name,
+ gwl_registry_wp_pointer_constraints_add,
+ nullptr,
+ gwl_registry_wp_pointer_constraints_remove,
+ },
+ {
+ &zwp_pointer_gestures_v1_interface.name,
+ gwl_registry_wp_pointer_gestures_add,
+ nullptr,
+ gwl_registry_wp_pointer_gestures_remove,
+ },
+ /* Display outputs. */
+ {
+ &wl_output_interface.name,
+ gwl_registry_wl_output_add,
+ gwl_registry_wl_output_update,
+ gwl_registry_wl_output_remove,
+ },
+ /* Seats.
+ * Keep the seat near the end to ensure other types are created first.
+ * as the seat creates data based on other interfaces. */
+ {
+ &wl_seat_interface.name,
+ gwl_registry_wl_seat_add,
+ gwl_registry_wl_seat_update,
+ gwl_registry_wl_seat_remove,
+ },
+ {nullptr, nullptr, nullptr},
+};
+
+/**
+ * Workaround for `gwl_registry_handlers` order of declaration,
+ * preventing `ARRAY_SIZE(gwl_registry_handlers) - 1` being used.
+ */
+static int gwl_registry_handler_interface_slot_max()
+{
+ return ARRAY_SIZE(gwl_registry_handlers) - 1;
+}
+
+static int gwl_registry_handler_interface_slot_from_string(const char *interface)
+{
+ for (const GWL_RegistryHandler *handler = gwl_registry_handlers; handler->interface_p != nullptr;
+ handler++) {
+ if (STREQ(interface, *handler->interface_p)) {
+ return int(handler - gwl_registry_handlers);
+ }
+ }
+ return -1;
+}
+
+static const GWL_RegistryHandler *gwl_registry_handler_from_interface_slot(int interface_slot)
+{
+ GHOST_ASSERT(uint32_t(interface_slot) < uint32_t(gwl_registry_handler_interface_slot_max()),
+ "Index out of range");
+ return &gwl_registry_handlers[interface_slot];
+}
+
+static void global_handle_add(void *data,
+ [[maybe_unused]] struct wl_registry *wl_registry,
+ const uint32_t name,
+ const char *interface,
+ const uint32_t version)
+{
+ /* Log last since it's useful to know if the interface was handled or not. */
+ GWL_Display *display = static_cast<GWL_Display *>(data);
+ GHOST_ASSERT(display->wl_registry == wl_registry, "Registry argument must match!");
+
+ const int interface_slot = gwl_registry_handler_interface_slot_from_string(interface);
+ bool added = false;
+
+ if (interface_slot != -1) {
+ const GWL_RegistryHandler *handler = &gwl_registry_handlers[interface_slot];
+ const GWL_RegistryEntry *registry_entry_prev = display->registry_entry;
+
+ /* The interface name that is ensured not to be freed. */
+ GWL_RegisteryAdd_Params params = {
+ .name = name,
+ .interface_slot = interface_slot,
+ .version = version,
+ };
+
+ handler->add_fn(display, &params);
+
+ added = display->registry_entry != registry_entry_prev;
+ }
+ else {
+ /* Not found. */
#ifdef USE_GNOME_NEEDS_LIBDECOR_HACK
if (STRPREFIX(interface, "gtk_shell")) { /* `gtk_shell1` at time of writing. */
/* Only require `libdecor` when built with X11 support,
@@ -3106,10 +4921,18 @@ static void global_handle_add(void *data,
CLOG_INFO(LOG,
2,
"add %s(interface=%s, version=%u, name=%u)",
- found ? "" : "(skipped), ",
+ (interface_slot != -1) ? (added ? "" : "(found but not added)") : "(skipped), ",
interface,
version,
name);
+
+ /* Initialization avoids excessive calls by calling update after all have been initialized. */
+ if (added) {
+ if (display->registry_skip_update_all == false) {
+ /* See doc-string for rationale on updating all on add/removal. */
+ gwl_registry_entry_update_all(display, interface_slot);
+ }
+ }
}
/**
@@ -3121,11 +4944,28 @@ static void global_handle_add(void *data,
* name is no longer available. If the client bound to the global
* using the bind request, the client should now destroy that object.
*/
-static void global_handle_remove(void * /*data*/,
- struct wl_registry * /*wl_registry*/,
+static void global_handle_remove(void *data,
+ [[maybe_unused]] struct wl_registry *wl_registry,
const uint32_t name)
{
- CLOG_INFO(LOG, 2, "remove (name=%u)", name);
+ GWL_Display *display = static_cast<GWL_Display *>(data);
+ GHOST_ASSERT(display->wl_registry == wl_registry, "Registry argument must match!");
+
+ int interface_slot = 0;
+ const bool removed = gwl_registry_entry_remove_by_name(display, name, &interface_slot);
+
+ CLOG_INFO(LOG,
+ 2,
+ "remove (name=%u, interface=%s)",
+ name,
+ removed ? *gwl_registry_handlers[interface_slot].interface_p : "(unknown)");
+
+ if (removed) {
+ if (display->registry_skip_update_all == false) {
+ /* See doc-string for rationale on updating all on add/removal. */
+ gwl_registry_entry_update_all(display, interface_slot);
+ }
+ }
}
static const struct wl_registry_listener registry_listener = {
@@ -3143,7 +4983,8 @@ static const struct wl_registry_listener registry_listener = {
* WAYLAND specific implementation of the #GHOST_System interface.
* \{ */
-GHOST_SystemWayland::GHOST_SystemWayland() : GHOST_System(), display_(new GWL_Display)
+GHOST_SystemWayland::GHOST_SystemWayland(bool background)
+ : GHOST_System(), display_(new GWL_Display)
{
wl_log_set_handler_client(ghost_wayland_log_handler);
@@ -3151,25 +4992,41 @@ GHOST_SystemWayland::GHOST_SystemWayland() : GHOST_System(), display_(new GWL_Di
/* Connect to the Wayland server. */
display_->wl_display = wl_display_connect(nullptr);
if (!display_->wl_display) {
- display_destroy(display_);
+ gwl_display_destroy(display_);
throw std::runtime_error("Wayland: unable to connect to display!");
}
/* This may be removed later if decorations are required, needed as part of registration. */
- display_->xdg_decor = new WGL_XDG_Decor_System;
+ display_->xdg_decor = new GWL_XDG_Decor_System;
/* Register interfaces. */
- struct wl_registry *registry = wl_display_get_registry(display_->wl_display);
- wl_registry_add_listener(registry, &registry_listener, display_);
- /* Call callback for registry listener. */
- wl_display_roundtrip(display_->wl_display);
- /* Call callbacks for registered listeners. */
- wl_display_roundtrip(display_->wl_display);
- wl_registry_destroy(registry);
+ {
+ display_->registry_skip_update_all = true;
+ struct wl_registry *registry = wl_display_get_registry(display_->wl_display);
+ display_->wl_registry = registry;
+ wl_registry_add_listener(registry, &registry_listener, display_);
+ /* First round-trip to receive all registry objects. */
+ wl_display_roundtrip(display_->wl_display);
+ /* Second round-trip to receive all output events. */
+ wl_display_roundtrip(display_->wl_display);
+
+ /* Account for dependencies between interfaces. */
+ gwl_registry_entry_update_all(display_, -1);
+
+ display_->registry_skip_update_all = false;
+ }
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
+ /* Ignore windowing requirements when running in background mode,
+ * as it doesn't make sense to fall back to X11 because of windowing functionality
+ * in background mode, also LIBDECOR is crashing in background mode `blender -b -f 1`
+ * for e.g. while it could be fixed, requiring the library at all makes no sense . */
+ if (background) {
+ display_->libdecor_required = false;
+ }
+
if (display_->libdecor_required) {
- wgl_xdg_decor_system_destroy(display_->xdg_decor);
+ gwl_xdg_decor_system_destroy(display_, display_->xdg_decor);
display_->xdg_decor = nullptr;
if (!has_libdecor) {
@@ -3179,8 +5036,10 @@ GHOST_SystemWayland::GHOST_SystemWayland() : GHOST_System(), display_(new GWL_Di
"WAYLAND found but libdecor was not, install libdecor for Wayland support, "
"falling back to X11\n");
# endif
- display_destroy(display_);
+ gwl_display_destroy(display_);
throw std::runtime_error("Wayland: unable to find libdecor!");
+
+ use_libdecor = true;
}
}
else {
@@ -3190,45 +5049,28 @@ GHOST_SystemWayland::GHOST_SystemWayland() : GHOST_System(), display_(new GWL_Di
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
if (use_libdecor) {
- display_->libdecor = new WGL_LibDecor_System;
- WGL_LibDecor_System &decor = *display_->libdecor;
+ display_->libdecor = new GWL_LibDecor_System;
+ GWL_LibDecor_System &decor = *display_->libdecor;
decor.context = libdecor_new(display_->wl_display, &libdecor_interface);
if (!decor.context) {
- display_destroy(display_);
+ gwl_display_destroy(display_);
throw std::runtime_error("Wayland: unable to create window decorations!");
}
}
else
#endif
{
- WGL_XDG_Decor_System &decor = *display_->xdg_decor;
+ GWL_XDG_Decor_System &decor = *display_->xdg_decor;
if (!decor.shell) {
- display_destroy(display_);
+ gwl_display_destroy(display_);
throw std::runtime_error("Wayland: unable to access xdg_shell!");
}
}
-
- /* Register data device per seat for IPC between Wayland clients. */
- if (display_->data_device_manager) {
- for (GWL_Seat *seat : display_->seats) {
- seat->data_device = wl_data_device_manager_get_data_device(display_->data_device_manager,
- seat->wl_seat);
- wl_data_device_add_listener(seat->data_device, &data_device_listener, seat);
- }
- }
-
- if (display_->tablet_manager) {
- for (GWL_Seat *seat : display_->seats) {
- seat->tablet_seat = zwp_tablet_manager_v2_get_tablet_seat(display_->tablet_manager,
- seat->wl_seat);
- zwp_tablet_seat_v2_add_listener(seat->tablet_seat, &tablet_seat_listener, seat);
- }
- }
}
GHOST_SystemWayland::~GHOST_SystemWayland()
{
- display_destroy(display_);
+ gwl_display_destroy(display_);
}
GHOST_TSuccess GHOST_SystemWayland::init()
@@ -3283,12 +5125,11 @@ bool GHOST_SystemWayland::setConsoleWindowState(GHOST_TConsoleWindowState /*acti
GHOST_TSuccess GHOST_SystemWayland::getModifierKeys(GHOST_ModifierKeys &keys) const
{
- if (UNLIKELY(display_->seats.empty())) {
+ GWL_Seat *seat = gwl_display_seat_active_get(display_);
+ if (UNLIKELY(!seat)) {
return GHOST_kFailure;
}
- GWL_Seat *seat = display_->seats[0];
-
const xkb_mod_mask_t state = xkb_state_serialize_mods(seat->xkb_state, XKB_STATE_MODS_DEPRESSED);
bool show_warning = true;
@@ -3300,7 +5141,7 @@ GHOST_TSuccess GHOST_SystemWayland::getModifierKeys(GHOST_ModifierKeys &keys) co
}
#endif
- /* Use local #WGL_KeyboardDepressedState to check which key is pressed.
+ /* Use local #GWL_KeyboardDepressedState to check which key is pressed.
* Use XKB as the source of truth, if there is any discrepancy. */
for (int i = 0; i < MOD_INDEX_NUM; i++) {
if (UNLIKELY(seat->xkb_keymap_mod_index[i] == XKB_MOD_INVALID)) {
@@ -3345,11 +5186,11 @@ GHOST_TSuccess GHOST_SystemWayland::getModifierKeys(GHOST_ModifierKeys &keys) co
GHOST_TSuccess GHOST_SystemWayland::getButtons(GHOST_Buttons &buttons) const
{
- if (UNLIKELY(display_->seats.empty())) {
+ GWL_Seat *seat = gwl_display_seat_active_get(display_);
+ if (UNLIKELY(!seat)) {
return GHOST_kFailure;
}
- GWL_Seat *seat = display_->seats[0];
- GWL_SeatStatePointer *seat_state_pointer = seat_state_pointer_active(seat);
+ GWL_SeatStatePointer *seat_state_pointer = gwl_seat_state_pointer_active(seat);
if (!seat_state_pointer) {
return GHOST_kFailure;
}
@@ -3358,43 +5199,208 @@ GHOST_TSuccess GHOST_SystemWayland::getButtons(GHOST_Buttons &buttons) const
return GHOST_kSuccess;
}
-char *GHOST_SystemWayland::getClipboard(bool /*selection*/) const
+/**
+ * Return a mime type which is supported by GHOST and exists in `types`
+ * (defined by the data offer).
+ */
+static const char *system_clipboard_text_mime_type(
+ const std::unordered_set<std::string> &data_offer_types)
+{
+ const char *ghost_supported_types[] = {ghost_wl_mime_text_utf8, ghost_wl_mime_text_plain};
+ for (size_t i = 0; i < ARRAY_SIZE(ghost_supported_types); i++) {
+ if (data_offer_types.count(ghost_supported_types[i])) {
+ return ghost_supported_types[i];
+ }
+ }
+ return nullptr;
+}
+
+static char *system_clipboard_get_primary_selection(GWL_Display *display)
+{
+ GWL_Seat *seat = gwl_display_seat_active_get(display);
+ if (UNLIKELY(!seat)) {
+ return nullptr;
+ }
+ GWL_PrimarySelection *primary = &seat->primary_selection;
+ std::mutex &mutex = primary->data_offer_mutex;
+
+ mutex.lock();
+ bool mutex_locked = true;
+ char *data = nullptr;
+
+ GWL_PrimarySelection_DataOffer *data_offer = primary->data_offer;
+ if (data_offer != nullptr) {
+ const char *mime_receive = system_clipboard_text_mime_type(data_offer->types);
+ if (mime_receive) {
+ /* Receive the clipboard in a thread, performing round-trips while waiting.
+ * This is needed so pasting contents from our own `primary->data_source` doesn't hang. */
+ struct ThreadResult {
+ char *data = nullptr;
+ std::atomic<bool> done = false;
+ } thread_result;
+ auto read_clipboard_fn = [](GWL_PrimarySelection_DataOffer *data_offer,
+ const char *mime_receive,
+ std::mutex *mutex,
+ struct ThreadResult *thread_result) {
+ size_t data_len = 0;
+ thread_result->data = read_buffer_from_primary_selection_offer(
+ data_offer, mime_receive, mutex, true, &data_len);
+ thread_result->done = true;
+ };
+ std::thread read_thread(read_clipboard_fn, data_offer, mime_receive, &mutex, &thread_result);
+ read_thread.detach();
+
+ while (!thread_result.done) {
+ wl_display_roundtrip(display->wl_display);
+ }
+ data = thread_result.data;
+
+ /* Reading the data offer unlocks the mutex. */
+ mutex_locked = false;
+ }
+ }
+ if (mutex_locked) {
+ mutex.unlock();
+ }
+ return data;
+}
+
+static char *system_clipboard_get(GWL_Display *display)
+{
+ GWL_Seat *seat = gwl_display_seat_active_get(display);
+ if (UNLIKELY(!seat)) {
+ return nullptr;
+ }
+ std::mutex &mutex = seat->data_offer_copy_paste_mutex;
+
+ mutex.lock();
+ bool mutex_locked = true;
+ char *data = nullptr;
+
+ GWL_DataOffer *data_offer = seat->data_offer_copy_paste;
+ if (data_offer != nullptr) {
+ const char *mime_receive = system_clipboard_text_mime_type(data_offer->types);
+ if (mime_receive) {
+ /* Receive the clipboard in a thread, performing round-trips while waiting.
+ * This is needed so pasting contents from our own `seat->data_source` doesn't hang. */
+ struct ThreadResult {
+ char *data = nullptr;
+ std::atomic<bool> done = false;
+ } thread_result;
+ auto read_clipboard_fn = [](GWL_DataOffer *data_offer,
+ const char *mime_receive,
+ std::mutex *mutex,
+ struct ThreadResult *thread_result) {
+ size_t data_len = 0;
+ thread_result->data = read_buffer_from_data_offer(
+ data_offer, mime_receive, mutex, true, &data_len);
+ thread_result->done = true;
+ };
+ std::thread read_thread(read_clipboard_fn, data_offer, mime_receive, &mutex, &thread_result);
+ read_thread.detach();
+
+ while (!thread_result.done) {
+ wl_display_roundtrip(display->wl_display);
+ }
+ data = thread_result.data;
+
+ /* Reading the data offer unlocks the mutex. */
+ mutex_locked = false;
+ }
+ }
+ if (mutex_locked) {
+ mutex.unlock();
+ }
+ return data;
+}
+
+char *GHOST_SystemWayland::getClipboard(bool selection) const
{
- char *clipboard = static_cast<char *>(malloc(clipboard_.size() + 1));
- memcpy(clipboard, clipboard_.data(), clipboard_.size() + 1);
- return clipboard;
+ char *data = nullptr;
+ if (selection) {
+ data = system_clipboard_get_primary_selection(display_);
+ }
+ else {
+ data = system_clipboard_get(display_);
+ }
+ return data;
}
-void GHOST_SystemWayland::putClipboard(const char *buffer, bool /*selection*/) const
+static void system_clipboard_put_primary_selection(GWL_Display *display, const char *buffer)
{
- if (UNLIKELY(!display_->data_device_manager || display_->seats.empty())) {
+ if (!display->wp_primary_selection_device_manager) {
+ return;
+ }
+ GWL_Seat *seat = gwl_display_seat_active_get(display);
+ if (UNLIKELY(!seat)) {
return;
}
+ GWL_PrimarySelection *primary = &seat->primary_selection;
+
+ std::lock_guard lock{primary->data_source_mutex};
+
+ gwl_primary_selection_discard_source(primary);
+
+ GWL_PrimarySelection_DataSource *data_source = new GWL_PrimarySelection_DataSource;
+ primary->data_source = data_source;
+
+ /* Copy buffer. */
+ gwl_simple_buffer_set_from_string(&data_source->buffer_out, buffer);
+
+ data_source->wp_source = zwp_primary_selection_device_manager_v1_create_source(
+ display->wp_primary_selection_device_manager);
+
+ zwp_primary_selection_source_v1_add_listener(
+ data_source->wp_source, &primary_selection_source_listener, primary);
+
+ for (size_t i = 0; i < ARRAY_SIZE(ghost_wl_mime_send); i++) {
+ zwp_primary_selection_source_v1_offer(data_source->wp_source, ghost_wl_mime_send[i]);
+ }
- GWL_Seat *seat = display_->seats[0];
+ if (seat->wp_primary_selection_device) {
+ zwp_primary_selection_device_v1_set_selection(
+ seat->wp_primary_selection_device, data_source->wp_source, seat->data_source_serial);
+ }
+}
+static void system_clipboard_put(GWL_Display *display, const char *buffer)
+{
+ if (!display->wl_data_device_manager) {
+ return;
+ }
+ GWL_Seat *seat = gwl_display_seat_active_get(display);
+ if (UNLIKELY(!seat)) {
+ return;
+ }
std::lock_guard lock{seat->data_source_mutex};
GWL_DataSource *data_source = seat->data_source;
/* Copy buffer. */
- free(data_source->buffer_out);
- const size_t buffer_size = strlen(buffer) + 1;
- data_source->buffer_out = static_cast<char *>(malloc(buffer_size));
- std::memcpy(data_source->buffer_out, buffer, buffer_size);
+ gwl_simple_buffer_set_from_string(&data_source->buffer_out, buffer);
- data_source->data_source = wl_data_device_manager_create_data_source(
- display_->data_device_manager);
+ data_source->wl_source = wl_data_device_manager_create_data_source(
+ display->wl_data_device_manager);
- wl_data_source_add_listener(data_source->data_source, &data_source_listener, seat);
+ wl_data_source_add_listener(data_source->wl_source, &data_source_listener, seat);
- for (const std::string &type : mime_send) {
- wl_data_source_offer(data_source->data_source, type.c_str());
+ for (size_t i = 0; i < ARRAY_SIZE(ghost_wl_mime_send); i++) {
+ wl_data_source_offer(data_source->wl_source, ghost_wl_mime_send[i]);
}
- if (seat->data_device) {
+ if (seat->wl_data_device) {
wl_data_device_set_selection(
- seat->data_device, data_source->data_source, seat->data_source_serial);
+ seat->wl_data_device, data_source->wl_source, seat->data_source_serial);
+ }
+}
+
+void GHOST_SystemWayland::putClipboard(const char *buffer, bool selection) const
+{
+ if (selection) {
+ system_clipboard_put_primary_selection(display_, buffer);
+ }
+ else {
+ system_clipboard_put(display_, buffer);
}
}
@@ -3423,7 +5429,7 @@ static GHOST_TSuccess setCursorPositionClientRelative_impl(GWL_Seat *seat,
/* NOTE: WAYLAND doesn't support warping the cursor.
* However when grab is enabled, we already simulate a cursor location
* so that can be set to a new location. */
- if (!seat->relative_pointer) {
+ if (!seat->wp_relative_pointer) {
return GHOST_kFailure;
}
const wl_fixed_t scale = win->scale();
@@ -3442,12 +5448,12 @@ GHOST_TSuccess GHOST_SystemWayland::getCursorPositionClientRelative(const GHOST_
int32_t &x,
int32_t &y) const
{
- if (UNLIKELY(display_->seats.empty())) {
+ GWL_Seat *seat = gwl_display_seat_active_get(display_);
+ if (UNLIKELY(!seat)) {
return GHOST_kFailure;
}
- GWL_Seat *seat = display_->seats[0];
- GWL_SeatStatePointer *seat_state_pointer = seat_state_pointer_active(seat);
- if (!seat_state_pointer || !seat_state_pointer->wl_surface) {
+ GWL_SeatStatePointer *seat_state_pointer = gwl_seat_state_pointer_active(seat);
+ if (!seat_state_pointer || !seat_state_pointer->wl_surface_window) {
return GHOST_kFailure;
}
const GHOST_WindowWayland *win = static_cast<const GHOST_WindowWayland *>(window);
@@ -3458,26 +5464,26 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorPositionClientRelative(GHOST_IWindo
const int32_t x,
const int32_t y)
{
- if (UNLIKELY(display_->seats.empty())) {
+ GWL_Seat *seat = gwl_display_seat_active_get(display_);
+ if (UNLIKELY(!seat)) {
return GHOST_kFailure;
}
- GWL_Seat *seat = display_->seats[0];
GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(window);
return setCursorPositionClientRelative_impl(seat, win, x, y);
}
GHOST_TSuccess GHOST_SystemWayland::getCursorPosition(int32_t &x, int32_t &y) const
{
- if (UNLIKELY(display_->seats.empty())) {
+ GWL_Seat *seat = gwl_display_seat_active_get(display_);
+ if (UNLIKELY(!seat)) {
return GHOST_kFailure;
}
- GWL_Seat *seat = display_->seats[0];
- GWL_SeatStatePointer *seat_state_pointer = seat_state_pointer_active(seat);
+ GWL_SeatStatePointer *seat_state_pointer = gwl_seat_state_pointer_active(seat);
if (!seat_state_pointer) {
return GHOST_kFailure;
}
- if (wl_surface *wl_surface_focus = seat_state_pointer->wl_surface) {
+ if (wl_surface *wl_surface_focus = seat_state_pointer->wl_surface_window) {
GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
return getCursorPositionClientRelative_impl(seat_state_pointer, win, x, y);
}
@@ -3486,14 +5492,14 @@ GHOST_TSuccess GHOST_SystemWayland::getCursorPosition(int32_t &x, int32_t &y) co
GHOST_TSuccess GHOST_SystemWayland::setCursorPosition(const int32_t x, const int32_t y)
{
- if (UNLIKELY(display_->seats.empty())) {
+ GWL_Seat *seat = gwl_display_seat_active_get(display_);
+ if (UNLIKELY(!seat)) {
return GHOST_kFailure;
}
- GWL_Seat *seat = display_->seats[0];
/* Intentionally different from `getCursorPosition` which supports both tablet & pointer.
* In the case of setting the cursor location, tablets don't support this. */
- if (wl_surface *wl_surface_focus = seat->pointer.wl_surface) {
+ if (wl_surface *wl_surface_focus = seat->pointer.wl_surface_window) {
GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_focus);
return setCursorPositionClientRelative_impl(seat, win, x, y);
}
@@ -3616,17 +5622,12 @@ GHOST_IWindow *GHOST_SystemWayland::createWindow(const char *title,
const uint32_t width,
const uint32_t height,
const GHOST_TWindowState state,
- const GHOST_TDrawingContextType type,
const GHOST_GLSettings glSettings,
const bool exclusive,
const bool is_dialog,
const GHOST_IWindow *parentWindow)
{
/* Globally store pointer to window manager. */
- if (!window_manager) {
- window_manager = getWindowManager();
- }
-
GHOST_WindowWayland *window = new GHOST_WindowWayland(
this,
title,
@@ -3636,7 +5637,7 @@ GHOST_IWindow *GHOST_SystemWayland::createWindow(const char *title,
height,
state,
parentWindow,
- type,
+ glSettings.context_type,
is_dialog,
((glSettings.flags & GHOST_glStereoVisual) != 0),
exclusive);
@@ -3664,22 +5665,22 @@ GHOST_IWindow *GHOST_SystemWayland::createWindow(const char *title,
*/
static void cursor_buffer_show(const GWL_Seat *seat)
{
- const GWL_Cursor *c = &seat->cursor;
+ const GWL_Cursor *cursor = &seat->cursor;
if (seat->wl_pointer) {
- const int scale = c->is_custom ? c->custom_scale : seat->pointer.theme_scale;
- const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / scale;
- const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / scale;
+ const int scale = cursor->is_custom ? cursor->custom_scale : seat->pointer.theme_scale;
+ const int32_t hotspot_x = int32_t(cursor->wl_image.hotspot_x) / scale;
+ const int32_t hotspot_y = int32_t(cursor->wl_image.hotspot_y) / scale;
if (seat->wl_pointer) {
wl_pointer_set_cursor(
- seat->wl_pointer, seat->pointer.serial, c->wl_surface, hotspot_x, hotspot_y);
+ seat->wl_pointer, seat->pointer.serial, cursor->wl_surface_cursor, hotspot_x, hotspot_y);
}
}
if (!seat->tablet_tools.empty()) {
- const int scale = c->is_custom ? c->custom_scale : seat->tablet.theme_scale;
- const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / scale;
- const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / scale;
+ const int scale = cursor->is_custom ? cursor->custom_scale : seat->tablet.theme_scale;
+ const int32_t hotspot_x = int32_t(cursor->wl_image.hotspot_x) / scale;
+ const int32_t hotspot_y = int32_t(cursor->wl_image.hotspot_y) / scale;
for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : seat->tablet_tools) {
GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(
zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2));
@@ -3688,6 +5689,9 @@ static void cursor_buffer_show(const GWL_Seat *seat)
tablet_tool->wl_surface_cursor,
hotspot_x,
hotspot_y);
+#ifdef USE_KDE_TABLET_HIDDEN_CURSOR_HACK
+ wl_surface_commit(tablet_tool->wl_surface_cursor);
+#endif
}
}
}
@@ -3742,21 +5746,21 @@ static void cursor_buffer_set_surface_impl(const GWL_Seat *seat,
static void cursor_buffer_set(const GWL_Seat *seat, wl_buffer *buffer)
{
- const GWL_Cursor *c = &seat->cursor;
+ const GWL_Cursor *cursor = &seat->cursor;
const wl_cursor_image *wl_image = &seat->cursor.wl_image;
- const bool visible = (c->visible && c->is_hardware);
+ const bool visible = (cursor->visible && cursor->is_hardware);
/* This is a requirement of WAYLAND, when this isn't the case,
* it causes Blender's window to close intermittently. */
if (seat->wl_pointer) {
const int scale = cursor_buffer_compatible_scale_from_image(
- wl_image, c->is_custom ? c->custom_scale : seat->pointer.theme_scale);
+ wl_image, cursor->is_custom ? cursor->custom_scale : seat->pointer.theme_scale);
const int32_t hotspot_x = int32_t(wl_image->hotspot_x) / scale;
const int32_t hotspot_y = int32_t(wl_image->hotspot_y) / scale;
- cursor_buffer_set_surface_impl(seat, buffer, c->wl_surface, scale);
+ cursor_buffer_set_surface_impl(seat, buffer, cursor->wl_surface_cursor, scale);
wl_pointer_set_cursor(seat->wl_pointer,
seat->pointer.serial,
- visible ? c->wl_surface : nullptr,
+ visible ? cursor->wl_surface_cursor : nullptr,
hotspot_x,
hotspot_y);
}
@@ -3764,7 +5768,7 @@ static void cursor_buffer_set(const GWL_Seat *seat, wl_buffer *buffer)
/* Set the cursor for all tablet tools as well. */
if (!seat->tablet_tools.empty()) {
const int scale = cursor_buffer_compatible_scale_from_image(
- wl_image, c->is_custom ? c->custom_scale : seat->tablet.theme_scale);
+ wl_image, cursor->is_custom ? cursor->custom_scale : seat->tablet.theme_scale);
const int32_t hotspot_x = int32_t(wl_image->hotspot_x) / scale;
const int32_t hotspot_y = int32_t(wl_image->hotspot_y) / scale;
for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : seat->tablet_tools) {
@@ -3842,40 +5846,40 @@ static bool cursor_is_software(const GHOST_TGrabCursorMode mode, const bool use_
GHOST_TSuccess GHOST_SystemWayland::setCursorShape(const GHOST_TStandardCursor shape)
{
- if (UNLIKELY(display_->seats.empty())) {
+ GWL_Seat *seat = gwl_display_seat_active_get(display_);
+ if (UNLIKELY(!seat)) {
return GHOST_kFailure;
}
- auto cursor_find = cursors.find(shape);
- const char *cursor_name = (cursor_find == cursors.end()) ?
- cursors.at(GHOST_kStandardCursorDefault) :
+ auto cursor_find = ghost_wl_cursors.find(shape);
+ const char *cursor_name = (cursor_find == ghost_wl_cursors.end()) ?
+ ghost_wl_cursors.at(GHOST_kStandardCursorDefault) :
(*cursor_find).second;
- GWL_Seat *seat = display_->seats[0];
- GWL_Cursor *c = &seat->cursor;
+ GWL_Cursor *cursor = &seat->cursor;
- if (!c->wl_theme) {
+ if (!cursor->wl_theme) {
/* The cursor wl_surface hasn't entered an output yet. Initialize theme with scale 1. */
- c->wl_theme = wl_cursor_theme_load(
- c->theme_name.c_str(), c->size, display_->seats[0]->system->wl_shm());
+ cursor->wl_theme = wl_cursor_theme_load(
+ cursor->theme_name.c_str(), cursor->theme_size, wl_shm());
}
- wl_cursor *cursor = wl_cursor_theme_get_cursor(c->wl_theme, cursor_name);
+ wl_cursor *wl_cursor = wl_cursor_theme_get_cursor(cursor->wl_theme, cursor_name);
- if (!cursor) {
+ if (!wl_cursor) {
GHOST_PRINT("cursor '" << cursor_name << "' does not exist" << std::endl);
return GHOST_kFailure;
}
- struct wl_cursor_image *image = cursor->images[0];
+ struct wl_cursor_image *image = wl_cursor->images[0];
struct wl_buffer *buffer = wl_cursor_image_get_buffer(image);
if (!buffer) {
return GHOST_kFailure;
}
- c->visible = true;
- c->is_custom = false;
- c->wl_buffer = buffer;
- c->wl_image = *image;
+ cursor->visible = true;
+ cursor->is_custom = false;
+ cursor->wl_buffer = buffer;
+ cursor->wl_image = *image;
cursor_buffer_set(seat, buffer);
@@ -3884,8 +5888,8 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorShape(const GHOST_TStandardCursor s
GHOST_TSuccess GHOST_SystemWayland::hasCursorShape(const GHOST_TStandardCursor cursorShape)
{
- auto cursor_find = cursors.find(cursorShape);
- if (cursor_find == cursors.end()) {
+ auto cursor_find = ghost_wl_cursors.find(cursorShape);
+ if (cursor_find == ghost_wl_cursors.end()) {
return GHOST_kFailure;
}
const char *value = (*cursor_find).second;
@@ -3903,12 +5907,12 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap,
const int hotY,
const bool /*canInvertColor*/)
{
- if (UNLIKELY(display_->seats.empty())) {
+ GWL_Seat *seat = gwl_display_seat_active_get(display_);
+ if (UNLIKELY(!seat)) {
return GHOST_kFailure;
}
- GWL_Cursor *cursor = &display_->seats[0]->cursor;
-
+ GWL_Cursor *cursor = &seat->cursor;
if (cursor->custom_data) {
munmap(cursor->custom_data, cursor->custom_data_size);
cursor->custom_data = nullptr;
@@ -3966,14 +5970,19 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap,
cursor->wl_image.hotspot_x = uint32_t(hotX);
cursor->wl_image.hotspot_y = uint32_t(hotY);
- cursor_buffer_set(display_->seats[0], buffer);
+ cursor_buffer_set(seat, buffer);
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_SystemWayland::getCursorBitmap(GHOST_CursorBitmapRef *bitmap)
{
- GWL_Cursor *cursor = &display_->seats[0]->cursor;
+ GWL_Seat *seat = gwl_display_seat_active_get(display_);
+ if (UNLIKELY(!seat)) {
+ return GHOST_kFailure;
+ }
+
+ GWL_Cursor *cursor = &seat->cursor;
if (cursor->custom_data == nullptr) {
return GHOST_kFailure;
}
@@ -3994,11 +6003,11 @@ GHOST_TSuccess GHOST_SystemWayland::getCursorBitmap(GHOST_CursorBitmapRef *bitma
GHOST_TSuccess GHOST_SystemWayland::setCursorVisibility(const bool visible)
{
- if (UNLIKELY(display_->seats.empty())) {
+ GWL_Seat *seat = gwl_display_seat_active_get(display_);
+ if (UNLIKELY(!seat)) {
return GHOST_kFailure;
}
- GWL_Seat *seat = display_->seats[0];
cursor_visible_set(seat, visible, seat->cursor.is_hardware, CURSOR_VISIBLE_ALWAYS_SET);
return GHOST_kSuccess;
}
@@ -4018,12 +6027,12 @@ bool GHOST_SystemWayland::supportsWindowPosition()
bool GHOST_SystemWayland::getCursorGrabUseSoftwareDisplay(const GHOST_TGrabCursorMode mode)
{
- if (UNLIKELY(display_->seats.empty())) {
+ GWL_Seat *seat = gwl_display_seat_active_get(display_);
+ if (UNLIKELY(!seat)) {
return false;
}
#ifdef USE_GNOME_CONFINE_HACK
- GWL_Seat *seat = display_->seats[0];
const bool use_software_confine = seat->use_pointer_software_confine;
#else
const bool use_software_confine = false;
@@ -4062,11 +6071,10 @@ static GWL_SeatStateGrab seat_grab_state_from_mode(const GHOST_TGrabCursorMode m
const bool use_software_confine)
{
/* Initialize all members. */
- const struct GWL_SeatStateGrab grab_state = {
- /* Warping happens to require software cursor which also hides. */
- .use_lock = ELEM(mode, GHOST_kGrabWrap, GHOST_kGrabHide) || use_software_confine,
- .use_confine = (mode == GHOST_kGrabNormal) && (use_software_confine == false),
- };
+ GWL_SeatStateGrab grab_state;
+ /* Warping happens to require software cursor which also hides. */
+ grab_state.use_lock = ELEM(mode, GHOST_kGrabWrap, GHOST_kGrabHide) || use_software_confine;
+ grab_state.use_confine = (mode == GHOST_kGrabNormal) && (use_software_confine == false);
return grab_state;
}
@@ -4140,6 +6148,16 @@ wl_compositor *GHOST_SystemWayland::wl_compositor()
return display_->wl_compositor;
}
+struct zwp_primary_selection_device_manager_v1 *GHOST_SystemWayland::wp_primary_selection_manager()
+{
+ return display_->wp_primary_selection_device_manager;
+}
+
+struct zwp_pointer_gestures_v1 *GHOST_SystemWayland::wp_pointer_gestures()
+{
+ return display_->wp_pointer_gestures;
+}
+
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
libdecor *GHOST_SystemWayland::libdecor_context()
@@ -4202,9 +6220,9 @@ GHOST_WindowWayland *ghost_wl_surface_user_data(struct wl_surface *wl_surface)
* Functionality only used for the WAYLAND implementation.
* \{ */
-void GHOST_SystemWayland::clipboard_set(const std::string &clipboard)
+void GHOST_SystemWayland::seat_active_set(const struct GWL_Seat *seat)
{
- clipboard_ = clipboard;
+ gwl_display_seat_active_set(display_, seat);
}
void GHOST_SystemWayland::window_surface_unref(const wl_surface *wl_surface)
@@ -4217,10 +6235,10 @@ void GHOST_SystemWayland::window_surface_unref(const wl_surface *wl_surface)
/* Only clear window surfaces (not cursors, off-screen surfaces etc). */
for (GWL_Seat *seat : display_->seats) {
- SURFACE_CLEAR_PTR(seat->pointer.wl_surface);
- SURFACE_CLEAR_PTR(seat->tablet.wl_surface);
- SURFACE_CLEAR_PTR(seat->keyboard.wl_surface);
- SURFACE_CLEAR_PTR(seat->wl_surface_focus_dnd);
+ SURFACE_CLEAR_PTR(seat->pointer.wl_surface_window);
+ SURFACE_CLEAR_PTR(seat->tablet.wl_surface_window);
+ SURFACE_CLEAR_PTR(seat->keyboard.wl_surface_window);
+ SURFACE_CLEAR_PTR(seat->wl_surface_window_focus_dnd);
}
#undef SURFACE_CLEAR_PTR
}
@@ -4234,11 +6252,12 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
const int scale)
{
/* Ignore, if the required protocols are not supported. */
- if (UNLIKELY(!display_->relative_pointer_manager || !display_->pointer_constraints)) {
+ if (UNLIKELY(!display_->wp_relative_pointer_manager || !display_->wp_pointer_constraints)) {
return GHOST_kFailure;
}
- if (UNLIKELY(display_->seats.empty())) {
+ GWL_Seat *seat = gwl_display_seat_active_get(display_);
+ if (UNLIKELY(!seat)) {
return GHOST_kFailure;
}
/* No change, success. */
@@ -4246,8 +6265,6 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
return GHOST_kSuccess;
}
- GWL_Seat *seat = display_->seats[0];
-
#ifdef USE_GNOME_CONFINE_HACK
const bool was_software_confine = seat->use_pointer_software_confine;
const bool use_software_confine = setCursorGrab_use_software_confine(mode, wl_surface);
@@ -4273,11 +6290,11 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
* in this case disable the current locks as it makes logic confusing,
* postpone changing the cursor to avoid flickering. */
if (!grab_state_next.use_lock) {
- if (seat->relative_pointer) {
- zwp_relative_pointer_v1_destroy(seat->relative_pointer);
- seat->relative_pointer = nullptr;
+ if (seat->wp_relative_pointer) {
+ zwp_relative_pointer_v1_destroy(seat->wp_relative_pointer);
+ seat->wp_relative_pointer = nullptr;
}
- if (seat->locked_pointer) {
+ if (seat->wp_locked_pointer) {
/* Potentially add a motion event so the application has updated X/Y coordinates. */
int32_t xy_motion[2] = {0, 0};
bool xy_motion_create_event = false;
@@ -4306,7 +6323,7 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
seat->pointer.xy[0] = xy_next[0];
seat->pointer.xy[1] = xy_next[1];
- zwp_locked_pointer_v1_set_cursor_position_hint(seat->locked_pointer, UNPACK2(xy_next));
+ zwp_locked_pointer_v1_set_cursor_position_hint(seat->wp_locked_pointer, UNPACK2(xy_next));
wl_surface_commit(wl_surface);
}
else if (mode_current == GHOST_kGrabHide) {
@@ -4316,7 +6333,8 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
wl_fixed_from_int(init_grab_xy[0]) / scale,
wl_fixed_from_int(init_grab_xy[1]) / scale,
};
- zwp_locked_pointer_v1_set_cursor_position_hint(seat->locked_pointer, UNPACK2(xy_next));
+ zwp_locked_pointer_v1_set_cursor_position_hint(seat->wp_locked_pointer,
+ UNPACK2(xy_next));
wl_surface_commit(wl_surface);
/* NOTE(@campbellbarton): The new cursor position is a hint,
@@ -4330,7 +6348,7 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
#ifdef USE_GNOME_CONFINE_HACK
else if (mode_current == GHOST_kGrabNormal) {
if (was_software_confine) {
- zwp_locked_pointer_v1_set_cursor_position_hint(seat->locked_pointer,
+ zwp_locked_pointer_v1_set_cursor_position_hint(seat->wp_locked_pointer,
UNPACK2(seat->pointer.xy));
wl_surface_commit(wl_surface);
}
@@ -4346,15 +6364,15 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
GHOST_TABLET_DATA_NONE));
}
- zwp_locked_pointer_v1_destroy(seat->locked_pointer);
- seat->locked_pointer = nullptr;
+ zwp_locked_pointer_v1_destroy(seat->wp_locked_pointer);
+ seat->wp_locked_pointer = nullptr;
}
}
if (!grab_state_next.use_confine) {
- if (seat->confined_pointer) {
- zwp_confined_pointer_v1_destroy(seat->confined_pointer);
- seat->confined_pointer = nullptr;
+ if (seat->wp_confined_pointer) {
+ zwp_confined_pointer_v1_destroy(seat->wp_confined_pointer);
+ seat->wp_confined_pointer = nullptr;
}
}
@@ -4365,12 +6383,12 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
* possible to support #GHOST_kGrabWrap by pragmatically settings it's coordinates.
* An alternative could be to draw the cursor in software (and hide the real cursor),
* or just accept a locked cursor on WAYLAND. */
- seat->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(
- display_->relative_pointer_manager, seat->wl_pointer);
+ seat->wp_relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(
+ display_->wp_relative_pointer_manager, seat->wl_pointer);
zwp_relative_pointer_v1_add_listener(
- seat->relative_pointer, &relative_pointer_listener, seat);
- seat->locked_pointer = zwp_pointer_constraints_v1_lock_pointer(
- display_->pointer_constraints,
+ seat->wp_relative_pointer, &relative_pointer_listener, seat);
+ seat->wp_locked_pointer = zwp_pointer_constraints_v1_lock_pointer(
+ display_->wp_pointer_constraints,
wl_surface,
seat->wl_pointer,
nullptr,
@@ -4387,8 +6405,8 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
}
else if (grab_state_next.use_confine) {
if (!grab_state_prev.use_confine) {
- seat->confined_pointer = zwp_pointer_constraints_v1_confine_pointer(
- display_->pointer_constraints,
+ seat->wp_confined_pointer = zwp_pointer_constraints_v1_confine_pointer(
+ display_->wp_pointer_constraints,
wl_surface,
seat->wl_pointer,
nullptr,
diff --git a/intern/ghost/intern/GHOST_SystemWayland.h b/intern/ghost/intern/GHOST_SystemWayland.h
index 5d2cfe45582..a8e8d8ddc45 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.h
+++ b/intern/ghost/intern/GHOST_SystemWayland.h
@@ -23,6 +23,7 @@
# include <libdecor.h>
#endif
+#include <mutex>
#include <string>
class GHOST_WindowWayland;
@@ -51,6 +52,8 @@ void ghost_wl_dynload_libraries_exit();
#endif
struct GWL_Output {
+ GHOST_SystemWayland *system = nullptr;
+
struct wl_output *wl_output = nullptr;
struct zxdg_output_v1 *xdg_output = nullptr;
/** Dimensions in pixels. */
@@ -84,11 +87,12 @@ struct GWL_Output {
class GHOST_SystemWayland : public GHOST_System {
public:
- GHOST_SystemWayland();
+ GHOST_SystemWayland(bool background);
+ GHOST_SystemWayland() : GHOST_SystemWayland(true){};
~GHOST_SystemWayland() override;
- GHOST_TSuccess init();
+ GHOST_TSuccess init() override;
bool processEvents(bool waitForEvent) override;
@@ -128,7 +132,6 @@ class GHOST_SystemWayland : public GHOST_System {
uint32_t width,
uint32_t height,
GHOST_TWindowState state,
- GHOST_TDrawingContextType type,
GHOST_GLSettings glSettings,
const bool exclusive,
const bool is_dialog,
@@ -150,8 +153,8 @@ class GHOST_SystemWayland : public GHOST_System {
GHOST_TSuccess setCursorVisibility(bool visible);
- bool supportsCursorWarp();
- bool supportsWindowPosition();
+ bool supportsCursorWarp() override;
+ bool supportsWindowPosition() override;
bool getCursorGrabUseSoftwareDisplay(const GHOST_TGrabCursorMode mode);
@@ -159,6 +162,8 @@ class GHOST_SystemWayland : public GHOST_System {
struct wl_display *wl_display();
struct wl_compositor *wl_compositor();
+ struct zwp_primary_selection_device_manager_v1 *wp_primary_selection_manager();
+ struct zwp_pointer_gestures_v1 *wp_pointer_gestures();
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
libdecor *libdecor_context();
@@ -173,7 +178,8 @@ class GHOST_SystemWayland : public GHOST_System {
/* WAYLAND utility functions. */
- void clipboard_set(const std::string &clipboard);
+ /** Set this seat to be active. */
+ void seat_active_set(const struct GWL_Seat *seat);
/** Clear all references to this surface to prevent accessing NULL pointers. */
void window_surface_unref(const wl_surface *wl_surface);
@@ -192,5 +198,4 @@ class GHOST_SystemWayland : public GHOST_System {
private:
struct GWL_Display *display_;
- std::string clipboard_;
};
diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp
index 667198241f0..54c892d296e 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemWin32.cpp
@@ -213,7 +213,6 @@ GHOST_IWindow *GHOST_SystemWin32::createWindow(const char *title,
uint32_t width,
uint32_t height,
GHOST_TWindowState state,
- GHOST_TDrawingContextType type,
GHOST_GLSettings glSettings,
const bool exclusive,
const bool is_dialog,
@@ -227,7 +226,7 @@ GHOST_IWindow *GHOST_SystemWin32::createWindow(const char *title,
width,
height,
state,
- type,
+ glSettings.context_type,
((glSettings.flags & GHOST_glStereoVisual) != 0),
false,
(GHOST_WindowWin32 *)parentWindow,
diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h
index f5cd0055b34..98a7e5dfb35 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.h
+++ b/intern/ghost/intern/GHOST_SystemWin32.h
@@ -103,7 +103,6 @@ class GHOST_SystemWin32 : public GHOST_System {
* \param width: The width the window.
* \param height: The height the window.
* \param state: The state of the window when opened.
- * \param type: The type of drawing context installed in this window.
* \param glSettings: Misc OpenGL settings.
* \param exclusive: Use to show the window on top and ignore others (used full-screen).
* \param parentWindow: Parent window.
@@ -115,7 +114,6 @@ class GHOST_SystemWin32 : public GHOST_System {
uint32_t width,
uint32_t height,
GHOST_TWindowState state,
- GHOST_TDrawingContextType type,
GHOST_GLSettings glSettings,
const bool exclusive = false,
const bool is_dialog = false,
diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp
index 4523f6fa37c..5c89febe97c 100644
--- a/intern/ghost/intern/GHOST_SystemX11.cpp
+++ b/intern/ghost/intern/GHOST_SystemX11.cpp
@@ -308,7 +308,6 @@ void GHOST_SystemX11::getAllDisplayDimensions(uint32_t &width, uint32_t &height)
* \param width: The width the window.
* \param height: The height the window.
* \param state: The state of the window when opened.
- * \param type: The type of drawing context installed in this window.
* \param glSettings: Misc OpenGL settings.
* \param exclusive: Use to show the window on top and ignore others (used full-screen).
* \param parentWindow: Parent window.
@@ -320,7 +319,6 @@ GHOST_IWindow *GHOST_SystemX11::createWindow(const char *title,
uint32_t width,
uint32_t height,
GHOST_TWindowState state,
- GHOST_TDrawingContextType type,
GHOST_GLSettings glSettings,
const bool exclusive,
const bool is_dialog,
@@ -341,7 +339,7 @@ GHOST_IWindow *GHOST_SystemX11::createWindow(const char *title,
height,
state,
(GHOST_WindowX11 *)parentWindow,
- type,
+ glSettings.context_type,
is_dialog,
((glSettings.flags & GHOST_glStereoVisual) != 0),
exclusive,
diff --git a/intern/ghost/intern/GHOST_SystemX11.h b/intern/ghost/intern/GHOST_SystemX11.h
index 572be30174d..1f071da6da7 100644
--- a/intern/ghost/intern/GHOST_SystemX11.h
+++ b/intern/ghost/intern/GHOST_SystemX11.h
@@ -113,7 +113,6 @@ class GHOST_SystemX11 : public GHOST_System {
* \param width: The width the window.
* \param height: The height the window.
* \param state: The state of the window when opened.
- * \param type: The type of drawing context installed in this window.
* \param stereoVisual: Create a stereo visual for quad buffered stereo.
* \param exclusive: Use to show the window on top and ignore others (used full-screen).
* \param parentWindow: Parent (embedder) window.
@@ -125,7 +124,6 @@ class GHOST_SystemX11 : public GHOST_System {
uint32_t width,
uint32_t height,
GHOST_TWindowState state,
- GHOST_TDrawingContextType type,
GHOST_GLSettings glSettings,
const bool exclusive = false,
const bool is_dialog = false,
diff --git a/intern/ghost/intern/GHOST_Window.h b/intern/ghost/intern/GHOST_Window.h
index 1c0991bba30..396691fa161 100644
--- a/intern/ghost/intern/GHOST_Window.h
+++ b/intern/ghost/intern/GHOST_Window.h
@@ -74,7 +74,7 @@ class GHOST_Window : public GHOST_IWindow {
*/
virtual bool getValid() const override
{
- return m_context != NULL;
+ return m_context != nullptr;
}
/**
@@ -283,8 +283,9 @@ class GHOST_Window : public GHOST_IWindow {
float getNativePixelSize(void) override
{
- if (m_nativePixelSize > 0.0f)
+ if (m_nativePixelSize > 0.0f) {
return m_nativePixelSize;
+ }
return 1.0f;
}
@@ -298,7 +299,8 @@ class GHOST_Window : public GHOST_IWindow {
}
#ifdef WITH_INPUT_IME
- virtual void beginIME(int32_t x, int32_t y, int32_t w, int32_t h, bool completed) override
+ virtual void beginIME(
+ int32_t /*x*/, int32_t /*y*/, int32_t /*w*/, int32_t /*h*/, bool /*completed*/) override
{
/* do nothing temporarily if not in windows */
}
diff --git a/intern/ghost/intern/GHOST_WindowCocoa.mm b/intern/ghost/intern/GHOST_WindowCocoa.mm
index 737fd64bdf0..bc1f1e99a3a 100644
--- a/intern/ghost/intern/GHOST_WindowCocoa.mm
+++ b/intern/ghost/intern/GHOST_WindowCocoa.mm
@@ -803,10 +803,10 @@ GHOST_TSuccess GHOST_WindowCocoa::setOrder(GHOST_TWindowOrder order)
GHOST_Context *GHOST_WindowCocoa::newDrawingContext(GHOST_TDrawingContextType type)
{
- if (type == GHOST_kDrawingContextTypeOpenGL) {
+ if (type == GHOST_kDrawingContextTypeOpenGL || type == GHOST_kDrawingContextTypeMetal) {
GHOST_Context *context = new GHOST_ContextCGL(
- m_wantStereoVisual, m_metalView, m_metalLayer, m_openGLView);
+ m_wantStereoVisual, m_metalView, m_metalLayer, m_openGLView, type);
if (context->initializeDrawingContext())
return context;
diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp
index b29c5efd8d4..ad94a02b514 100644
--- a/intern/ghost/intern/GHOST_WindowWayland.cpp
+++ b/intern/ghost/intern/GHOST_WindowWayland.cpp
@@ -53,7 +53,7 @@ struct WGL_LibDecor_Window {
bool configured = false;
};
-static void wgl_libdecor_window_destroy(WGL_LibDecor_Window *decor)
+static void gwl_libdecor_window_destroy(WGL_LibDecor_Window *decor)
{
libdecor_frame_unref(decor->frame);
delete decor;
@@ -67,7 +67,7 @@ struct WGL_XDG_Decor_Window {
enum zxdg_toplevel_decoration_v1_mode mode = (enum zxdg_toplevel_decoration_v1_mode)0;
};
-static void wgl_xdg_decor_window_destroy(WGL_XDG_Decor_Window *decor)
+static void gwl_xdg_decor_window_destroy(WGL_XDG_Decor_Window *decor)
{
if (decor->toplevel_decor) {
zxdg_toplevel_decoration_v1_destroy(decor->toplevel_decor);
@@ -91,12 +91,10 @@ struct GWL_Window {
/** The scale value written to #wl_surface_set_buffer_scale. */
int scale = 0;
/**
- * The DPI, either:
- * - `scale * base_dpi`
- * - `wl_fixed_to_int(scale_fractional * base_dpi)`
- * When fractional scaling is available.
+ * The fractional scale used to calculate the DPI.
+ * (always set, even when scaling is rounded to whole units).
*/
- uint32_t dpi = 0;
+ wl_fixed_t scale_fractional = 0;
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
WGL_LibDecor_Window *libdecor = nullptr;
@@ -147,7 +145,7 @@ static int output_scale_cmp(const GWL_Output *output_a, const GWL_Output *output
static int outputs_max_scale_or_default(const std::vector<GWL_Output *> &outputs,
const int32_t scale_default,
- uint32_t *r_dpi)
+ wl_fixed_t *r_scale_fractional)
{
const GWL_Output *output_max = nullptr;
for (const GWL_Output *reg_output : outputs) {
@@ -157,18 +155,16 @@ static int outputs_max_scale_or_default(const std::vector<GWL_Output *> &outputs
}
if (output_max) {
- if (r_dpi) {
- *r_dpi = output_max->has_scale_fractional ?
- /* Fractional DPI. */
- wl_fixed_to_int(output_max->scale_fractional * base_dpi) :
- /* Simple non-fractional DPI. */
- (output_max->scale * base_dpi);
+ if (r_scale_fractional) {
+ *r_scale_fractional = output_max->has_scale_fractional ?
+ output_max->scale_fractional :
+ wl_fixed_from_int(output_max->scale);
}
return output_max->scale;
}
- if (r_dpi) {
- *r_dpi = scale_default * base_dpi;
+ if (r_scale_fractional) {
+ *r_scale_fractional = wl_fixed_from_int(scale_default);
}
return scale_default;
}
@@ -479,7 +475,7 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
*
* Using the maximum scale is best as it results in the window first being smaller,
* avoiding a large window flashing before it's made smaller. */
- window_->scale = outputs_max_scale_or_default(system_->outputs(), 1, &window_->dpi);
+ window_->scale = outputs_max_scale_or_default(system_->outputs(), 1, &window_->scale_fractional);
/* Window surfaces. */
window_->wl_surface = wl_compositor_create_surface(system_->wl_compositor());
@@ -727,12 +723,12 @@ GHOST_WindowWayland::~GHOST_WindowWayland()
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
if (use_libdecor) {
- wgl_libdecor_window_destroy(window_->libdecor);
+ gwl_libdecor_window_destroy(window_->libdecor);
}
else
#endif
{
- wgl_xdg_decor_window_destroy(window_->xdg_decor);
+ gwl_xdg_decor_window_destroy(window_->xdg_decor);
}
/* Clear any pointers to this window. This is needed because there are no guarantees
@@ -751,7 +747,9 @@ GHOST_WindowWayland::~GHOST_WindowWayland()
uint16_t GHOST_WindowWayland::getDPIHint()
{
- return window_->dpi;
+ /* Using the physical DPI will cause wrong scaling of the UI
+ * use a multiplier for the default DPI as a workaround. */
+ return wl_fixed_to_int(window_->scale_fractional * base_dpi);
}
GHOST_TSuccess GHOST_WindowWayland::setWindowCursorVisibility(bool visible)
@@ -946,7 +944,12 @@ GHOST_Context *GHOST_WindowWayland::newDrawingContext(GHOST_TDrawingContextType
EGL_OPENGL_API);
}
- return (context->initializeDrawingContext() == GHOST_kSuccess) ? context : nullptr;
+ if (context->initializeDrawingContext()) {
+ return context;
+ }
+
+ delete context;
+ return nullptr;
}
/** \} */
@@ -957,14 +960,14 @@ GHOST_Context *GHOST_WindowWayland::newDrawingContext(GHOST_TDrawingContextType
* Expose some members via methods.
* \{ */
-uint16_t GHOST_WindowWayland::dpi() const
+int GHOST_WindowWayland::scale() const
{
- return window_->dpi;
+ return window_->scale;
}
-int GHOST_WindowWayland::scale() const
+wl_fixed_t GHOST_WindowWayland::scale_fractional() const
{
- return window_->scale;
+ return window_->scale_fractional;
}
wl_surface *GHOST_WindowWayland::wl_surface() const
@@ -1030,30 +1033,39 @@ GHOST_TSuccess GHOST_WindowWayland::notify_size()
*/
bool GHOST_WindowWayland::outputs_changed_update_scale()
{
- uint32_t dpi_next;
- const int scale_next = outputs_max_scale_or_default(outputs(), 0, &dpi_next);
+ wl_fixed_t scale_fractional_next = 0;
+ const int scale_next = outputs_max_scale_or_default(outputs(), 0, &scale_fractional_next);
if (UNLIKELY(scale_next == 0)) {
return false;
}
- const uint32_t dpi_curr = window_->dpi;
+ const wl_fixed_t scale_fractional_curr = window_->scale_fractional;
const int scale_curr = window_->scale;
bool changed = false;
if (scale_next != scale_curr) {
- /* Unlikely but possible there is a pending size change is set. */
- window_->size_pending[0] = (window_->size_pending[0] / scale_curr) * scale_next;
- window_->size_pending[1] = (window_->size_pending[1] / scale_curr) * scale_next;
-
window_->scale = scale_next;
wl_surface_set_buffer_scale(window_->wl_surface, scale_next);
+
+ /* It's important to resize the window immediately, to avoid the window changing size
+ * and flickering in a constant feedback loop (in some bases). */
+ if ((window_->size_pending[0] != 0) && (window_->size_pending[1] != 0)) {
+ /* Unlikely but possible there is a pending size change is set. */
+ window_->size[0] = window_->size_pending[0];
+ window_->size[1] = window_->size_pending[1];
+ window_->size_pending[0] = 0;
+ window_->size_pending[1] = 0;
+ }
+ window_->size[0] = (window_->size[0] / scale_curr) * scale_next;
+ window_->size[1] = (window_->size[1] / scale_curr) * scale_next;
+ wl_egl_window_resize(window_->egl_window, UNPACK2(window_->size), 0, 0);
+ window_->ghost_window->notify_size();
+
changed = true;
}
- if (dpi_next != dpi_curr) {
- /* Using the real DPI will cause wrong scaling of the UI
- * use a multiplier for the default DPI as workaround. */
- window_->dpi = dpi_next;
+ if (scale_fractional_next != scale_fractional_curr) {
+ window_->scale_fractional = scale_fractional_next;
changed = true;
/* As this is a low-level function, we might want adding this event to be optional,
diff --git a/intern/ghost/intern/GHOST_WindowWayland.h b/intern/ghost/intern/GHOST_WindowWayland.h
index e95f5386310..ec473c4a710 100644
--- a/intern/ghost/intern/GHOST_WindowWayland.h
+++ b/intern/ghost/intern/GHOST_WindowWayland.h
@@ -12,6 +12,8 @@
#include <vector>
+#include <wayland-util.h> /* For #wl_fixed_t */
+
class GHOST_SystemWayland;
struct GWL_Output;
@@ -95,8 +97,8 @@ class GHOST_WindowWayland : public GHOST_Window {
/* WAYLAND direct-data access. */
- uint16_t dpi() const;
int scale() const;
+ wl_fixed_t scale_fractional() const;
struct wl_surface *wl_surface() const;
const std::vector<GWL_Output *> &outputs();
diff --git a/intern/ghost/test/gears/GHOST_Test.cpp b/intern/ghost/test/gears/GHOST_Test.cpp
index 1891f1eca77..3c9206b2010 100644
--- a/intern/ghost/test/gears/GHOST_Test.cpp
+++ b/intern/ghost/test/gears/GHOST_Test.cpp
@@ -407,17 +407,12 @@ Application::Application(GHOST_ISystem *system)
stereo(false)
{
GHOST_GLSettings glSettings = {0};
+ glSettings.context_type = GHOST_kDrawingContextTypeOpenGL;
fApp = this;
// Create the main window
- m_mainWindow = system->createWindow("gears - main window",
- 10,
- 64,
- 320,
- 200,
- GHOST_kWindowStateNormal,
- GHOST_kDrawingContextTypeOpenGL,
- glSettings);
+ m_mainWindow = system->createWindow(
+ "gears - main window", 10, 64, 320, 200, GHOST_kWindowStateNormal, glSettings);
if (!m_mainWindow) {
std::cout << "could not create main window\n";
@@ -425,14 +420,8 @@ Application::Application(GHOST_ISystem *system)
}
// Create a secondary window
- m_secondaryWindow = system->createWindow("gears - secondary window",
- 340,
- 64,
- 320,
- 200,
- GHOST_kWindowStateNormal,
- GHOST_kDrawingContextTypeOpenGL,
- glSettings);
+ m_secondaryWindow = system->createWindow(
+ "gears - secondary window", 340, 64, 320, 200, GHOST_kWindowStateNormal, glSettings);
if (!m_secondaryWindow) {
std::cout << "could not create secondary window\n";
exit(-1);
diff --git a/intern/guardedalloc/intern/leak_detector.cc b/intern/guardedalloc/intern/leak_detector.cc
index 288d78fd206..5b565b15920 100644
--- a/intern/guardedalloc/intern/leak_detector.cc
+++ b/intern/guardedalloc/intern/leak_detector.cc
@@ -41,7 +41,7 @@ class MemLeakPrinter {
if (fail_on_memleak) {
/* There are many other ways to change the exit code to failure here:
- * - Make the destructor noexcept(false) and throw an exception.
+ * - Make the destructor `noexcept(false)` and throw an exception.
* - Call exit(EXIT_FAILURE).
* - Call terminate().
*/
diff --git a/intern/opensubdiv/CMakeLists.txt b/intern/opensubdiv/CMakeLists.txt
index 1dddd70928a..920b8d5c542 100644
--- a/intern/opensubdiv/CMakeLists.txt
+++ b/intern/opensubdiv/CMakeLists.txt
@@ -81,12 +81,6 @@ if(WITH_OPENSUBDIV)
)
endif()
- opensubdiv_define_component(OPENSUBDIV_HAS_OPENMP)
- opensubdiv_define_component(OPENSUBDIV_HAS_OPENCL)
- opensubdiv_define_component(OPENSUBDIV_HAS_CUDA)
- opensubdiv_define_component(OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK)
- opensubdiv_define_component(OPENSUBDIV_HAS_GLSL_COMPUTE)
-
if(WIN32)
add_definitions(-DNOMINMAX)
add_definitions(-D_USE_MATH_DEFINES)
diff --git a/intern/wayland_dynload/extern/wayland_dynload_client.h b/intern/wayland_dynload/extern/wayland_dynload_client.h
index 8e9dddd91a3..d80ef5c9f0c 100644
--- a/intern/wayland_dynload/extern/wayland_dynload_client.h
+++ b/intern/wayland_dynload/extern/wayland_dynload_client.h
@@ -41,6 +41,7 @@ WAYLAND_DYNLOAD_IFACE(wl_seat_interface)
WAYLAND_DYNLOAD_IFACE(wl_shm_interface)
WAYLAND_DYNLOAD_IFACE(wl_shm_pool_interface)
WAYLAND_DYNLOAD_IFACE(wl_surface_interface)
+WAYLAND_DYNLOAD_IFACE(wl_touch_interface)
#else
/* Header guard. */