Compare commits

...

12 Commits

Author SHA1 Message Date
Vitaly Provodin
644fb1db0a update exclude list on results of 21.0.5_b721.23 test runs 2024-12-11 13:58:46 +04:00
Maxim Kartashev
6b2a8b03f7 JBR-7993 Menus are not displayed directly underneath main menu if offset in monitor configuration exists 2024-12-06 16:07:50 +04:00
Vitaly Provodin
0665558bcc update exclude list on results of 21.0.5_b714.21 test runs 2024-12-05 07:15:31 +04:00
Sergey Shelomentsev
17cefef4f9 JBR-7919 add tests for Wayland popups 2024-12-03 18:42:18 +02:00
Maxim Kartashev
0d95dbe3eb JBR-7972 Wayland: EXTREME lag when scrolling through any type of list in the settings when using WLToolkit
Avoid requesting the Wayland server to change the cursor when the change
is vacuous
2024-12-03 12:12:33 +04:00
Maxim Kartashev
44fca1c619 JBR-7071 Wayland: cursor does not change when hovering over gutter icons 2024-12-02 19:10:45 +04:00
Maxim Kartashev
4a50c3cc82 JBR-7879 Wayland: Self-moving quick-doc popup in nightly
- Position popups at the exact offset given; this is achieved by
  using the XDG_POSITIONER_ANCHOR_TOP_LEFT anchor
- Update of popups location is done in sync with all other updates
  that affect the size (like the surface size update)
- Maintain a popup's location relative to the popup's parent, not
  its toplevel window
2024-12-02 12:22:54 +04:00
Maxim Kartashev
a139bc7866 JBR-7969 Wayland: some popups misplaced when maximized with fractional scale 2024-12-02 12:19:39 +04:00
Nikita Gubarkov
8e05c17afb JBR-7575 Vulkan: Implement composites (blending and XOR mode)
- Implemented dynamic pipeline compilation.
- Added 64-bit per pixel format usage in debug mode for testing.
- Now passing colors from Java to Vulkan with straight alpha.

(cherry picked from commit 3d7baad687)
2024-11-27 15:25:26 +01:00
Vitaly Provodin
6ef6b1d328 update exclude list on results of 21.0.5_b709.19 test runs 2024-11-26 17:07:45 +04:00
Nikita Gubarkov
7092fb7d71 JBR-7943 Vulkan: Provide utilities for inspecting image formats 2024-11-22 22:03:10 +01:00
Sergey Shelomentsev
2f8123cf84 JBR-7939 set max wait to 1 min for jetsign client 2024-11-22 13:40:45 +02:00
39 changed files with 1878 additions and 353 deletions

View File

@@ -38,7 +38,7 @@ else
max_attempts=3
attempt=1
while [ $attempt -le $max_attempts ]; do
if "$JETSIGN_CLIENT" -log-format text -denoted-content-type "$contentType" -extensions "$jetSignExtensions" "$pathToBeSigned"; then
if "$JETSIGN_CLIENT" -log-format text -max-wait 1m -denoted-content-type "$contentType" -extensions "$jetSignExtensions" "$pathToBeSigned"; then
break
else
if [ $attempt -eq $max_attempts ]; then

View File

@@ -75,9 +75,11 @@ public abstract class VKSurfaceData extends SurfaceData
private static final String DESC_VK_TEXTURE = "VK Texture";
// We want non-premultiplied alpha to prevent precision loss, so use PixelConverter.Argb
// See also VKUtil_DecodeJavaColor.
static final SurfaceType VKSurface =
SurfaceType.Any.deriveSubType(DESC_VK_SURFACE,
PixelConverter.ArgbPre.instance);
PixelConverter.Argb.instance);
static final SurfaceType VKSurfaceRTT =
VKSurface.deriveSubType(DESC_VK_SURFACE_RTT);
static final SurfaceType VKTexture =

View File

@@ -33,15 +33,15 @@
#undef SHADER_ENTRY
#undef BYTECODE_END
struct VKPipelineSet {
typedef struct VKPipelineSet {
VkPipeline pipelines[PIPELINE_COUNT];
};
} VKPipelineSet;
struct VKShaders {
typedef struct VKShaders {
# define SHADER_ENTRY(NAME, TYPE) VkPipelineShaderStageCreateInfo NAME ## _ ## TYPE;
# include "vulkan/shader_list.h"
# undef SHADER_ENTRY
};
} VKShaders;
static void VKPipelines_DestroyShaders(VKDevice* device, VKShaders* shaders) {
assert(device != NULL);
@@ -79,19 +79,19 @@ static VKShaders* VKPipelines_CreateShaders(VKDevice* device) {
return shaders;
}
#define MAKE_INPUT_STATE(TYPE, ...) \
static const VkVertexInputAttributeDescription attributes[] = { __VA_ARGS__ }; \
static const VkVertexInputBindingDescription binding = { \
.binding = 0, \
.stride = sizeof(TYPE), \
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX \
}; \
static const VkPipelineVertexInputStateCreateInfo inputState = { \
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, \
.vertexBindingDescriptionCount = 1, \
.pVertexBindingDescriptions = &binding, \
.vertexAttributeDescriptionCount = SARRAY_COUNT_OF(attributes), \
.pVertexAttributeDescriptions = attributes \
#define MAKE_INPUT_STATE(NAME, TYPE, ...) \
static const VkVertexInputAttributeDescription INPUT_STATE_ATTRIBUTES_##NAME[] = { __VA_ARGS__ }; \
static const VkVertexInputBindingDescription INPUT_STATE_BINDING_##NAME = { \
.binding = 0, \
.stride = sizeof(TYPE), \
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX \
}; \
static const VkPipelineVertexInputStateCreateInfo INPUT_STATE_##NAME = { \
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, \
.vertexBindingDescriptionCount = 1, \
.pVertexBindingDescriptions = &INPUT_STATE_BINDING_##NAME, \
.vertexAttributeDescriptionCount = SARRAY_COUNT_OF(INPUT_STATE_ATTRIBUTES_##NAME), \
.pVertexAttributeDescriptions = INPUT_STATE_ATTRIBUTES_##NAME \
}
typedef struct {
@@ -177,13 +177,62 @@ static const VkPipelineInputAssemblyStateCreateInfo INPUT_ASSEMBLY_STATE_LINE_LI
.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST
};
const VkPipelineColorBlendAttachmentState BLEND_STATE = {
.blendEnable = VK_FALSE,
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT
// Blend states are hard-coded, but can also be loaded dynamically to implement custom composites.
#define DEF_BLEND(NAME, SRC_COLOR, DST_COLOR, SRC_ALPHA, DST_ALPHA) \
{ .blendEnable = VK_TRUE, \
.srcColorBlendFactor = VK_BLEND_FACTOR_ ## SRC_COLOR, \
.dstColorBlendFactor = VK_BLEND_FACTOR_ ## DST_COLOR, \
.colorBlendOp = VK_BLEND_OP_ADD, \
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ ## SRC_ALPHA, \
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ ## DST_ALPHA, \
.alphaBlendOp = VK_BLEND_OP_ADD, \
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | \
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT }
const VkPipelineColorBlendAttachmentState COMPOSITE_BLEND_STATES[COMPOSITE_COUNT] = {
{ .blendEnable = VK_FALSE, // LOGIC_XOR
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT },
// NAME || SRC_COLOR | DST_COLOR | SRC_ALPHA | DST_ALPHA ||
DEF_BLEND(| CLEAR |, ZERO , ZERO , ZERO , ZERO ),
DEF_BLEND(| SRC |, ONE , ZERO , ONE , ZERO ),
DEF_BLEND(| SRC_OVER |, ONE , ONE_MINUS_SRC_ALPHA , ONE , ONE_MINUS_SRC_ALPHA ),
DEF_BLEND(| DST_OVER |, ONE_MINUS_DST_ALPHA , ONE , ONE_MINUS_DST_ALPHA , ONE ),
DEF_BLEND(| SRC_IN |, DST_ALPHA , ZERO , DST_ALPHA , ZERO ),
DEF_BLEND(| DST_IN |, ZERO , SRC_ALPHA , ZERO , SRC_ALPHA ),
DEF_BLEND(| SRC_OUT |, ONE_MINUS_DST_ALPHA , ZERO , ONE_MINUS_DST_ALPHA , ZERO ),
DEF_BLEND(| DST_OUT |, ZERO , ONE_MINUS_SRC_ALPHA , ZERO , ONE_MINUS_SRC_ALPHA ),
DEF_BLEND(| DST |, ZERO , ONE , ZERO , ONE ),
DEF_BLEND(| SRC_ATOP |, DST_ALPHA , ONE_MINUS_SRC_ALPHA , ZERO , ONE ),
DEF_BLEND(| DST_ATOP |, ONE_MINUS_DST_ALPHA , SRC_ALPHA , ONE , ZERO ),
DEF_BLEND(| XOR |, ONE_MINUS_DST_ALPHA , ONE_MINUS_SRC_ALPHA , ONE_MINUS_DST_ALPHA , ONE_MINUS_SRC_ALPHA ),
};
static const VkVertexInputAttributeDescription INPUT_ATTRIBUTE_POSITION = {
.location = 0,
.binding = 0,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = 0
};
static const VkVertexInputAttributeDescription INPUT_ATTRIBUTE_COLOR = {
.location = 1,
.binding = 0,
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.offset = sizeof(float) * 2
};
static const VkVertexInputAttributeDescription INPUT_ATTRIBUTE_TEXCOORD = {
.location = 1,
.binding = 0,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = sizeof(float) * 2
};
MAKE_INPUT_STATE(COLOR_VERTEX, VKColorVertex, INPUT_ATTRIBUTE_POSITION, INPUT_ATTRIBUTE_COLOR);
MAKE_INPUT_STATE(TEXCOORD_VERTEX, VKTxVertex, INPUT_ATTRIBUTE_POSITION, INPUT_ATTRIBUTE_TEXCOORD);
static void VKPipelines_DestroyPipelineSet(VKDevice* device, VKPipelineSet* set) {
assert(device != NULL);
if (set == NULL) return;
for (uint32_t i = 0; i < PIPELINE_COUNT; i++) {
device->vkDestroyPipeline(device->handle, set->pipelines[i], NULL);
@@ -191,8 +240,9 @@ static void VKPipelines_DestroyPipelineSet(VKDevice* device, VKPipelineSet* set)
free(set);
}
static VKPipelineSet* VKPipelines_CreatePipelineSet(VKRenderPassContext* renderPassContext) {
static VKPipelineSet* VKPipelines_CreatePipelineSet(VKRenderPassContext* renderPassContext, VKCompositeMode composite) {
assert(renderPassContext != NULL && renderPassContext->pipelineContext != NULL);
assert(composite < COMPOSITE_COUNT);
VKPipelineContext* pipelineContext = renderPassContext->pipelineContext;
VKPipelineSet* set = calloc(1, sizeof(VKPipelineSet));
@@ -205,7 +255,8 @@ static VKPipelineSet* VKPipelines_CreatePipelineSet(VKRenderPassContext* renderP
VKPipelines_InitPipelineCreateState(&base);
base.createInfo.layout = pipelineContext->pipelineLayout;
base.createInfo.renderPass = renderPassContext->renderPass;
base.colorBlendState.pAttachments = &BLEND_STATE;
base.colorBlendState.pAttachments = &COMPOSITE_BLEND_STATES[composite];
if (COMPOSITE_GROUP(composite) == LOGIC_COMPOSITE_GROUP) base.colorBlendState.logicOpEnable = VK_TRUE;
assert(base.dynamicState.dynamicStateCount <= SARRAY_COUNT_OF(base.dynamicStates));
ShaderStages stages[PIPELINE_COUNT];
@@ -215,36 +266,15 @@ static VKPipelineSet* VKPipelines_CreatePipelineSet(VKRenderPassContext* renderP
createInfos[i].pStages = stages[i].createInfos;
}
static const VkVertexInputAttributeDescription positionAttribute = {
.location = 0,
.binding = 0,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = 0
};
static const VkVertexInputAttributeDescription texcoordAttribute = {
.location = 1,
.binding = 0,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = sizeof(float) * 2
};
static const VkVertexInputAttributeDescription colorAttribute = {
.location = 1,
.binding = 0,
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.offset = sizeof(float) * 2
};
{ // Setup plain color pipelines.
MAKE_INPUT_STATE(VKColorVertex, positionAttribute, colorAttribute);
createInfos[PIPELINE_DRAW_COLOR].pVertexInputState = createInfos[PIPELINE_FILL_COLOR].pVertexInputState = &inputState;
createInfos[PIPELINE_DRAW_COLOR].pVertexInputState = createInfos[PIPELINE_FILL_COLOR].pVertexInputState = &INPUT_STATE_COLOR_VERTEX;
createInfos[PIPELINE_FILL_COLOR].pInputAssemblyState = &INPUT_ASSEMBLY_STATE_TRIANGLE_LIST;
createInfos[PIPELINE_DRAW_COLOR].pInputAssemblyState = &INPUT_ASSEMBLY_STATE_LINE_LIST;
stages[PIPELINE_DRAW_COLOR] = stages[PIPELINE_FILL_COLOR] = (ShaderStages) {{ shaders->color_vert, shaders->color_frag }};
}
{ // Setup texture pipeline.
MAKE_INPUT_STATE(VKTxVertex, positionAttribute, texcoordAttribute);
createInfos[PIPELINE_BLIT].pVertexInputState = &inputState;
createInfos[PIPELINE_BLIT].pVertexInputState = &INPUT_STATE_TEXCOORD_VERTEX;
createInfos[PIPELINE_BLIT].pInputAssemblyState = &INPUT_ASSEMBLY_STATE_TRIANGLE_STRIP;
createInfos[PIPELINE_BLIT].layout = pipelineContext->texturePipelineLayout;
stages[PIPELINE_BLIT] = (ShaderStages) {{ shaders->blit_vert, shaders->blit_frag }};
@@ -254,11 +284,12 @@ static VKPipelineSet* VKPipelines_CreatePipelineSet(VKRenderPassContext* renderP
// TODO pipeline cache
VK_IF_ERROR(device->vkCreateGraphicsPipelines(device->handle, VK_NULL_HANDLE, PIPELINE_COUNT,
createInfos, NULL, set->pipelines)) VK_UNHANDLED_ERROR();
J2dRlsTraceLn(J2D_TRACE_INFO, "VKPipelines_CreatePipelineSet");
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKPipelines_CreatePipelineSet: composite=%d", composite);
return set;
}
static VkResult VKPipelines_InitRenderPass(VKDevice* device, VKRenderPassContext* renderPassContext) {
assert(device != NULL && renderPassContext != NULL);
VkAttachmentDescription colorAttachment = {
.format = renderPassContext->format,
.samples = VK_SAMPLE_COUNT_1_BIT,
@@ -304,8 +335,13 @@ static void VKPipelines_DestroyRenderPassContext(VKRenderPassContext* renderPass
if (renderPassContext == NULL) return;
VKDevice* device = renderPassContext->pipelineContext->device;
assert(device != NULL);
VKPipelines_DestroyPipelineSet(device, renderPassContext->pipelineSet);
for (uint32_t i = 0; i < ARRAY_SIZE(renderPassContext->pipelineSets); i++) {
VKPipelines_DestroyPipelineSet(device, renderPassContext->pipelineSets[i]);
}
ARRAY_FREE(renderPassContext->pipelineSets);
device->vkDestroyRenderPass(device->handle, renderPassContext->renderPass, NULL);
J2dRlsTraceLn2(J2D_TRACE_INFO, "VKPipelines_DestroyRenderPassContext(%p): format=%d",
renderPassContext, renderPassContext->format);
free(renderPassContext);
}
@@ -321,6 +357,7 @@ static VKRenderPassContext* VKPipelines_CreateRenderPassContext(VKPipelineContex
return NULL;
}
J2dRlsTraceLn2(J2D_TRACE_INFO, "VKPipelines_CreateRenderPassContext(%p): format=%d", renderPassContext, format);
return renderPassContext;
}
@@ -397,6 +434,7 @@ VKPipelineContext* VKPipelines_CreateContext(VKDevice* device) {
return NULL;
}
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKPipelines_CreateContext(%p)", pipelineContext);
return pipelineContext;
}
@@ -417,13 +455,16 @@ void VKPipelines_DestroyContext(VKPipelineContext* pipelineContext) {
device->vkDestroyPipelineLayout(device->handle, pipelineContext->texturePipelineLayout, NULL);
device->vkDestroyDescriptorSetLayout(device->handle, pipelineContext->textureDescriptorSetLayout, NULL);
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKPipelines_DestroyContext(%p)", pipelineContext);
free(pipelineContext);
}
VKRenderPassContext* VKPipelines_GetRenderPassContext(VKPipelineContext* pipelineContext, VkFormat format) {
assert(pipelineContext != NULL && pipelineContext->device != NULL);
for (uint32_t i = 0; i < ARRAY_SIZE(pipelineContext->renderPassContexts); i++) {
if (pipelineContext->renderPassContexts[i]->format == format) return pipelineContext->renderPassContexts[i];
if (pipelineContext->renderPassContexts[i]->format == format) {
return pipelineContext->renderPassContexts[i];
}
}
// Not found, create.
VKRenderPassContext* renderPassContext = VKPipelines_CreateRenderPassContext(pipelineContext, format);
@@ -431,9 +472,16 @@ VKRenderPassContext* VKPipelines_GetRenderPassContext(VKPipelineContext* pipelin
return renderPassContext;
}
VkPipeline VKPipelines_GetPipeline(VKRenderPassContext* renderPassContext, VKPipeline pipeline) {
if (renderPassContext->pipelineSet == NULL) {
renderPassContext->pipelineSet = VKPipelines_CreatePipelineSet(renderPassContext);
VkPipeline VKPipelines_GetPipeline(VKRenderPassContext* renderPassContext, VKCompositeMode composite, VKPipeline pipeline) {
assert(renderPassContext != NULL);
assert(composite < COMPOSITE_COUNT); // We could append custom composites after that index.
assert(pipeline < PIPELINE_COUNT); // We could append custom pipelines after that index.
// Currently, our pipelines map to composite modes 1-to-1, but this may change in future when we'll add more states.
uint32_t setIndex = (uint32_t) composite;
while (ARRAY_SIZE(renderPassContext->pipelineSets) <= setIndex) ARRAY_PUSH_BACK(renderPassContext->pipelineSets, NULL);
if (renderPassContext->pipelineSets[setIndex] == NULL) {
renderPassContext->pipelineSets[setIndex] = VKPipelines_CreatePipelineSet(renderPassContext, composite);
}
return renderPassContext->pipelineSet->pipelines[pipeline];
return renderPassContext->pipelineSets[setIndex]->pipelines[pipeline];
}

View File

@@ -24,22 +24,51 @@
#ifndef VKPipelines_h_Included
#define VKPipelines_h_Included
#include "java_awt_AlphaComposite.h"
#include "VKTypes.h"
typedef struct VKShaders VKShaders;
typedef struct VKPipelineSet VKPipelineSet;
/**
* All pipeline types, use these to index into VKPipelineSet.pipelines.
* All pipeline types.
*/
typedef enum {
PIPELINE_FILL_COLOR = 0,
PIPELINE_DRAW_COLOR = 1,
PIPELINE_BLIT = 2,
PIPELINE_COUNT = 3,
NO_PIPELINE = PIPELINE_COUNT
PIPELINE_BLIT = 2,
PIPELINE_COUNT = 3,
NO_PIPELINE = 0x7FFFFFFF
} VKPipeline;
/**
* There are two groups of composite modes:
* - Logic composite - using logicOp.
* - Alpha compisite - using blending.
*/
typedef enum {
LOGIC_COMPOSITE_XOR = 0,
LOGIC_COMPOSITE_GROUP = LOGIC_COMPOSITE_XOR,
ALPHA_COMPOSITE_CLEAR = java_awt_AlphaComposite_CLEAR,
ALPHA_COMPOSITE_SRC = java_awt_AlphaComposite_SRC,
ALPHA_COMPOSITE_DST = java_awt_AlphaComposite_DST,
ALPHA_COMPOSITE_SRC_OVER = java_awt_AlphaComposite_SRC_OVER,
ALPHA_COMPOSITE_DST_OVER = java_awt_AlphaComposite_DST_OVER,
ALPHA_COMPOSITE_SRC_IN = java_awt_AlphaComposite_SRC_IN,
ALPHA_COMPOSITE_DST_IN = java_awt_AlphaComposite_DST_IN,
ALPHA_COMPOSITE_SRC_OUT = java_awt_AlphaComposite_SRC_OUT,
ALPHA_COMPOSITE_DST_OUT = java_awt_AlphaComposite_DST_OUT,
ALPHA_COMPOSITE_SRC_ATOP = java_awt_AlphaComposite_SRC_ATOP,
ALPHA_COMPOSITE_DST_ATOP = java_awt_AlphaComposite_DST_ATOP,
ALPHA_COMPOSITE_XOR = java_awt_AlphaComposite_XOR,
ALPHA_COMPOSITE_GROUP = ALPHA_COMPOSITE_XOR,
COMPOSITE_COUNT = ALPHA_COMPOSITE_GROUP + 1,
NO_COMPOSITE = 0x7FFFFFFF
} VKCompositeMode;
#define COMPOSITE_GROUP(COMPOSITE) ( \
(COMPOSITE) <= LOGIC_COMPOSITE_GROUP ? LOGIC_COMPOSITE_GROUP : \
(COMPOSITE) <= ALPHA_COMPOSITE_GROUP ? ALPHA_COMPOSITE_GROUP : \
NO_COMPOSITE )
extern const VkPipelineColorBlendAttachmentState COMPOSITE_BLEND_STATES[COMPOSITE_COUNT];
/**
* Global pipeline context.
*/
@@ -51,7 +80,7 @@ struct VKPipelineContext {
VkSampler linearRepeatSampler;
VKShaders* shaders;
struct VKShaders* shaders;
VKRenderPassContext** renderPassContexts;
};
@@ -59,10 +88,10 @@ struct VKPipelineContext {
* Per-format context.
*/
struct VKRenderPassContext {
VKPipelineContext* pipelineContext;
VkFormat format;
VkRenderPass renderPass;
VKPipelineSet* pipelineSet; // TODO we will need a real hash map for this in the future.
VKPipelineContext* pipelineContext;
VkFormat format;
VkRenderPass renderPass;
struct VKPipelineSet** pipelineSets; // TODO we will need a real hash map for this in the future.
};
typedef struct {
@@ -79,6 +108,6 @@ VKPipelineContext* VKPipelines_CreateContext(VKDevice* device);
void VKPipelines_DestroyContext(VKPipelineContext* pipelines);
VKRenderPassContext* VKPipelines_GetRenderPassContext(VKPipelineContext* pipelineContext, VkFormat format);
VkPipeline VKPipelines_GetPipeline(VKRenderPassContext* renderPassContext, VKPipeline pipeline);
VkPipeline VKPipelines_GetPipeline(VKRenderPassContext* renderPassContext, VKCompositeMode composite, VKPipeline pipeline);
#endif //VKPipelines_h_Included

View File

@@ -94,9 +94,18 @@
// Rendering context is only accessed from VKRenderQueue_flushBuffer,
// which is only called from queue flusher thread, no need for synchronization.
static VKRenderingContext context = {
NULL, {},
{1.0, 0.0, 0.0,0.0, 1.0, 0.0},
{{0, 0}, {INT_MAX, INT_MAX}}};
.surface = NULL,
.transform = {1.0, 0.0, 0.0,0.0, 1.0, 0.0},
.clipRect = {{0, 0},{INT_MAX, INT_MAX}},
.color = {},
.composite = ALPHA_COMPOSITE_SRC_OVER,
.extraAlpha = 1.0f
};
// We keep this color separately from context.color,
// because we need consistent state when switching between XOR and alpha composite modes.
// This variable holds last value set by SET_COLOR, while context.color holds color,
// currently used for drawing, which may have also been provided by SET_XOR_COMPOSITE.
Color color;
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
(JNIEnv *env, jobject oglrq, jlong buf, jint limit)
@@ -430,11 +439,14 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
break;
case sun_java2d_pipe_BufferedOpCodes_SET_ALPHA_COMPOSITE:
{
jint rule = NEXT_INT(b);
jint rule = NEXT_INT(b);
jfloat extraAlpha = NEXT_FLOAT(b);
jint flags = NEXT_INT(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_ALPHA_COMPOSITE");
jint flags = NEXT_INT(b);
J2dRlsTraceLn3(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_ALPHA_COMPOSITE(%d, %f, %d)", rule, extraAlpha, flags);
context.color = color;
context.composite = (VKCompositeMode) rule;
context.extraAlpha = extraAlpha;
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_XOR_COMPOSITE:
@@ -442,12 +454,19 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jint xorPixel = NEXT_INT(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_XOR_COMPOSITE");
context.color = VKUtil_DecodeJavaColor(xorPixel);
context.color.a = 0.0f; // Alpha is left unchanged in XOR mode.
context.composite = LOGIC_COMPOSITE_XOR;
context.extraAlpha = 1.0f;
}
break;
case sun_java2d_pipe_BufferedOpCodes_RESET_COMPOSITE:
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: RESET_COMPOSITE");
context.color = color;
context.composite = ALPHA_COMPOSITE_SRC;
context.extraAlpha = 1.0f;
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_TRANSFORM:
@@ -582,10 +601,11 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
case sun_java2d_pipe_BufferedOpCodes_SET_COLOR:
{
jint javaColor = NEXT_INT(b);
context.color = VKUtil_DecodeJavaColor(javaColor);
J2dRlsTraceLn5(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_COLOR 0x%08x, linear_rgba={%.3f, %.3f, %.3f, %.3f}",
javaColor, context.color.r, context.color.g, context.color.b, context.color.a);
color = VKUtil_DecodeJavaColor(javaColor);
if (COMPOSITE_GROUP(context.composite) == ALPHA_COMPOSITE_GROUP) context.color = color;
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: SET_COLOR(0x%08x)", javaColor);
J2dTraceLn4(J2D_TRACE_VERBOSE, // Print color values with straight alpha for convenience.
" srgb={%.3f, %.3f, %.3f, %.3f}", color.r/color.a, color.g/color.a, color.b/color.a, color.a);
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_GRADIENT_PAINT:

View File

@@ -142,11 +142,12 @@ struct VKRenderPass {
uint32_t vertexCount;
BufferWritingState vertexBufferWriting;
VKPipeline currentPipeline;
VkBool32 pendingFlush;
VkBool32 pendingCommands;
VkBool32 pendingClear;
uint64_t lastTimestamp; // When was this surface last used?
VKCompositeMode currentComposite;
VKPipeline currentPipeline;
VkBool32 pendingFlush;
VkBool32 pendingCommands;
VkBool32 pendingClear;
uint64_t lastTimestamp; // When was this surface last used?
};
/**
@@ -464,6 +465,7 @@ inline void VKRenderer_FlushDraw(VKSDOps* surface) {
*/
static void VKRenderer_ResetDrawing(VKSDOps* surface) {
assert(surface != NULL && surface->renderPass != NULL);
surface->renderPass->currentComposite = NO_COMPOSITE;
surface->renderPass->currentPipeline = NO_PIPELINE;
surface->renderPass->firstVertex = 0;
surface->renderPass->vertexCount = 0;
@@ -502,9 +504,7 @@ void VKRenderer_DestroyRenderPass(VKSDOps* surface) {
VKRenderer_Wait(device->renderer, surface->renderPass->lastTimestamp);
VKRenderer_DiscardRenderPass(surface);
// Release resources.
if (surface->renderPass->framebuffer != VK_NULL_HANDLE) {
device->vkDestroyFramebuffer(device->handle, surface->renderPass->framebuffer, NULL);
}
device->vkDestroyFramebuffer(device->handle, surface->renderPass->framebuffer, NULL);
if (surface->renderPass->commandBuffer != VK_NULL_HANDLE) {
POOL_RETURN(device->renderer, secondaryCommandBufferPool, surface->renderPass->commandBuffer);
}
@@ -535,6 +535,7 @@ static VkBool32 VKRenderer_InitRenderPass(VKSDOps* surface) {
(*renderPass) = (VKRenderPass) {
.pendingCommands = VK_FALSE,
.pendingClear = VK_TRUE, // Clear the surface by default
.currentComposite = NO_COMPOSITE,
.currentPipeline = NO_PIPELINE,
.lastTimestamp = 0
};
@@ -556,7 +557,7 @@ static VkBool32 VKRenderer_InitRenderPass(VKSDOps* surface) {
.layers = 1
};
VK_IF_ERROR(device->vkCreateFramebuffer(device->handle, &framebufferCreateInfo, NULL,
&renderPass->framebuffer)) VK_UNHANDLED_ERROR();
&renderPass->framebuffer)) VK_UNHANDLED_ERROR();
}
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "VKRenderer_InitRenderPass(%p)", surface);
@@ -888,28 +889,39 @@ VkBool32 VKRenderer_Validate(VKRenderingContext* context, VKPipeline pipeline) {
assert(context != NULL && context->surface != NULL);
VKSDOps* surface = context->surface;
// Validate render pass state.
// Init render pass.
if (surface->renderPass == NULL || !surface->renderPass->pendingCommands) {
// We must only [re]init render pass between frames.
// Now this is correct, but in future we may have frames consisting of multiple render passes,
// so we must be careful to NOT call VKRenderer_InitRenderPass between render passes within single frame.
// Be careful to NOT call VKRenderer_InitRenderPass between render passes within single frame.
if (!VKRenderer_InitRenderPass(surface)) return VK_FALSE;
// In the future, we may need to restart the render pass within single frame,
// for example when switching between blended and XOR drawing modes.
// So, generally, this should depend on VKRenderingContext, but now we just start the render pass once.
VKRenderer_BeginRenderPass(surface);
}
VKRenderPass* renderPass = surface->renderPass;
// Validate render pass state.
if (renderPass->currentComposite != context->composite) {
// ALPHA_COMPOSITE_DST keeps destination intact, so don't even bother to change the state.
if (context->composite == ALPHA_COMPOSITE_DST) return VK_FALSE;
VKCompositeMode oldComposite = renderPass->currentComposite;
// Update state.
VKRenderer_FlushDraw(surface);
renderPass->currentComposite = context->composite;
// Begin render pass.
if (!renderPass->pendingCommands) VKRenderer_BeginRenderPass(surface);
// Validate current composite.
J2dTraceLn2(J2D_TRACE_VERBOSE, "VKRenderer_Validate: updating composite, old=%d, new=%d", oldComposite, context->composite);
// Reset the pipeline.
renderPass->currentPipeline = NO_PIPELINE;
}
// Validate current pipeline.
if (renderPass->currentPipeline != pipeline) {
J2dTraceLn2(J2D_TRACE_VERBOSE, "VKRenderer_Validate: updating pipeline, old=%d, new=%d",
surface->renderPass->currentPipeline, pipeline);
renderPass->currentPipeline, pipeline);
VKRenderer_FlushDraw(surface);
VkCommandBuffer cb = renderPass->commandBuffer;
renderPass->currentPipeline = pipeline;
surface->device->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS,
VKPipelines_GetPipeline(renderPass->context, pipeline));
VKPipelines_GetPipeline(renderPass->context, context->composite, pipeline));
renderPass->vertexBufferWriting.bound = VK_FALSE;
}
return VK_TRUE;

View File

@@ -31,10 +31,14 @@
#include "VKPipelines.h"
struct VKRenderingContext {
VKSDOps* surface;
Color color;
VKTransform transform;
VkRect2D clipRect;
VKSDOps* surface;
VKTransform transform;
VkRect2D clipRect;
Color color;
VKCompositeMode composite;
// Extra alpha is not used when painting with plain color,
// in this case color.a already includes it.
float extraAlpha;
};
typedef struct {

View File

@@ -84,7 +84,10 @@ VkBool32 VKSD_ConfigureImageSurface(VKSDOps* vksdo) {
vksdo->requestedExtent.width != vksdo->image->extent.width ||
vksdo->requestedExtent.height != vksdo->image->extent.height)) {
// VK_FORMAT_B8G8R8A8_UNORM is the most widely-supported format for our use.
// Currently, we only support *_SRGB and *_UNORM formats,
// as other types may not be trivial to alias for logicOp rendering.
VkFormat format = VK_FORMAT_B8G8R8A8_UNORM;
VKImage* image = VKImage_Create(device, vksdo->requestedExtent.width, vksdo->requestedExtent.height,
0, format, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,

View File

@@ -24,14 +24,13 @@
* questions.
*/
#include <stdlib.h>
#include <pthread.h>
#include "VKImage.h"
#include "VKUtil.h"
#include "VKTexturePool.h"
#include "AccelTexturePool.h"
#include "jni.h"
#include "jni_util.h"
#include "Trace.h"
@@ -103,15 +102,11 @@ static ATexturePrivPtr* VKTexturePool_createTexture(ADevicePrivPtr *device,
}
static int VKTexturePool_bytesPerPixel(long format) {
switch ((VkFormat)format) {
case VK_FORMAT_R8G8B8A8_UNORM:
return 4;
case VK_FORMAT_R8_UNORM:
return 1;
default:
J2dRlsTraceLn1(J2D_TRACE_ERROR, "VKTexturePool_bytesPerPixel: format=%d not supported (4 bytes by default)", format);
return 4;
}
FormatGroup group = VKUtil_GetFormatGroup((VkFormat)format);
if (group.bytes == 0) {
J2dRlsTraceLn1(J2D_TRACE_ERROR, "VKTexturePool_bytesPerPixel: format=%d not supported (4 bytes by default)", format);
return 4;
} else return (int) group.bytes;
}
static void VKTexturePool_freeTexture(ADevicePrivPtr *device, ATexturePrivPtr *texture) {

View File

@@ -26,7 +26,7 @@
#include <vulkan/vulkan.h>
/**
* Floating-point RGBA color with sRGB encoding.
* Floating-point RGBA color with sRGB encoding and pre-multiplied alpha.
*/
typedef union {
struct {
@@ -56,7 +56,6 @@ typedef struct VKRenderPass VKRenderPass;
typedef struct VKRenderingContext VKRenderingContext;
typedef struct VKPipelineContext VKPipelineContext;
typedef struct VKRenderPassContext VKRenderPassContext;
typedef struct VKShaders VKShaders;
typedef struct VKBuffer VKBuffer;
typedef struct VKImage VKImage;
typedef struct VKSDOps VKSDOps;

View File

@@ -21,9 +21,11 @@
// or visit www.oracle.com if you need additional information or have any
// questions.
#include <assert.h>
#include "VKUtil.h"
Color VKUtil_DecodeJavaColor(uint32_t srgb) {
Color VKUtil_DecodeJavaColor(uint32_t color) {
assert(sizeof(Color) == sizeof(float) * 4);
// Just map [0, 255] integer colors onto [0, 1] floating-point range, it remains in sRGB color space.
// sRGB gamma correction remains unsupported.
static const float NormTable256[256] = {
@@ -32,13 +34,17 @@ Color VKUtil_DecodeJavaColor(uint32_t srgb) {
#define NORM64(N) NORM8(N),NORM8(N+8),NORM8(N+16),NORM8(N+24),NORM8(N+32),NORM8(N+40),NORM8(N+48),NORM8(N+56)
NORM64(0),NORM64(64),NORM64(128),NORM64(192)
};
Color c = {
.r = NormTable256[(srgb >> 16) & 0xFF],
.g = NormTable256[(srgb >> 8) & 0xFF],
.b = NormTable256[ srgb & 0xFF],
.a = NormTable256[(srgb >> 24) & 0xFF]
Color srgb = {
.r = NormTable256[(color >> 16) & 0xFF],
.g = NormTable256[(color >> 8) & 0xFF],
.b = NormTable256[ color & 0xFF],
.a = NormTable256[(color >> 24) & 0xFF]
};
return c;
// Convert to pre-multiplied alpha.
srgb.r *= srgb.a;
srgb.g *= srgb.a;
srgb.b *= srgb.a;
return srgb;
}
uint32_t VKUtil_Log2(uint64_t i) {
@@ -59,6 +65,209 @@ uint32_t VKUtil_Log2(uint64_t i) {
else return LogTable256[i & 0xFF];
}
FormatGroup VKUtil_GetFormatGroup(VkFormat format) {
#define GROUP(SIZE, ...) return ((FormatGroup) { .bytes = SIZE, .aliases[FORMAT_ALIAS_ORIGINAL] = format, __VA_ARGS__ })
switch (format) {
case VK_FORMAT_R4G4_UNORM_PACK8: GROUP(1, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R4G4_UNORM_PACK8);
case VK_FORMAT_R4G4B4A4_UNORM_PACK16: GROUP(2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R4G4B4A4_UNORM_PACK16);
case VK_FORMAT_B4G4R4A4_UNORM_PACK16: GROUP(2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_B4G4R4A4_UNORM_PACK16);
case VK_FORMAT_R5G6B5_UNORM_PACK16: GROUP(2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R5G6B5_UNORM_PACK16);
case VK_FORMAT_B5G6R5_UNORM_PACK16: GROUP(2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_B5G6R5_UNORM_PACK16);
case VK_FORMAT_R5G5B5A1_UNORM_PACK16: GROUP(2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R5G5B5A1_UNORM_PACK16);
case VK_FORMAT_B5G5R5A1_UNORM_PACK16: GROUP(2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_B5G5R5A1_UNORM_PACK16);
case VK_FORMAT_A1R5G5B5_UNORM_PACK16: GROUP(2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_A1R5G5B5_UNORM_PACK16);
case VK_FORMAT_R8_UNORM:
case VK_FORMAT_R8_SNORM:
case VK_FORMAT_R8_USCALED:
case VK_FORMAT_R8_SSCALED:
case VK_FORMAT_R8_UINT:
case VK_FORMAT_R8_SINT:
case VK_FORMAT_R8_SRGB:
GROUP(1, .aliases[FORMAT_ALIAS_SRGB] = VK_FORMAT_R8_SRGB,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R8_UNORM, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_R8_SNORM,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_R8_USCALED, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_R8_SSCALED,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R8_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R8_SINT);
case VK_FORMAT_R8G8_UNORM:
case VK_FORMAT_R8G8_SNORM:
case VK_FORMAT_R8G8_USCALED:
case VK_FORMAT_R8G8_SSCALED:
case VK_FORMAT_R8G8_UINT:
case VK_FORMAT_R8G8_SINT:
case VK_FORMAT_R8G8_SRGB:
GROUP(2, .aliases[FORMAT_ALIAS_SRGB] = VK_FORMAT_R8G8_SRGB,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R8G8_UNORM, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_R8G8_SNORM,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_R8G8_USCALED, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_R8G8_SSCALED,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R8G8_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R8G8_SINT);
case VK_FORMAT_R8G8B8_UNORM:
case VK_FORMAT_R8G8B8_SNORM:
case VK_FORMAT_R8G8B8_USCALED:
case VK_FORMAT_R8G8B8_SSCALED:
case VK_FORMAT_R8G8B8_UINT:
case VK_FORMAT_R8G8B8_SINT:
case VK_FORMAT_R8G8B8_SRGB:
GROUP(3, .aliases[FORMAT_ALIAS_SRGB] = VK_FORMAT_R8G8B8_SRGB,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R8G8B8_UNORM, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_R8G8B8_SNORM,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_R8G8B8_USCALED, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_R8G8B8_SSCALED,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R8G8B8_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R8G8B8_SINT);
case VK_FORMAT_B8G8R8_UNORM:
case VK_FORMAT_B8G8R8_SNORM:
case VK_FORMAT_B8G8R8_USCALED:
case VK_FORMAT_B8G8R8_SSCALED:
case VK_FORMAT_B8G8R8_UINT:
case VK_FORMAT_B8G8R8_SINT:
case VK_FORMAT_B8G8R8_SRGB:
GROUP(3, .aliases[FORMAT_ALIAS_SRGB] = VK_FORMAT_B8G8R8_SRGB,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_B8G8R8_UNORM, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_B8G8R8_SNORM,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_B8G8R8_USCALED, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_B8G8R8_SSCALED,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_B8G8R8_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_B8G8R8_SINT);
case VK_FORMAT_R8G8B8A8_UNORM:
case VK_FORMAT_R8G8B8A8_SNORM:
case VK_FORMAT_R8G8B8A8_USCALED:
case VK_FORMAT_R8G8B8A8_SSCALED:
case VK_FORMAT_R8G8B8A8_UINT:
case VK_FORMAT_R8G8B8A8_SINT:
case VK_FORMAT_R8G8B8A8_SRGB:
GROUP(4, .aliases[FORMAT_ALIAS_SRGB] = VK_FORMAT_R8G8B8A8_SRGB,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R8G8B8A8_UNORM, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_R8G8B8A8_SNORM,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_R8G8B8A8_USCALED, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_R8G8B8A8_SSCALED,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R8G8B8A8_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R8G8B8A8_SINT);
case VK_FORMAT_B8G8R8A8_UNORM:
case VK_FORMAT_B8G8R8A8_SNORM:
case VK_FORMAT_B8G8R8A8_USCALED:
case VK_FORMAT_B8G8R8A8_SSCALED:
case VK_FORMAT_B8G8R8A8_UINT:
case VK_FORMAT_B8G8R8A8_SINT:
case VK_FORMAT_B8G8R8A8_SRGB:
GROUP(4, .aliases[FORMAT_ALIAS_SRGB] = VK_FORMAT_B8G8R8A8_SRGB,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_B8G8R8A8_UNORM, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_B8G8R8A8_SNORM,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_B8G8R8A8_USCALED, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_B8G8R8A8_SSCALED,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_B8G8R8A8_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_B8G8R8A8_SINT);
case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
case VK_FORMAT_A8B8G8R8_SNORM_PACK32:
case VK_FORMAT_A8B8G8R8_USCALED_PACK32:
case VK_FORMAT_A8B8G8R8_SSCALED_PACK32:
case VK_FORMAT_A8B8G8R8_UINT_PACK32:
case VK_FORMAT_A8B8G8R8_SINT_PACK32:
case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
GROUP(4, .aliases[FORMAT_ALIAS_SRGB] = VK_FORMAT_A8B8G8R8_SRGB_PACK32,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_A8B8G8R8_UNORM_PACK32, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_A8B8G8R8_SNORM_PACK32,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_A8B8G8R8_USCALED_PACK32, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_A8B8G8R8_SSCALED_PACK32,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_A8B8G8R8_UINT_PACK32, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_A8B8G8R8_SINT_PACK32);
case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
case VK_FORMAT_A2R10G10B10_SNORM_PACK32:
case VK_FORMAT_A2R10G10B10_USCALED_PACK32:
case VK_FORMAT_A2R10G10B10_SSCALED_PACK32:
case VK_FORMAT_A2R10G10B10_UINT_PACK32:
case VK_FORMAT_A2R10G10B10_SINT_PACK32:
GROUP(4,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_A2R10G10B10_UNORM_PACK32, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_A2R10G10B10_SNORM_PACK32,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_A2R10G10B10_USCALED_PACK32, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_A2R10G10B10_SSCALED_PACK32,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_A2R10G10B10_UINT_PACK32, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_A2R10G10B10_SINT_PACK32);
case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
case VK_FORMAT_A2B10G10R10_SNORM_PACK32:
case VK_FORMAT_A2B10G10R10_USCALED_PACK32:
case VK_FORMAT_A2B10G10R10_SSCALED_PACK32:
case VK_FORMAT_A2B10G10R10_UINT_PACK32:
case VK_FORMAT_A2B10G10R10_SINT_PACK32:
GROUP(4,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_A2B10G10R10_UNORM_PACK32, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_A2B10G10R10_SNORM_PACK32,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_A2B10G10R10_USCALED_PACK32, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_A2B10G10R10_SSCALED_PACK32,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_A2B10G10R10_UINT_PACK32, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_A2B10G10R10_SINT_PACK32);
case VK_FORMAT_R16_UNORM:
case VK_FORMAT_R16_SNORM:
case VK_FORMAT_R16_USCALED:
case VK_FORMAT_R16_SSCALED:
case VK_FORMAT_R16_UINT:
case VK_FORMAT_R16_SINT:
case VK_FORMAT_R16_SFLOAT:
GROUP(2, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R16_SFLOAT,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R16_UNORM, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_R16_SNORM,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_R16_USCALED, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_R16_SSCALED,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R16_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R16_SINT);
case VK_FORMAT_R16G16_UNORM:
case VK_FORMAT_R16G16_SNORM:
case VK_FORMAT_R16G16_USCALED:
case VK_FORMAT_R16G16_SSCALED:
case VK_FORMAT_R16G16_UINT:
case VK_FORMAT_R16G16_SINT:
case VK_FORMAT_R16G16_SFLOAT:
GROUP(4, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R16G16_SFLOAT,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R16G16_UNORM, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_R16G16_SNORM,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_R16G16_USCALED, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_R16G16_SSCALED,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R16G16_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R16G16_SINT);
case VK_FORMAT_R16G16B16_UNORM:
case VK_FORMAT_R16G16B16_SNORM:
case VK_FORMAT_R16G16B16_USCALED:
case VK_FORMAT_R16G16B16_SSCALED:
case VK_FORMAT_R16G16B16_UINT:
case VK_FORMAT_R16G16B16_SINT:
case VK_FORMAT_R16G16B16_SFLOAT:
GROUP(6, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R16G16B16_SFLOAT,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R16G16B16_UNORM, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_R16G16B16_SNORM,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_R16G16B16_USCALED, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_R16G16B16_SSCALED,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R16G16B16_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R16G16B16_SINT);
case VK_FORMAT_R16G16B16A16_UNORM:
case VK_FORMAT_R16G16B16A16_SNORM:
case VK_FORMAT_R16G16B16A16_USCALED:
case VK_FORMAT_R16G16B16A16_SSCALED:
case VK_FORMAT_R16G16B16A16_UINT:
case VK_FORMAT_R16G16B16A16_SINT:
case VK_FORMAT_R16G16B16A16_SFLOAT:
GROUP(8, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R16G16B16A16_SFLOAT,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R16G16B16A16_UNORM, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_R16G16B16A16_SNORM,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_R16G16B16A16_USCALED, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_R16G16B16A16_SSCALED,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R16G16B16A16_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R16G16B16A16_SINT);
case VK_FORMAT_R32_UINT:
case VK_FORMAT_R32_SINT:
case VK_FORMAT_R32_SFLOAT:
GROUP(4, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R32_SFLOAT,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R32_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R32_SINT);
case VK_FORMAT_R32G32_UINT:
case VK_FORMAT_R32G32_SINT:
case VK_FORMAT_R32G32_SFLOAT:
GROUP(8, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R32G32_SFLOAT,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R32G32_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R32G32_SINT);
case VK_FORMAT_R32G32B32_UINT:
case VK_FORMAT_R32G32B32_SINT:
case VK_FORMAT_R32G32B32_SFLOAT:
GROUP(12, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R32G32B32_SFLOAT,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R32G32B32_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R32G32B32_SINT);
case VK_FORMAT_R32G32B32A32_UINT:
case VK_FORMAT_R32G32B32A32_SINT:
case VK_FORMAT_R32G32B32A32_SFLOAT:
GROUP(16, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R32G32B32A32_SFLOAT,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R32G32B32A32_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R32G32B32A32_SINT);
case VK_FORMAT_R64_UINT:
case VK_FORMAT_R64_SINT:
case VK_FORMAT_R64_SFLOAT:
GROUP(8, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R64_SFLOAT,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R64_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R64_SINT);
case VK_FORMAT_R64G64_UINT:
case VK_FORMAT_R64G64_SINT:
case VK_FORMAT_R64G64_SFLOAT:
GROUP(16, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R64G64_SFLOAT,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R64G64_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R64G64_SINT);
case VK_FORMAT_R64G64B64_UINT:
case VK_FORMAT_R64G64B64_SINT:
case VK_FORMAT_R64G64B64_SFLOAT:
GROUP(24, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R64G64B64_SFLOAT,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R64G64B64_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R64G64B64_SINT);
case VK_FORMAT_R64G64B64A64_UINT:
case VK_FORMAT_R64G64B64A64_SINT:
case VK_FORMAT_R64G64B64A64_SFLOAT:
GROUP(32, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R64G64B64A64_SFLOAT,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R64G64B64A64_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R64G64B64A64_SINT);
case VK_FORMAT_R10X6_UNORM_PACK16: GROUP(2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R10X6_UNORM_PACK16);
case VK_FORMAT_R10X6G10X6_UNORM_2PACK16: GROUP(4, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R10X6G10X6_UNORM_2PACK16);
case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16: GROUP(8, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16);
case VK_FORMAT_R12X4_UNORM_PACK16: GROUP(2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R12X4_UNORM_PACK16);
case VK_FORMAT_R12X4G12X4_UNORM_2PACK16: GROUP(4, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R12X4G12X4_UNORM_2PACK16);
case VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16: GROUP(8, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16);
default: GROUP(0);
}
#undef GROUP
}
void VKUtil_LogResultError(const char* string, VkResult result) {
const char* r;
switch (result) {

View File

@@ -69,13 +69,37 @@ inline VkBool32 VKUtil_CheckError(VkResult result, const char* errorMessage) {
#define VK_UNHANDLED_ERROR() VK_FATAL_ERROR("Unhandled Vulkan error")
#define VK_RUNTIME_ASSERT(...) if (!(__VA_ARGS__)) VK_FATAL_ERROR("Vulkan assertion failed: " #__VA_ARGS__)
typedef enum {
FORMAT_ALIAS_ORIGINAL = 0,
FORMAT_ALIAS_UNORM = 1,
FORMAT_ALIAS_SNORM = 2,
FORMAT_ALIAS_USCALED = 3,
FORMAT_ALIAS_SSCALED = 4,
FORMAT_ALIAS_UINT = 5,
FORMAT_ALIAS_SINT = 6,
FORMAT_ALIAS_SFLOAT = 7,
FORMAT_ALIAS_SRGB = 8,
FORMAT_ALIAS_COUNT = 9
} FormatAlias;
/**
* Group of format aliases. Use FormatAlias enum values to index into FormatGroup.aliases.
*/
typedef struct {
VkFormat aliases[FORMAT_ALIAS_COUNT];
uint bytes;
} FormatGroup;
/**
* Vulkan expects linear colors.
* However Java2D expects legacy behavior, as if colors were blended in sRGB color space.
* Therefore this function just remaps color components from [0, 255] to [0, 1] range,
* they still represent sRGB color.
* Therefore this function converts straight-alpha Java color in range [0, 255]
* to pre-multiplied alpha normalized [0, 1] color, still representing sRGB color.
* This is also accounted for in VKSD_ConfigureWindowSurface, so that Vulkan doesn't do any
* color space conversions on its own, as the colors we are drawing are already in sRGB.
*
* Note: we receive colors from Java with straight (non-premultiplied) alpha, which is done to prevent precision loss.
* This is controlled by PixelConverter parameter of SurfaceType, see VKSurfaceData.java.
*/
Color VKUtil_DecodeJavaColor(uint32_t color);
@@ -84,6 +108,11 @@ Color VKUtil_DecodeJavaColor(uint32_t color);
*/
uint32_t VKUtil_Log2(uint64_t i);
/**
* Get group of formats with the same component layout.
*/
FormatGroup VKUtil_GetFormatGroup(VkFormat format);
/*
* The following macros allow the caller to return (or continue) if the
* provided value is NULL. (The strange else clause is included below to

View File

@@ -82,25 +82,6 @@ public class WLComponentPeer implements ComponentPeer {
private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.awt.wl.focus.WLComponentPeer");
private static final PlatformLogger popupLog = PlatformLogger.getLogger("sun.awt.wl.popup.WLComponentPeer");
// mapping of AWT cursor types to X cursor names
// multiple variants can be specified, that will be tried in order
private static final String[][] CURSOR_NAMES = {
{"default", "arrow", "left_ptr", "left_arrow"}, // DEFAULT_CURSOR
{"crosshair"}, // CROSSHAIR_CURSOR
{"text", "xterm"}, // TEXT_CURSOR
{"wait", "watch"}, // WAIT_CURSOR
{"sw-resize", "bottom_left_corner"}, // SW_RESIZE_CURSOR
{"se-resize", "bottom_right_corner"}, // SE_RESIZE_CURSOR
{"nw-resize", "top_left_corner"}, // NW_RESIZE_CURSOR
{"ne-resize", "top_right_corner"}, // NE_RESIZE_CURSOR
{"n-resize", "top_side"}, // N_RESIZE_CURSOR
{"s-resize", "bottom_side"}, // S_RESIZE_CURSOR
{"w-resize", "left_side"}, // W_RESIZE_CURSOR
{"e-resize", "right_side"}, // E_RESIZE_CURSOR
{"hand"}, // HAND_CURSOR
{"move"}, // MOVE_CURSOR
};
private static final int MINIMUM_WIDTH = 1;
private static final int MINIMUM_HEIGHT = 1;
@@ -123,6 +104,7 @@ public class WLComponentPeer implements ComponentPeer {
int displayScale; // protected by dataLock
double effectiveScale; // protected by dataLock
private final WLSize wlSize = new WLSize();
boolean repositionPopup = false; // protected by dataLock
static {
initIDs();
@@ -278,6 +260,12 @@ public class WLComponentPeer implements ComponentPeer {
return null;
}
private static Component realParentFor(Component c) {
return (c instanceof Window window && isWlPopup(window))
? AWTAccessor.getWindowAccessor().getPopupParent(window)
: c.getParent();
}
static Point getRelativeLocation(Component c, Window toplevel) {
Objects.requireNonNull(c);
@@ -286,26 +274,24 @@ public class WLComponentPeer implements ComponentPeer {
}
int x = 0, y = 0;
while (c != null) {
if (c instanceof Window window) {
// The location of non-popup windows has no relevance since
// there are no absolute coordinates in Wayland.
// The popup windows position, on the other hand, is set relative to their
// parent toplevel.
if (isWlPopup(window)) {
x += c.getX();
y += c.getY();
}
break;
}
while (c != null && c != toplevel) {
x += c.getX();
y += c.getY();
c = c.getParent();
c = realParentFor(c);
}
return new Point(x, y);
}
Point nativeLocationForPopup(Window popup, Component popupParent, Window toplevel) {
// We need to provide popup's "parent" location relative to the surface this parent is painted upon:
Point parentLocation = javaUnitsToSurfaceUnits(getRelativeLocation(popupParent, toplevel));
// Offset is relative to the top-left corner of the "parent".
Point offsetFromParent = javaUnitsToSurfaceUnits(popup.getLocation());
return new Point(parentLocation.x + offsetFromParent.x, parentLocation.y + offsetFromParent.y);
}
protected void wlSetVisible(boolean v) {
synchronized (getStateLock()) {
if (this.visible == v) return;
@@ -327,30 +313,12 @@ public class WLComponentPeer implements ComponentPeer {
performLocked(() -> {
if (isWlPopup) {
Window popup = (Window) target;
final Component popupParent = AWTAccessor.getWindowAccessor().getPopupParent(popup);
final Window toplevel = getToplevelFor(popupParent);
// We need to provide popup "parent" location relative to
// the surface it is painted upon:
final Point toplevelLocation = getRelativeLocation(popupParent, toplevel);
final int parentX = javaUnitsToSurfaceUnits(toplevelLocation.x);
final int parentY = javaUnitsToSurfaceUnits(toplevelLocation.y);
// Offset must be relative to the top-left corner of the "parent".
final Point offsetFromParent = popup.getLocation();
final int offsetX = javaUnitsToSurfaceUnits(offsetFromParent.x);
final int offsetY = javaUnitsToSurfaceUnits(offsetFromParent.y);
if (popupLog.isLoggable(PlatformLogger.Level.FINE)) {
popupLog.fine("New popup: " + popup);
popupLog.fine("\tparent:" + popupParent);
popupLog.fine("\ttoplevel: " + toplevel);
popupLog.fine("\toffset of anchor from toplevel: " + toplevelLocation);
popupLog.fine("\toffset from anchor: " + offsetFromParent);
}
Component popupParent = AWTAccessor.getWindowAccessor().getPopupParent(popup);
Window toplevel = getToplevelFor(popupParent);
Point nativeLocation = nativeLocationForPopup(popup, popupParent, toplevel);
nativeCreateWLPopup(nativePtr, getNativePtrFor(toplevel),
thisWidth, thisHeight,
parentX + offsetX, parentY + offsetY);
nativeLocation.x, nativeLocation.y);
} else {
int xNative = javaUnitsToSurfaceUnits(target.getX());
int yNative = javaUnitsToSurfaceUnits(target.getY());
@@ -421,6 +389,18 @@ public class WLComponentPeer implements ComponentPeer {
if (surfaceMaxSize != null) {
nativeSetMaximumSize(nativePtr, surfaceMaxSize.width, surfaceMaxSize.height);
}
if (popupNeedsReposition()) {
popupRepositioned();
// Since popup's reposition request includes both its size and location, the request
// needs to be in sync with all the other sizes this method is responsible for updating.
Window popup = (Window) target;
final Component popupParent = AWTAccessor.getWindowAccessor().getPopupParent(popup);
final Window toplevel = getToplevelFor(popupParent);
Point nativeLocation = nativeLocationForPopup(popup, popupParent, toplevel);
nativeRepositionWLPopup(nativePtr, surfaceWidth, surfaceHeight, nativeLocation.x, nativeLocation.y);
}
}
void configureWLSurface() {
@@ -505,11 +485,28 @@ public class WLComponentPeer implements ComponentPeer {
}
}
private void setLocationTo(int newX, int newY) {
private void resetTargetLocationTo(int newX, int newY) {
var acc = AWTAccessor.getComponentAccessor();
acc.setLocation(target, newX, newY);
}
private boolean popupNeedsReposition() {
synchronized (dataLock) {
return repositionPopup;
}
}
private void markPopupNeedsReposition() {
synchronized (dataLock) {
repositionPopup = true;
}
}
private void popupRepositioned() {
synchronized (dataLock) {
repositionPopup = false;
}
}
public void setBounds(int newX, int newY, int newWidth, int newHeight, int op) {
Dimension newSize = constrainSize(newWidth, newHeight);
boolean positionChanged = (op == SET_BOUNDS || op == SET_LOCATION);
@@ -527,9 +524,7 @@ public class WLComponentPeer implements ComponentPeer {
if ((positionChanged || sizeChanged) && isPopup && visible) {
// Need to update the location and size even if does not (yet) have a surface
// as the initial configure event needs to have the latest data on the location/size.
repositionWlPopup(newX, newY, newSize.width, newSize.height);
// the location will be updated in notifyConfigured() following
// the xdg_popup::repositioned event
markPopupNeedsReposition();
}
if (positionChanged) {
@@ -546,9 +541,9 @@ public class WLComponentPeer implements ComponentPeer {
layout();
WLToolkit.postEvent(new ComponentEvent(getTarget(), ComponentEvent.COMPONENT_RESIZED));
}
postPaintEvent();
postPaintEvent(); // no need to repaint after being moved, only when resized
}
}
boolean isSizeBeingConfigured() {
@@ -564,43 +559,17 @@ public class WLComponentPeer implements ComponentPeer {
}
private void setSizeTo(int newWidth, int newHeight) {
Dimension newSize = constrainSize(newWidth, newHeight);
if (isSizeBeingConfigured() && wlSize.hasPixelSizeSet()) {
// Must be careful not to override the size of the Wayland surface because
// some implementations (Weston) react badly when the size of the surface
// mismatches the configured size. We can't always precisely derive the surface
// size from the Java (client) size because of scaling rounding errors.
wlSize.setJavaSize(newSize.width, newSize.height);
wlSize.setJavaSize(newWidth, newHeight);
} else {
wlSize.deriveFromJavaSize(newSize.width, newSize.height);
wlSize.deriveFromJavaSize(newWidth, newHeight);
}
}
private void repositionWlPopup(int newX, int newY, int newWidth, int newHeight) {
performLocked(() -> {
Window popup = (Window) target;
final Component popupParent = AWTAccessor.getWindowAccessor().getPopupParent(popup);
final Window toplevel = getToplevelFor(popupParent);
// We need to provide popup "parent" location relative to
// the surface it is painted upon:
final Point toplevelLocation = getRelativeLocation(popupParent, toplevel);
final int parentX = javaUnitsToSurfaceUnits(toplevelLocation.x);
final int parentY = javaUnitsToSurfaceUnits(toplevelLocation.y);
int newXNative = javaUnitsToSurfaceUnits(newX);
int newYNative = javaUnitsToSurfaceUnits(newY);
int newWidthNative = javaUnitsToSurfaceUnits(newWidth);
int newHeightNative = javaUnitsToSurfaceUnits(newHeight);
if (popupLog.isLoggable(Level.FINE)) {
popupLog.fine("Repositioning popup: " + popup);
popupLog.fine("\tparent:" + popupParent);
popupLog.fine("\ttoplevel: " + toplevel);
popupLog.fine("\toffset of anchor from toplevel: " + toplevelLocation);
popupLog.fine("\toffset from anchor: " + newX + ", " + newY);
}
nativeRepositionWLPopup(nativePtr, newWidthNative, newHeightNative, parentX + newXNative, parentY + newYNative);
} );
}
public int getBufferWidth() {
return wlSize.getPixelWidth();
}
@@ -645,12 +614,23 @@ public class WLComponentPeer implements ComponentPeer {
try {
return WLRobotPeer.getLocationOfWLSurface(wlSurfacePtr);
} catch (UnsupportedOperationException ignore) {
return new Point();
return getFakeLocationOnScreen();
}
} else {
return new Point();
return getFakeLocationOnScreen();
}
}, Point::new);
}, this::getFakeLocationOnScreen);
}
private Point getFakeLocationOnScreen() {
// If we can't learn the real location from WLRobotPeer, we can at least
// return a reasonable fake. This fake location places all windows in the top-left
// corner of their respective screen.
GraphicsConfiguration graphicsConfig = target.getGraphicsConfiguration();
if (graphicsConfig != null) {
return graphicsConfig.getBounds().getLocation();
}
return new Point();
}
/**
@@ -858,17 +838,10 @@ public class WLComponentPeer implements ComponentPeer {
}
public void updateCursorImmediately() {
updateCursorImmediately(WLToolkit.getInputState());
WLToolkit.updateCursorImmediatelyFor(this);
}
private void updateCursorImmediately(WLInputState inputState) {
WLComponentPeer peer = inputState.peerForPointerEvents();
if (peer == null) return;
Cursor cursor = peer.getCursor(inputState.getPointerX(), inputState.getPointerY());
setCursor(cursor, getGraphicsDevice() != null ? getGraphicsDevice().getDisplayScale() : 1);
}
Cursor getCursor(int x, int y) {
Cursor cursorAt(int x, int y) {
Component target = this.target;
if (target instanceof Container) {
Component c = AWTAccessor.getContainerAccessor().findComponentAt((Container) target, x, y, false);
@@ -879,56 +852,6 @@ public class WLComponentPeer implements ComponentPeer {
return AWTAccessor.getComponentAccessor().getCursor(target);
}
private static void setCursor(Cursor c, int scale) {
long serial = WLToolkit.getInputState().pointerEnterSerial();
if (serial == 0) {
if (log.isLoggable(Level.WARNING)) {
log.warning("setCursor aborted due to missing event serial");
}
return; // Wayland will ignore the request anyway
}
Cursor cursor;
if (c.getType() == Cursor.CUSTOM_CURSOR && !(c instanceof WLCustomCursor)) {
cursor = Cursor.getDefaultCursor();
} else {
cursor = c;
}
performLockedGlobal(() -> {
long pData = AWTAccessor.getCursorAccessor().getPData(cursor, scale);
if (pData == 0) {
// instead of destroying and creating new cursor after changing scale could be used caching
long oldPData = AWTAccessor.getCursorAccessor().getPData(cursor);
if (oldPData != 0 && oldPData != -1) {
nativeDestroyPredefinedCursor(oldPData);
}
pData = createNativeCursor(cursor.getType(), scale);
if (pData == 0) {
pData = createNativeCursor(Cursor.DEFAULT_CURSOR, scale);
}
if (pData == 0) {
pData = -1; // mark as unavailable
}
AWTAccessor.getCursorAccessor().setPData(cursor, scale, pData);
}
nativeSetCursor(pData, scale, serial);
});
}
private static long createNativeCursor(int type, int scale) {
if (type < Cursor.DEFAULT_CURSOR || type > Cursor.MOVE_CURSOR) {
type = Cursor.DEFAULT_CURSOR;
}
for (String name : CURSOR_NAMES[type]) {
long pData = nativeGetPredefinedCursor(name, scale);
if (pData != 0) {
return pData;
}
}
return 0;
}
@Override
public Image createImage(int width, int height) {
WLGraphicsConfig graphicsConfig = (WLGraphicsConfig) target.getGraphicsConfiguration();
@@ -1094,9 +1017,6 @@ public class WLComponentPeer implements ComponentPeer {
private native void nativeSetWindowGeometry(long ptr, int x, int y, int width, int height);
private native void nativeSetMinimumSize(long ptr, int width, int height);
private native void nativeSetMaximumSize(long ptr, int width, int height);
private static native void nativeSetCursor(long pData, int scale, long pointerEnterSerial);
private static native long nativeGetPredefinedCursor(String name, int scale);
private static native long nativeDestroyPredefinedCursor(long pData);
private native void nativeShowWindowMenu(long serial, long ptr, int x, int y);
private native void nativeActivate(long serial, long ptr, long activatingSurfacePtr);
@@ -1139,7 +1059,7 @@ public class WLComponentPeer implements ComponentPeer {
final long timestamp = newInputState.getTimestamp();
if (e.hasEnterEvent()) {
performUnlocked(() -> updateCursorImmediately(newInputState));
updateCursorImmediately();
final MouseEvent mouseEvent = new MouseEvent(getTarget(), MouseEvent.MOUSE_ENTERED,
timestamp,
newInputState.getModifiers(),
@@ -1548,23 +1468,34 @@ public class WLComponentPeer implements ComponentPeer {
}
}
Point javaUnitsToSurfaceUnits(Point p) {
return new Point(javaUnitsToSurfaceUnits(p.x), javaUnitsToSurfaceUnits(p.y));
}
Dimension javaUnitsToSurfaceUnits(Dimension d) {
return new Dimension(javaUnitsToSurfaceUnits(d.width), javaUnitsToSurfaceUnits(d.height));
}
void notifyConfigured(int newSurfaceX, int newSurfaceY, int newSurfaceWidth, int newSurfaceHeight, boolean active, boolean maximized) {
// NB: The width and height, as well as X and Y arguments specify the size and the location
// NB: The width and height, as well as X and Y arguments, specify the size and the location
// of the window in surface-local coordinates.
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine(String.format("%s configured to %dx%d surface units", this, newSurfaceWidth, newSurfaceHeight));
}
boolean isWlPopup = targetIsWlPopup();
if (isWlPopup) { // Only popups provide (relative) location
int newX = surfaceUnitsToJavaUnits(newSurfaceX);
int newY = surfaceUnitsToJavaUnits(newSurfaceY);
setLocationTo(newX, newY);
// The popup itself stores its location relative to its parent, but what we've got is
// the location relative to the toplevel. Let's convert:
Window popup = (Window) target;
Component popupParent = AWTAccessor.getWindowAccessor().getPopupParent(popup);
Window toplevel = getToplevelFor(popupParent);
Point parentLocation = getRelativeLocation(popupParent, toplevel);
Point locationRelativeToParent = new Point(newX - parentLocation.x, newY - parentLocation.y);
resetTargetLocationTo(locationRelativeToParent.x, locationRelativeToParent.y);
}
// From xdg-shell.xml: "If the width or height arguments are zero,
@@ -1721,15 +1652,6 @@ public class WLComponentPeer implements ComponentPeer {
// done using these methods. Then one can be sure that native data is not changed concurrently in any way while the
// specified task is executed.
static void performLockedGlobal(Runnable task) {
WLToolkit.awtLock();
try {
task.run();
} finally {
WLToolkit.awtUnlock();
}
}
void performLocked(Runnable task) {
WLToolkit.awtLock();
try {

View File

@@ -144,12 +144,12 @@ public abstract class WLDecoratedPeer extends WLWindowPeer {
}
@Override
Cursor getCursor(int x, int y) {
Cursor cursorAt(int x, int y) {
Cursor cursor = decoration.getCursor(x, y);
if (cursor != null) {
return cursor;
} else {
return super.getCursor(x, y);
return super.cursorAt(x, y);
}
}
}

View File

@@ -39,17 +39,27 @@ public abstract class WLGraphicsConfig extends GraphicsConfiguration {
private final WLGraphicsDevice device;
private final int x;
private final int y;
private final int xLogical; // logical (scaled) horizontal location; optional, could be zero
private final int yLogical; // logical (scaled) vertical location; optional, could be zero
private final int width;
private final int height;
private final int widthLogical; // logical (scaled) width; optional, could be zero
private final int heightLogical;// logical (scaled) height; optional, could be zero
private final int displayScale; // as reported by Wayland
private final double effectiveScale; // as enforced by Java
protected WLGraphicsConfig(WLGraphicsDevice device, int x, int y, int width, int height, int displayScale) {
protected WLGraphicsConfig(WLGraphicsDevice device, int x, int y, int xLogical, int yLogical,
int width, int height, int widthLogical, int heightLogical,
int displayScale) {
this.device = device;
this.x = x;
this.y = y;
this.xLogical = xLogical;
this.yLogical = yLogical;
this.width = width;
this.height = height;
this.widthLogical = widthLogical;
this.heightLogical = heightLogical;
this.displayScale = displayScale;
this.effectiveScale = WLGraphicsEnvironment.effectiveScaleFrom(displayScale);
}
@@ -86,10 +96,13 @@ public abstract class WLGraphicsConfig extends GraphicsConfiguration {
@Override
public Rectangle getBounds() {
// NB: despite the claims of GraphicsConfiguration.getBounds()'s javadoc,
// the value returned is expected to be in user-space coordinates,
// same as windows sizes, offsets, components' coordinates, etc.
return new Rectangle(x, y, (int) (width / effectiveScale), (int) (height / effectiveScale));
return (widthLogical > 0 && heightLogical > 0)
? new Rectangle(xLogical, yLogical, widthLogical, heightLogical)
: new Rectangle(x, y, width, height);
}
public Rectangle getRealBounds() {
return new Rectangle(x, y, width, height);
}
/**

View File

@@ -65,6 +65,9 @@ public class WLGraphicsDevice extends GraphicsDevice {
*/
private volatile int y; // only changes when the device gets invalidated
private volatile int xLogical; // logical (scaled) horizontal location; optional, could be zero
private volatile int yLogical; // logical (scaled) vertical location; optional, could be zero
private final int widthMm;
private final int heightMm;
@@ -78,10 +81,12 @@ public class WLGraphicsDevice extends GraphicsDevice {
// and get their graphics configuration from it
private final Set<WLComponentPeer> toplevels = new HashSet<>(); // guarded by 'this'
private WLGraphicsDevice(int id, int x, int y, int widthMm, int heightMm) {
private WLGraphicsDevice(int id, int x, int y, int xLogical, int yLogical, int widthMm, int heightMm) {
this.wlID = id;
this.x = x;
this.y = y;
this.xLogical = xLogical;
this.yLogical = yLogical;
this.widthMm = widthMm;
this.heightMm = heightMm;
}
@@ -90,7 +95,7 @@ public class WLGraphicsDevice extends GraphicsDevice {
return wlID;
}
void updateConfiguration(String name, int width, int height, int scale) {
void updateConfiguration(String name, int width, int height, int widthLogical, int heightLogical, int scale) {
this.name = name;
WLGraphicsConfig config = defaultConfig;
@@ -101,16 +106,16 @@ public class WLGraphicsDevice extends GraphicsDevice {
// It is necessary to create a new object whenever config changes as its
// identity is used to detect changes in scale, among other things.
if (VKInstance.isVulkanEnabled()) {
newDefaultConfig = WLVKGraphicsConfig.getConfig(this, x, y, width, height, scale);
newDefaultConfig = WLVKGraphicsConfig.getConfig(this, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale);
newConfigs = new GraphicsConfiguration[1];
newConfigs[0] = newDefaultConfig;
} else {
// TODO: Actually, Wayland may support a lot more shared memory buffer configurations, need to
// subscribe to the wl_shm:format event and get the list from there.
newDefaultConfig = WLSMGraphicsConfig.getConfig(this, x, y, width, height, scale, false);
newDefaultConfig = WLSMGraphicsConfig.getConfig(this, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale, false);
newConfigs = new GraphicsConfiguration[2];
newConfigs[0] = newDefaultConfig;
newConfigs[1] = WLSMGraphicsConfig.getConfig(this, x, y, width, height, scale, true);
newConfigs[1] = WLSMGraphicsConfig.getConfig(this, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale, true);
}
configs = newConfigs;
@@ -145,19 +150,22 @@ public class WLGraphicsDevice extends GraphicsDevice {
this.wlID = similarDevice.wlID;
this.x = similarDevice.x;
this.y = similarDevice.y;
this.xLogical = similarDevice.xLogical;
this.yLogical = similarDevice.yLogical;
int newScale = similarDevice.getDisplayScale();
Rectangle newBounds = similarDevice.defaultConfig.getBounds();
updateConfiguration(similarDevice.name, newBounds.width, newBounds.height, newScale);
Rectangle newRealBounds = similarDevice.defaultConfig.getRealBounds();
updateConfiguration(similarDevice.name, newRealBounds.width, newRealBounds.height, newBounds.width, newBounds.height, newScale);
}
public static WLGraphicsDevice createWithConfiguration(int id, String name,
int x, int y,
int width, int height,
int x, int y, int xLogical, int yLogical,
int width, int height, int widthLogical, int heightLogical,
int widthMm, int heightMm,
int scale) {
WLGraphicsDevice device = new WLGraphicsDevice(id, x, y, widthMm, heightMm);
device.updateConfiguration(name, width, height, scale);
WLGraphicsDevice device = new WLGraphicsDevice(id, x, y, xLogical, yLogical, widthMm, heightMm);
device.updateConfiguration(name, width, height, widthLogical, heightLogical, scale);
return device;
}
@@ -165,8 +173,8 @@ public class WLGraphicsDevice extends GraphicsDevice {
* Compares the identity of this device with the given attributes
* and returns true iff the attributes identify the same device.
*/
boolean isSameDeviceAs(int wlID, int x, int y) {
return this.wlID == wlID && this.x == x && this.y == y;
boolean isSameDeviceAs(int wlID, int x, int y, int xLogical, int yLogical) {
return this.wlID == wlID && this.x == x && this.y == y && this.xLogical == xLogical && this.yLogical == yLogical;
}
boolean hasSameNameAs(WLGraphicsDevice otherDevice) {
@@ -286,8 +294,8 @@ public class WLGraphicsDevice extends GraphicsDevice {
@Override
public String toString() {
var config = defaultConfig;
return String.format("WLGraphicsDevice: '%s' id=%d at (%d, %d) with %s",
name, wlID, x, y,
return String.format("WLGraphicsDevice: '%s' id=%d at (%d, %d) ((%d, %d) logical) with %s",
name, wlID, x, y, xLogical, yLogical,
config != null ? config : "<no configs>");
}

View File

@@ -41,6 +41,15 @@ import sun.util.logging.PlatformLogger;
import sun.util.logging.PlatformLogger.Level;
public class WLGraphicsEnvironment extends SunGraphicsEnvironment {
public static final int WL_OUTPUT_TRANSFORM_NORMAL = 0;
public static final int WL_OUTPUT_TRANSFORM_90 = 1;
public static final int WL_OUTPUT_TRANSFORM_180 = 2;
public static final int WL_OUTPUT_TRANSFORM_270 = 3;
public static final int WL_OUTPUT_TRANSFORM_FLIPPED = 4;
public static final int WL_OUTPUT_TRANSFORM_FLIPPED_90 = 5;
public static final int WL_OUTPUT_TRANSFORM_FLIPPED_180 = 6;
public static final int WL_OUTPUT_TRANSFORM_FLIPPED_270 = 7;
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.wl.WLGraphicsEnvironment");
private static final boolean debugScaleEnabled;
@@ -90,12 +99,16 @@ public class WLGraphicsEnvironment extends SunGraphicsEnvironment {
private final List<WLGraphicsDevice> devices = new ArrayList<>(5);
private void notifyOutputConfigured(String name, String make, String model, int wlID,
int x, int y, int width, int height, int widthMm, int heightMm,
int x, int y, int xLogical, int yLogical,
int width, int height,
int widthLogical, int heightLogical,
int widthMm, int heightMm,
int subpixel, int transform, int scale) {
// Called from native code whenever a new output appears or an existing one changes its properties
// NB: initially called during WLToolkit.initIDs() on the main thread; later on EDT.
if (log.isLoggable(Level.FINE)) {
log.fine(String.format("Output configured id=%d at (%d, %d) %dx%d %dx scale", wlID, x, y, width, height, scale));
log.fine(String.format("Output configured id=%d at (%d, %d) (%d, %d logical) %dx%d (%dx%d logical) %dx scale",
wlID, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale));
}
String humanID =
@@ -108,12 +121,13 @@ public class WLGraphicsEnvironment extends SunGraphicsEnvironment {
final WLGraphicsDevice gd = devices.get(i);
if (gd.getID() == wlID) {
newOutput = false;
if (gd.isSameDeviceAs(wlID, x, y)) {
if (gd.isSameDeviceAs(wlID, x, y, xLogical, yLogical)) {
// These coordinates and the size are not scaled.
gd.updateConfiguration(humanID, width, height, scale);
gd.updateConfiguration(humanID, width, height, widthLogical, heightLogical, scale);
} else {
final WLGraphicsDevice updatedDevice = WLGraphicsDevice.createWithConfiguration(wlID, humanID,
x, y, width, height, widthMm, heightMm, scale);
x, y, xLogical, yLogical, width, height, widthLogical, heightLogical,
widthMm, heightMm, scale);
devices.set(i, updatedDevice);
gd.invalidate(updatedDevice);
}
@@ -121,8 +135,9 @@ public class WLGraphicsEnvironment extends SunGraphicsEnvironment {
}
}
if (newOutput) {
final WLGraphicsDevice gd = WLGraphicsDevice.createWithConfiguration(wlID, humanID, x, y,
width, height, widthMm, heightMm, scale);
final WLGraphicsDevice gd = WLGraphicsDevice.createWithConfiguration(wlID, humanID,
x, y, xLogical, yLogical, width, height, widthLogical, heightLogical,
widthMm, heightMm, scale);
devices.add(gd);
}
if (LogDisplay.ENABLED) {

View File

@@ -55,11 +55,15 @@ public class WLSMGraphicsConfig extends WLGraphicsConfig {
private WLSMGraphicsConfig(WLGraphicsDevice device,
int x,
int y,
int xLogical,
int yLogical,
int width,
int height,
int widthLogical,
int heightLogical,
int scale,
boolean translucencyCapable) {
super(device, x, y, width, height, scale);
super(device, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale);
this.translucencyCapable = translucencyCapable;
this.colorModel = colorModelFor(translucencyCapable ? Transparency.TRANSLUCENT : Transparency.OPAQUE);
// Note: GNOME Shell definitely expects alpha values to be pre-multiplied
@@ -69,11 +73,15 @@ public class WLSMGraphicsConfig extends WLGraphicsConfig {
public static WLSMGraphicsConfig getConfig(WLGraphicsDevice device,
int x,
int y,
int xLogical,
int yLogical,
int width,
int height,
int widthLogical,
int heightLogical,
int scale,
boolean translucencyCapable) {
var newConfig = new WLSMGraphicsConfig(device, x, y, width, height, scale, translucencyCapable);
var newConfig = new WLSMGraphicsConfig(device, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale, translucencyCapable);
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("New shared memory config " + newConfig);
}

View File

@@ -133,11 +133,33 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
private static final int MOUSE_BUTTONS_COUNT = 7;
private static final int AWT_MULTICLICK_DEFAULT_TIME_MS = 500;
// mapping of AWT cursor types to X cursor names
// multiple variants can be specified, that will be tried in order
// See https://freedesktop.org/wiki/Specifications/cursor-spec/
private static final String[][] CURSOR_NAMES = {
{"default", "arrow", "left_ptr", "left_arrow"}, // DEFAULT_CURSOR
{"crosshair", "cross"}, // CROSSHAIR_CURSOR
{"text", "xterm"}, // TEXT_CURSOR
{"wait", "watch", "progress"}, // WAIT_CURSOR
{"sw-resize", "bottom_left_corner"}, // SW_RESIZE_CURSOR
{"se-resize", "bottom_right_corner"}, // SE_RESIZE_CURSOR
{"nw-resize", "top_left_corner"}, // NW_RESIZE_CURSOR
{"ne-resize", "top_right_corner"}, // NE_RESIZE_CURSOR
{"n-resize", "top_side"}, // N_RESIZE_CURSOR
{"s-resize", "bottom_side"}, // S_RESIZE_CURSOR
{"w-resize", "left_side"}, // W_RESIZE_CURSOR
{"e-resize", "right_side"}, // E_RESIZE_CURSOR
{"pointer", "pointing_hand", "hand1", "hand2"}, // HAND_CURSOR
{"move"}, // MOVE_CURSOR
};
private static boolean initialized = false;
private static Thread toolkitThread;
private final WLClipboard clipboard;
private final WLClipboard selection;
private static Cursor currentCursor;
private static native void initIDs(long displayPtr);
static {
@@ -300,6 +322,10 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
final WLInputState oldInputState = inputState;
final WLInputState newInputState = oldInputState.updatedFromPointerEvent(e);
inputState = newInputState;
if (e.hasLeaveEvent() || e.hasEnterEvent()) {
// We've lost the control over the cursor, assume no knowledge about it
currentCursor = null;
}
final WLComponentPeer peer = newInputState.peerForPointerEvents();
if (peer == null) {
// We don't know whom to notify of the event; may happen when
@@ -1064,4 +1090,80 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
static boolean isInitialized() {
return initialized;
}
static void performLockedGlobal(Runnable task) {
WLToolkit.awtLock();
try {
task.run();
} finally {
WLToolkit.awtUnlock();
}
}
static void updateCursorImmediatelyFor(WLComponentPeer peer) {
Cursor cursor = peer.cursorAt(inputState.getPointerX(), inputState.getPointerY());
GraphicsConfiguration gc = peer.getGraphicsConfiguration();
int scale = gc instanceof WLGraphicsConfig wlGC ? wlGC.getDisplayScale() : 1;
setCursorTo(cursor, scale);
}
static void setCursorTo(Cursor c, int scale) {
long serial = inputState.pointerEnterSerial();
if (serial == 0) {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("setCursor aborted due to missing event serial");
}
return; // Wayland will ignore the request anyway
}
Cursor cursor;
if (c.getType() == Cursor.CUSTOM_CURSOR && !(c instanceof WLCustomCursor)) {
cursor = Cursor.getDefaultCursor();
} else {
cursor = c;
}
performLockedGlobal(() -> {
// Cursors may get updated very often; prevent vacuous updates from reaching the native code so
// as not to overwhelm the Wayland server.
if (cursor == currentCursor) return;
currentCursor = cursor;
long pData = AWTAccessor.getCursorAccessor().getPData(cursor, scale);
if (pData == 0) {
// instead of destroying and creating new cursor after changing scale could be used caching
long oldPData = AWTAccessor.getCursorAccessor().getPData(cursor);
if (oldPData != 0 && oldPData != -1) {
nativeDestroyPredefinedCursor(oldPData);
}
pData = createNativeCursor(cursor.getType(), scale);
if (pData == 0) {
pData = createNativeCursor(Cursor.DEFAULT_CURSOR, scale);
}
if (pData == 0) {
pData = -1; // mark as unavailable
}
AWTAccessor.getCursorAccessor().setPData(cursor, scale, pData);
}
nativeSetCursor(pData, scale, serial);
});
}
private static long createNativeCursor(int type, int scale) {
if (type < Cursor.DEFAULT_CURSOR || type > Cursor.MOVE_CURSOR) {
type = Cursor.DEFAULT_CURSOR;
}
for (String name : CURSOR_NAMES[type]) {
long pData = nativeGetPredefinedCursor(name, scale);
if (pData != 0) {
return pData;
}
}
return 0;
}
private static native void nativeSetCursor(long pData, int scale, long pointerEnterSerial);
private static native long nativeGetPredefinedCursor(String name, int scale);
private static native long nativeDestroyPredefinedCursor(long pData);
}

View File

@@ -78,8 +78,11 @@ public final class WLVKGraphicsConfig extends WLGraphicsConfig
private static native long getVKConfigInfo();
public WLVKGraphicsConfig(WLGraphicsDevice device, int x, int y, int width, int height, int scale, ContextCapabilities vkCaps) {
super(device, x, y, width, height, scale);
public WLVKGraphicsConfig(WLGraphicsDevice device,
int x, int y, int xLogical, int yLogical,
int width, int height, int widthLogical, int heightLogical,
int scale, ContextCapabilities vkCaps) {
super(device, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale);
this.vkCaps = vkCaps;
context = new VKContext(VKRenderQueue.getInstance());
}
@@ -89,13 +92,16 @@ public final class WLVKGraphicsConfig extends WLGraphicsConfig
return surfaceDataProxyCache;
}
public static WLVKGraphicsConfig getConfig(WLGraphicsDevice device, int x, int y, int width, int height, int scale)
public static WLVKGraphicsConfig getConfig(WLGraphicsDevice device,
int x, int y, int xLogical, int yLogical,
int width, int height, int widthLogical, int heightLogical,
int scale)
{
ContextCapabilities caps = new VKContext.VKContextCaps(
CAPS_PS30 | CAPS_PS20 | CAPS_RT_TEXTURE_ALPHA |
CAPS_RT_TEXTURE_OPAQUE | CAPS_MULTITEXTURE | CAPS_TEXNONPOW2 |
CAPS_TEXNONSQUARE, null);
return new WLVKGraphicsConfig(device, x, y, width, height, scale, caps);
return new WLVKGraphicsConfig(device, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale, caps);
}
/**

View File

@@ -513,7 +513,7 @@ newPositioner
xdg_positioner_set_size(xdg_positioner, width, height);
xdg_positioner_set_anchor_rect(xdg_positioner, offsetX, offsetY, 1, 1);
xdg_positioner_set_offset(xdg_positioner, 0, 0);
xdg_positioner_set_anchor(xdg_positioner, XDG_POSITIONER_ANCHOR_BOTTOM_LEFT);
xdg_positioner_set_anchor(xdg_positioner, XDG_POSITIONER_ANCHOR_TOP_LEFT);
xdg_positioner_set_gravity(xdg_positioner, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
xdg_positioner_set_constraint_adjustment(xdg_positioner,
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y

View File

@@ -58,7 +58,7 @@ Java_java_awt_Cursor_finalizeImpl
}
}
JNIEXPORT jlong JNICALL Java_sun_awt_wl_WLComponentPeer_nativeGetPredefinedCursor
JNIEXPORT jlong JNICALL Java_sun_awt_wl_WLToolkit_nativeGetPredefinedCursor
(JNIEnv *env, jclass cls, jstring name, jint scale)
{
struct wl_cursor_theme *cursor_theme = getCursorTheme(scale);
@@ -91,7 +91,7 @@ JNIEXPORT jlong JNICALL Java_sun_awt_wl_WLComponentPeer_nativeGetPredefinedCurso
return ptr_to_jlong(cursor);
}
JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeDestroyPredefinedCursor
JNIEXPORT void JNICALL Java_sun_awt_wl_WLToolkit_nativeDestroyPredefinedCursor
(JNIEnv *env, jclass cls, struct WLCursor *cursor)
{
free(cursor);
@@ -140,7 +140,7 @@ JNIEXPORT jlong JNICALL Java_sun_awt_wl_WLCustomCursor_nativeCreateCustomCursor
return ptr_to_jlong(cursor);
}
JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeSetCursor
JNIEXPORT void JNICALL Java_sun_awt_wl_WLToolkit_nativeSetCursor
(JNIEnv *env, jclass cls, jlong pData, jint scale, jlong pointerEnterSerial)
{
struct wl_buffer *buffer = NULL;

View File

@@ -25,8 +25,10 @@
*/
#ifndef HEADLESS
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <Trace.h>
#include "JNIUtilities.h"
@@ -36,12 +38,17 @@ typedef struct WLOutput {
struct WLOutput * next;
struct wl_output * wl_output;
struct zxdg_output_v1 * zxdg_output;
uint32_t id;
int32_t x;
int32_t y;
int32_t x_logical;
int32_t y_logical;
int32_t width;
int32_t height;
int32_t width_logical;
int32_t height_logical;
int32_t width_mm;
int32_t height_mm;
@@ -137,12 +144,8 @@ wl_output_scale(
}
static void
wl_output_done(
void *data,
struct wl_output *wl_output)
NotifyOutputConfigured(WLOutput* output)
{
WLOutput * output = data;
JNIEnv *env = getEnv();
jobject obj = (*env)->CallStaticObjectMethod(env, geClass, getSingleInstanceMID);
JNU_CHECK_EXCEPTION(env);
@@ -158,8 +161,12 @@ wl_output_done(
output->id,
output->x,
output->y,
output->x_logical,
output->y_logical,
output->width,
output->height,
output->width_logical,
output->height_logical,
output->width_mm,
output->height_mm,
(jint)output->subpixel,
@@ -168,6 +175,17 @@ wl_output_done(
JNU_CHECK_EXCEPTION(env);
}
static void
wl_output_done(void *data, struct wl_output *wl_output)
{
WLOutput * output = data;
// When the manager is present we'll wait for another 'done' event (see zxdg_output_done()).
bool wait_for_zxdg_output_done = zxdg_output_manager_v1 != NULL;
if (!wait_for_zxdg_output_done) {
NotifyOutputConfigured(output);
}
}
struct wl_output_listener wl_output_listener = {
.geometry = &wl_output_geometry,
.mode = &wl_output_mode,
@@ -181,6 +199,49 @@ struct wl_output_listener wl_output_listener = {
.scale = &wl_output_scale
};
static void
zxdg_output_logical_size(void *data, struct zxdg_output_v1 *zxdg_output_v1, int32_t width, int32_t height)
{
WLOutput * output = data;
output->width_logical = width;
output->height_logical = height;
}
static void
zxdg_output_done(void *data, struct zxdg_output_v1 *zxdg_output_v1)
{
WLOutput * output = data;
NotifyOutputConfigured(output);
}
static void
zxdg_output_logical_position(void *data, struct zxdg_output_v1 *zxdg_output_v1, int32_t x, int32_t y)
{
WLOutput * output = data;
output->x_logical = x;
output->y_logical = y;
}
static void
zxdg_output_description(void *data, struct zxdg_output_v1 *zxdg_output_v1, const char *description)
{
// Ignored
}
static void
zxdg_output_name(void *data, struct zxdg_output_v1 *zxdg_output_v1, const char *name)
{
// Ignored
}
struct zxdg_output_v1_listener zxdg_output_listener = {
.logical_position = zxdg_output_logical_position,
.logical_size = zxdg_output_logical_size,
.description = zxdg_output_description,
.name = zxdg_output_name,
.done = zxdg_output_done
};
jboolean
WLGraphicsEnvironment_initIDs
(JNIEnv *env, jclass clazz)
@@ -196,7 +257,7 @@ WLGraphicsEnvironment_initIDs
CHECK_NULL_RETURN(
notifyOutputConfiguredMID = (*env)->GetMethodID(env, clazz,
"notifyOutputConfigured",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIIIIIIII)V"),
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIIIIIIIIIIII)V"),
JNI_FALSE);
CHECK_NULL_RETURN(
notifyOutputDestroyedMID = (*env)->GetMethodID(env, clazz,
@@ -206,6 +267,17 @@ WLGraphicsEnvironment_initIDs
return JNI_TRUE;
}
static void RegisterXdgOutput(WLOutput* output)
{
assert(zxdg_output_manager_v1 != NULL);
if (output->zxdg_output == NULL) {
output->zxdg_output = zxdg_output_manager_v1_get_xdg_output(zxdg_output_manager_v1, output->wl_output);
CHECK_NULL(output->zxdg_output);
zxdg_output_v1_add_listener(output->zxdg_output, &zxdg_output_listener, output);
}
}
void
WLOutputRegister(struct wl_registry *wl_registry, uint32_t id)
{
@@ -217,10 +289,25 @@ WLOutputRegister(struct wl_registry *wl_registry, uint32_t id)
output->wl_output = wl_registry_bind(wl_registry, id, &wl_output_interface, 2);
if (output->wl_output == NULL) {
JNU_ThrowByName(env, "java/awt/AWTError", "wl_registry_bind() failed");
return;
}
wl_output_add_listener(output->wl_output, &wl_output_listener, output);
output->next = outputList;
outputList = output;
if (zxdg_output_manager_v1 != NULL) {
RegisterXdgOutput(output);
}
}
void
WLOutputXdgOutputManagerBecameAvailable(void)
{
assert(zxdg_output_manager_v1 != NULL);
for (WLOutput* output = outputList; output; output = output->next) {
RegisterXdgOutput(output);
}
}
void
@@ -236,6 +323,9 @@ WLOutputDeregister(struct wl_registry *wl_registry, uint32_t id)
} else {
outputList = cur->next;
}
if (cur->zxdg_output != NULL) {
zxdg_output_v1_destroy(cur->zxdg_output);
}
wl_output_destroy(cur->wl_output);
WLOutput * next = cur->next;
free(cur->name);

View File

@@ -30,3 +30,4 @@ void WLOutputRegister(struct wl_registry *wl_registry, uint32_t id);
void WLOutputDeregister(struct wl_registry *wl_registry, uint32_t id);
uint32_t WLOutputID(struct wl_output *wlOutput);
struct wl_output* WLOutputByID(uint32_t id);
void WLOutputXdgOutputManagerBecameAvailable(void);

View File

@@ -80,6 +80,7 @@ struct wl_cursor_theme *cursor_themes[MAX_CURSOR_SCALE] = {NULL};
struct wl_data_device_manager *wl_ddm = NULL;
struct zwp_primary_selection_device_manager_v1 *zwp_selection_dm = NULL; // optional, check for NULL before use
struct zxdg_output_manager_v1 *zxdg_output_manager_v1 = NULL; // optional, check for NULL before use
static uint32_t num_of_outstanding_sync = 0;
@@ -557,6 +558,12 @@ registry_global(void *data, struct wl_registry *wl_registry,
zwp_selection_dm = wl_registry_bind(wl_registry, name, &zwp_primary_selection_device_manager_v1_interface, 1);
} else if (strcmp(interface, wp_viewporter_interface.name) == 0) {
wp_viewporter = wl_registry_bind(wl_registry, name, &wp_viewporter_interface, 1);
} else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) {
zxdg_output_manager_v1 = wl_registry_bind(wl_registry, name, &zxdg_output_manager_v1_interface, 2);
if (zxdg_output_manager_v1 != NULL) {
WLOutputXdgOutputManagerBecameAvailable();
process_new_listener_before_end_of_init();
}
}
#ifdef WAKEFIELD_ROBOT

View File

@@ -27,6 +27,7 @@
#include <wayland-cursor.h>
#include "xdg-shell-client-protocol.h"
#include "xdg-activation-v1-client-protocol.h"
#include "xdg-output-unstable-v1-client-protocol.h"
#include "primary-selection-client-protocol.h"
#include "viewporter-client-protocol.h"
#include "jvm_md.h"
@@ -62,6 +63,7 @@ extern struct gtk_shell1* gtk_shell1; // optional, check for NULL before use
extern struct wl_cursor_theme *wl_cursor_theme;
extern struct wl_data_device_manager *wl_ddm;
extern struct zwp_primary_selection_device_manager_v1 *zwp_selection_dm; // optional, check for NULL before use
extern struct zxdg_output_manager_v1 *zxdg_output_manager_v1; // optional, check for NULL before use
JNIEnv *getEnv();

View File

@@ -0,0 +1,418 @@
/* Generated by wayland-scanner 1.19.0 */
#ifndef XDG_OUTPUT_UNSTABLE_V1_CLIENT_PROTOCOL_H
#define XDG_OUTPUT_UNSTABLE_V1_CLIENT_PROTOCOL_H
#include <stdint.h>
#include <stddef.h>
#include "wayland-client.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @page page_xdg_output_unstable_v1 The xdg_output_unstable_v1 protocol
* Protocol to describe output regions
*
* @section page_desc_xdg_output_unstable_v1 Description
*
* This protocol aims at describing outputs in a way which is more in line
* with the concept of an output on desktop oriented systems.
*
* Some information are more specific to the concept of an output for
* a desktop oriented system and may not make sense in other applications,
* such as IVI systems for example.
*
* Typically, the global compositor space on a desktop system is made of
* a contiguous or overlapping set of rectangular regions.
*
* The logical_position and logical_size events defined in this protocol
* might provide information identical to their counterparts already
* available from wl_output, in which case the information provided by this
* protocol should be preferred to their equivalent in wl_output. The goal is
* to move the desktop specific concepts (such as output location within the
* global compositor space, etc.) out of the core wl_output protocol.
*
* Warning! The protocol described in this file is experimental and
* backward incompatible changes may be made. Backward compatible
* changes may be added together with the corresponding interface
* version bump.
* Backward incompatible changes are done by bumping the version
* number in the protocol and interface names and resetting the
* interface version. Once the protocol is to be declared stable,
* the 'z' prefix and the version number in the protocol and
* interface names are removed and the interface version number is
* reset.
*
* @section page_ifaces_xdg_output_unstable_v1 Interfaces
* - @subpage page_iface_zxdg_output_manager_v1 - manage xdg_output objects
* - @subpage page_iface_zxdg_output_v1 - compositor logical output region
* @section page_copyright_xdg_output_unstable_v1 Copyright
* <pre>
*
* Copyright © 2017 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
* </pre>
*/
struct wl_output;
struct zxdg_output_manager_v1;
struct zxdg_output_v1;
#ifndef ZXDG_OUTPUT_MANAGER_V1_INTERFACE
#define ZXDG_OUTPUT_MANAGER_V1_INTERFACE
/**
* @page page_iface_zxdg_output_manager_v1 zxdg_output_manager_v1
* @section page_iface_zxdg_output_manager_v1_desc Description
*
* A global factory interface for xdg_output objects.
* @section page_iface_zxdg_output_manager_v1_api API
* See @ref iface_zxdg_output_manager_v1.
*/
/**
* @defgroup iface_zxdg_output_manager_v1 The zxdg_output_manager_v1 interface
*
* A global factory interface for xdg_output objects.
*/
extern const struct wl_interface zxdg_output_manager_v1_interface;
#endif
#ifndef ZXDG_OUTPUT_V1_INTERFACE
#define ZXDG_OUTPUT_V1_INTERFACE
/**
* @page page_iface_zxdg_output_v1 zxdg_output_v1
* @section page_iface_zxdg_output_v1_desc Description
*
* An xdg_output describes part of the compositor geometry.
*
* This typically corresponds to a monitor that displays part of the
* compositor space.
*
* For objects version 3 onwards, after all xdg_output properties have been
* sent (when the object is created and when properties are updated), a
* wl_output.done event is sent. This allows changes to the output
* properties to be seen as atomic, even if they happen via multiple events.
* @section page_iface_zxdg_output_v1_api API
* See @ref iface_zxdg_output_v1.
*/
/**
* @defgroup iface_zxdg_output_v1 The zxdg_output_v1 interface
*
* An xdg_output describes part of the compositor geometry.
*
* This typically corresponds to a monitor that displays part of the
* compositor space.
*
* For objects version 3 onwards, after all xdg_output properties have been
* sent (when the object is created and when properties are updated), a
* wl_output.done event is sent. This allows changes to the output
* properties to be seen as atomic, even if they happen via multiple events.
*/
extern const struct wl_interface zxdg_output_v1_interface;
#endif
#define ZXDG_OUTPUT_MANAGER_V1_DESTROY 0
#define ZXDG_OUTPUT_MANAGER_V1_GET_XDG_OUTPUT 1
/**
* @ingroup iface_zxdg_output_manager_v1
*/
#define ZXDG_OUTPUT_MANAGER_V1_DESTROY_SINCE_VERSION 1
/**
* @ingroup iface_zxdg_output_manager_v1
*/
#define ZXDG_OUTPUT_MANAGER_V1_GET_XDG_OUTPUT_SINCE_VERSION 1
/** @ingroup iface_zxdg_output_manager_v1 */
static inline void
zxdg_output_manager_v1_set_user_data(struct zxdg_output_manager_v1 *zxdg_output_manager_v1, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) zxdg_output_manager_v1, user_data);
}
/** @ingroup iface_zxdg_output_manager_v1 */
static inline void *
zxdg_output_manager_v1_get_user_data(struct zxdg_output_manager_v1 *zxdg_output_manager_v1)
{
return wl_proxy_get_user_data((struct wl_proxy *) zxdg_output_manager_v1);
}
static inline uint32_t
zxdg_output_manager_v1_get_version(struct zxdg_output_manager_v1 *zxdg_output_manager_v1)
{
return wl_proxy_get_version((struct wl_proxy *) zxdg_output_manager_v1);
}
/**
* @ingroup iface_zxdg_output_manager_v1
*
* Using this request a client can tell the server that it is not
* going to use the xdg_output_manager object anymore.
*
* Any objects already created through this instance are not affected.
*/
static inline void
zxdg_output_manager_v1_destroy(struct zxdg_output_manager_v1 *zxdg_output_manager_v1)
{
wl_proxy_marshal((struct wl_proxy *) zxdg_output_manager_v1,
ZXDG_OUTPUT_MANAGER_V1_DESTROY);
wl_proxy_destroy((struct wl_proxy *) zxdg_output_manager_v1);
}
/**
* @ingroup iface_zxdg_output_manager_v1
*
* This creates a new xdg_output object for the given wl_output.
*/
static inline struct zxdg_output_v1 *
zxdg_output_manager_v1_get_xdg_output(struct zxdg_output_manager_v1 *zxdg_output_manager_v1, struct wl_output *output)
{
struct wl_proxy *id;
id = wl_proxy_marshal_constructor((struct wl_proxy *) zxdg_output_manager_v1,
ZXDG_OUTPUT_MANAGER_V1_GET_XDG_OUTPUT, &zxdg_output_v1_interface, NULL, output);
return (struct zxdg_output_v1 *) id;
}
/**
* @ingroup iface_zxdg_output_v1
* @struct zxdg_output_v1_listener
*/
struct zxdg_output_v1_listener {
/**
* position of the output within the global compositor space
*
* The position event describes the location of the wl_output
* within the global compositor space.
*
* The logical_position event is sent after creating an xdg_output
* (see xdg_output_manager.get_xdg_output) and whenever the
* location of the output changes within the global compositor
* space.
* @param x x position within the global compositor space
* @param y y position within the global compositor space
*/
void (*logical_position)(void *data,
struct zxdg_output_v1 *zxdg_output_v1,
int32_t x,
int32_t y);
/**
* size of the output in the global compositor space
*
* The logical_size event describes the size of the output in the
* global compositor space.
*
* Most regular Wayland clients should not pay attention to the
* logical size and would rather rely on xdg_shell interfaces.
*
* Some clients such as Xwayland, however, need this to configure
* their surfaces in the global compositor space as the compositor
* may apply a different scale from what is advertised by the
* output scaling property (to achieve fractional scaling, for
* example).
*
* For example, for a wl_output mode 3840×2160 and a scale factor
* 2:
*
* - A compositor not scaling the monitor viewport in its
* compositing space will advertise a logical size of 3840×2160,
*
* - A compositor scaling the monitor viewport with scale factor 2
* will advertise a logical size of 1920×1080,
*
* - A compositor scaling the monitor viewport using a fractional
* scale of 1.5 will advertise a logical size of 2560×1440.
*
* For example, for a wl_output mode 1920×1080 and a 90 degree
* rotation, the compositor will advertise a logical size of
* 1080x1920.
*
* The logical_size event is sent after creating an xdg_output (see
* xdg_output_manager.get_xdg_output) and whenever the logical size
* of the output changes, either as a result of a change in the
* applied scale or because of a change in the corresponding output
* mode(see wl_output.mode) or transform (see wl_output.transform).
* @param width width in global compositor space
* @param height height in global compositor space
*/
void (*logical_size)(void *data,
struct zxdg_output_v1 *zxdg_output_v1,
int32_t width,
int32_t height);
/**
* all information about the output have been sent
*
* This event is sent after all other properties of an xdg_output
* have been sent.
*
* This allows changes to the xdg_output properties to be seen as
* atomic, even if they happen via multiple events.
*
* For objects version 3 onwards, this event is deprecated.
* Compositors are not required to send it anymore and must send
* wl_output.done instead.
*/
void (*done)(void *data,
struct zxdg_output_v1 *zxdg_output_v1);
/**
* name of this output
*
* Many compositors will assign names to their outputs, show them
* to the user, allow them to be configured by name, etc. The
* client may wish to know this name as well to offer the user
* similar behaviors.
*
* The naming convention is compositor defined, but limited to
* alphanumeric characters and dashes (-). Each name is unique
* among all wl_output globals, but if a wl_output global is
* destroyed the same name may be reused later. The names will also
* remain consistent across sessions with the same hardware and
* software configuration.
*
* Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc.
* However, do not assume that the name is a reflection of an
* underlying DRM connector, X11 connection, etc.
*
* The name event is sent after creating an xdg_output (see
* xdg_output_manager.get_xdg_output). This event is only sent once
* per xdg_output, and the name does not change over the lifetime
* of the wl_output global.
*
* This event is deprecated, instead clients should use
* wl_output.name. Compositors must still support this event.
* @param name output name
* @since 2
*/
void (*name)(void *data,
struct zxdg_output_v1 *zxdg_output_v1,
const char *name);
/**
* human-readable description of this output
*
* Many compositors can produce human-readable descriptions of
* their outputs. The client may wish to know this description as
* well, to communicate the user for various purposes.
*
* The description is a UTF-8 string with no convention defined for
* its contents. Examples might include 'Foocorp 11" Display' or
* 'Virtual X11 output via :1'.
*
* The description event is sent after creating an xdg_output (see
* xdg_output_manager.get_xdg_output) and whenever the description
* changes. The description is optional, and may not be sent at
* all.
*
* For objects of version 2 and lower, this event is only sent once
* per xdg_output, and the description does not change over the
* lifetime of the wl_output global.
*
* This event is deprecated, instead clients should use
* wl_output.description. Compositors must still support this
* event.
* @param description output description
* @since 2
*/
void (*description)(void *data,
struct zxdg_output_v1 *zxdg_output_v1,
const char *description);
};
/**
* @ingroup iface_zxdg_output_v1
*/
static inline int
zxdg_output_v1_add_listener(struct zxdg_output_v1 *zxdg_output_v1,
const struct zxdg_output_v1_listener *listener, void *data)
{
return wl_proxy_add_listener((struct wl_proxy *) zxdg_output_v1,
(void (**)(void)) listener, data);
}
#define ZXDG_OUTPUT_V1_DESTROY 0
/**
* @ingroup iface_zxdg_output_v1
*/
#define ZXDG_OUTPUT_V1_LOGICAL_POSITION_SINCE_VERSION 1
/**
* @ingroup iface_zxdg_output_v1
*/
#define ZXDG_OUTPUT_V1_LOGICAL_SIZE_SINCE_VERSION 1
/**
* @ingroup iface_zxdg_output_v1
*/
#define ZXDG_OUTPUT_V1_DONE_SINCE_VERSION 1
/**
* @ingroup iface_zxdg_output_v1
*/
#define ZXDG_OUTPUT_V1_NAME_SINCE_VERSION 2
/**
* @ingroup iface_zxdg_output_v1
*/
#define ZXDG_OUTPUT_V1_DESCRIPTION_SINCE_VERSION 2
/**
* @ingroup iface_zxdg_output_v1
*/
#define ZXDG_OUTPUT_V1_DESTROY_SINCE_VERSION 1
/** @ingroup iface_zxdg_output_v1 */
static inline void
zxdg_output_v1_set_user_data(struct zxdg_output_v1 *zxdg_output_v1, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) zxdg_output_v1, user_data);
}
/** @ingroup iface_zxdg_output_v1 */
static inline void *
zxdg_output_v1_get_user_data(struct zxdg_output_v1 *zxdg_output_v1)
{
return wl_proxy_get_user_data((struct wl_proxy *) zxdg_output_v1);
}
static inline uint32_t
zxdg_output_v1_get_version(struct zxdg_output_v1 *zxdg_output_v1)
{
return wl_proxy_get_version((struct wl_proxy *) zxdg_output_v1);
}
/**
* @ingroup iface_zxdg_output_v1
*
* Using this request a client can tell the server that it is not
* going to use the xdg_output object anymore.
*/
static inline void
zxdg_output_v1_destroy(struct zxdg_output_v1 *zxdg_output_v1)
{
wl_proxy_marshal((struct wl_proxy *) zxdg_output_v1,
ZXDG_OUTPUT_V1_DESTROY);
wl_proxy_destroy((struct wl_proxy *) zxdg_output_v1);
}
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,78 @@
/* Generated by wayland-scanner 1.19.0 */
/*
* Copyright © 2017 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdint.h>
#include "wayland-util.h"
#ifndef __has_attribute
# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
#endif
#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
#define WL_PRIVATE __attribute__ ((visibility("hidden")))
#else
#define WL_PRIVATE
#endif
extern const struct wl_interface wl_output_interface;
extern const struct wl_interface zxdg_output_v1_interface;
static const struct wl_interface *xdg_output_unstable_v1_types[] = {
NULL,
NULL,
&zxdg_output_v1_interface,
&wl_output_interface,
};
static const struct wl_message zxdg_output_manager_v1_requests[] = {
{ "destroy", "", xdg_output_unstable_v1_types + 0 },
{ "get_xdg_output", "no", xdg_output_unstable_v1_types + 2 },
};
WL_PRIVATE const struct wl_interface zxdg_output_manager_v1_interface = {
"zxdg_output_manager_v1", 3,
2, zxdg_output_manager_v1_requests,
0, NULL,
};
static const struct wl_message zxdg_output_v1_requests[] = {
{ "destroy", "", xdg_output_unstable_v1_types + 0 },
};
static const struct wl_message zxdg_output_v1_events[] = {
{ "logical_position", "ii", xdg_output_unstable_v1_types + 0 },
{ "logical_size", "ii", xdg_output_unstable_v1_types + 0 },
{ "done", "", xdg_output_unstable_v1_types + 0 },
{ "name", "2s", xdg_output_unstable_v1_types + 0 },
{ "description", "2s", xdg_output_unstable_v1_types + 0 },
};
WL_PRIVATE const struct wl_interface zxdg_output_v1_interface = {
"zxdg_output_v1", 3,
1, zxdg_output_v1_requests,
5, zxdg_output_v1_events,
};

View File

@@ -1082,6 +1082,9 @@ jdk_swing_wayland= \
jb/javax/swing/Popup/WLPopupAsParent.java \
jb/javax/swing/Popup/WLPopupMinSize.java \
jb/javax/swing/Popup/WLPopupResize.java \
jb/javax/swing/Popup/WLPopupLocation.java \
jb/javax/swing/Popup/WLPopupMoves.java \
jb/javax/swing/Popup/WLPopupVisibility.java \
-com/sun/java/swing/plaf/gtk/TestBackSpaceAction.java \
-com/sun/java/swing/plaf/gtk/TestFileChooserCtrlASelection.java \
-com/sun/java/swing/plaf/gtk/TestFileChooserSingleDirectorySelection.java \

View File

@@ -41,6 +41,10 @@ import static javax.swing.WindowConstants.EXIT_ON_CLOSE;
* @key headful
* @modules java.desktop/sun.awt
* @run main WLPopupAsParent
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=1.0 WLPopupAsParent
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=1.25 WLPopupAsParent
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=1.5 WLPopupAsParent
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=2.0 WLPopupAsParent
*/
public class WLPopupAsParent {
private static JFrame frame;

View File

@@ -0,0 +1,125 @@
/*
* Copyright 2024 JetBrains s.r.o.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import javax.swing.*;
import java.awt.*;
import static javax.swing.WindowConstants.EXIT_ON_CLOSE;
/**
* @test
* @summary Verifies that the popup-style window can change it's size and location
* @requires os.family == "linux"
* @key headful
* @modules java.desktop/sun.awt
* @run main/othervm WLPopupLocation
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=1.0 WLPopupLocation
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=1.25 WLPopupLocation
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=1.5 WLPopupLocation
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=2.0 WLPopupLocation
*/
public class WLPopupLocation {
private static JFrame frame;
private static JWindow popup;
private static void createAndShowUI() {
frame = new JFrame("WLPopupLocation Test");
frame.setSize(300, 200);
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static void initPopup() {
JPanel popupContents = new JPanel();
popupContents.add(new JLabel("test popup"));
popup = new JWindow(frame);
popup.setType(Window.Type.POPUP);
sun.awt.AWTAccessor.getWindowAccessor().setPopupParent(popup, frame);
popup.add(popupContents);
}
public static void main(String[] args) throws Exception {
Toolkit toolkit = Toolkit.getDefaultToolkit();
if (!toolkit.getClass().getName().equals("sun.awt.wl.WLToolkit")) {
System.out.println("The test makes sense only for WLToolkit. Exiting...");
return;
}
Robot robot = new Robot();
SwingUtilities.invokeAndWait(WLPopupLocation::createAndShowUI);
pause(robot);
SwingUtilities.invokeAndWait(WLPopupLocation::initPopup);
pause(robot);
int w1 = 150, h1 = 200;
int x1 = 100, y1 = 100;
System.out.printf("Action: locate to (%d, %d), set size (%d, %d)\n", x1, y1, w1, h1);
SwingUtilities.invokeAndWait(() -> {
popup.setVisible(true);
popup.setSize(w1, h1);
popup.setLocation(x1, y1);
});
if (popup.getSize().width != w1 || popup.getSize().height != h1) {
throw new RuntimeException(String.format("Incorrect size (%d, %d), expected (%d, %d)", popup.getSize().width, popup.getSize().height, w1, h1));
}
if (popup.getBounds().x != x1 || popup.getBounds().y != y1) {
throw new RuntimeException(String.format("Wrong location (via getBounds()): (%d, %d). Expected: (%d, %d)", popup.getBounds().x, popup.getBounds().y, x1, y1));
}
pause(robot);
if (popup.getSize().width != h1 || popup.getSize().height != h1) {
throw new RuntimeException(String.format("Incorrect size (%d, %d) after robot's wait for idle, expected (%d, %d)", popup.getSize().width, popup.getSize().height, w1, h1));
}
if (popup.getBounds().x != x1 || popup.getBounds().y != y1) {
throw new RuntimeException(String.format("Wrong location (via getBounds()) after robot's wait for idle: (%d, %d). Expected: (%d, %d)", popup.getBounds().x, popup.getBounds().y, x1, y1));
}
int x2 = 200, y2 = 200;
System.out.printf("Action: set popup size to (%d, %d)\n", x2, y2);
SwingUtilities.invokeAndWait(() -> {
popup.setLocation(x2, y2);
});
if (popup.getSize().width != w1 || popup.getSize().height != h1) {
throw new RuntimeException(String.format("Incorrect size (%d, %d), expected (%d, %d)", popup.getSize().width, popup.getSize().height, w1, h1));
}
if (popup.getBounds().x != x2 || popup.getBounds().y != y2) {
throw new RuntimeException(String.format("Wrong location (via getBounds()): (%d, %d). Expected: (%x, %d)", popup.getBounds().x, popup.getBounds().y, x2, y2));
}
pause(robot);
if (popup.getSize().width != w1 || popup.getSize().height != h1) {
throw new RuntimeException(String.format("Incorrect size (%d, %d) after robot's wait for idle, expected (%d, %d)", popup.getSize().width, popup.getSize().height, w1, h1));
}
if (popup.getBounds().x != x2 || popup.getBounds().y != y2) {
throw new RuntimeException(String.format("Wrong location (via getBounds()) after robot's wait for idle: (%d, %d). Expected: (%d, %d)", popup.getBounds().x, popup.getBounds().y, x2, y2));
}
SwingUtilities.invokeAndWait(frame::dispose);
}
private static void pause(Robot robot) {
robot.waitForIdle();
robot.delay(500);
}
}

View File

@@ -38,7 +38,11 @@ import java.awt.Window;
* @requires os.family == "linux"
* @key headful
* @modules java.desktop/sun.awt
* @run main WLPopupMinSize
* @run main/othervm WLPopupMinSize
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=1.0 WLPopupMinSize
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=1.25 WLPopupMinSize
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=1.5 WLPopupMinSize
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=2.0 WLPopupMinSize
*/
public class WLPopupMinSize {
private static JFrame frame;

View File

@@ -0,0 +1,171 @@
/*
* Copyright 2024 JetBrains s.r.o.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import javax.swing.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import static javax.swing.WindowConstants.EXIT_ON_CLOSE;
/**
* @test
* @summary Verifies that the popup-style window can move under Wayland
* @requires os.family == "linux"
* @key headful
* @modules java.desktop/sun.awt
* @run main/othervm WLPopupMoves
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=1.0 WLPopupMoves
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=1.25 WLPopupMoves
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=1.5 WLPopupMoves
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=2.0 WLPopupMoves
*/
public class WLPopupMoves {
private static JFrame frame;
private static JWindow popup;
private static void createAndShowUI() {
frame = new JFrame("WLPopupMoves Test");
frame.setSize(300, 200);
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static void initPopup() {
JPanel popupContents = new JPanel();
popupContents.add(new JLabel("test popup"));
popup = new JWindow(frame);
popup.setType(Window.Type.POPUP);
sun.awt.AWTAccessor.getWindowAccessor().setPopupParent(popup, frame);
popup.add(popupContents);
}
public static void main(String... args) throws Exception {
Toolkit toolkit = Toolkit.getDefaultToolkit();
if (!toolkit.getClass().getName().equals("sun.awt.wl.WLToolkit")) {
System.out.println("The test makes sense only for WLToolkit. Exiting...");
return;
}
Robot robot = new Robot();
SwingUtilities.invokeAndWait(WLPopupMoves::createAndShowUI);
pause(robot);
SwingUtilities.invokeAndWait(WLPopupMoves::initPopup);
pause(robot);
double uiScale = getUiScale();
System.out.printf("UI scale: %.2f.\n", uiScale);
int pixelThreshold = uiScale == 1.0 ? 0 : (int) Math.ceil(uiScale);
System.out.printf("Pixel threshold for verifications: %d\n", pixelThreshold);
int w = 120, h = 200;
System.out.println("Set popup to (50, 50)");
SwingUtilities.invokeAndWait(() -> {
popup.setBounds(50, 50, w, h);
popup.setVisible(true);
});
verifyBounds("Popup position after setting to (50, 50)\n", 50, 50, w, h, pixelThreshold);
pause(robot);
verifyBounds("Popup position (50, 50) after robot's pause\n", 50, 50, w, h, pixelThreshold);
System.out.println("Set popup to (100, 100)");
SwingUtilities.invokeAndWait(() -> {
popup.setBounds(100, 100, w, h);
});
verifyBounds("Popup position after setting to (100, 100)\n", 100, 100, w, h, pixelThreshold);
pause(robot);
verifyBounds("Popup position (100, 100) after robot's pause\n", 100, 100, w, h, pixelThreshold);
int x1 = (int) (toolkit.getScreenSize().width / (2 * uiScale));
int y1 = (int) (toolkit.getScreenSize().height / (2 * uiScale));
System.out.printf("Set popup to (%d, %d)\n", x1, y1);
SwingUtilities.invokeAndWait(() -> {
popup.setBounds(x1, y1, w, h);
});
verifyBounds(String.format("Popup position after setting to (%d, %d)\n", x1, y1), x1, y1, w, h, pixelThreshold);
pause(robot);
verifyBounds(String.format("Popup position (%d, %d) after robot's pause\n", x1, y1), x1, y1, w, h, pixelThreshold);
int x2 = (int) (toolkit.getScreenSize().width / uiScale - 10 - w);
int y2 = (int) (toolkit.getScreenSize().height / uiScale - 10 - h);
System.out.printf("Set popup to (%d, %d). (to the bottom right corner) \n", x2, y2);
SwingUtilities.invokeAndWait(() -> {
popup.setBounds(x2, y2, w, h);
});
verifyBounds(String.format("Popup position after setting to (%d, %d)\n", x2, y2), x2, y2, w, h, pixelThreshold);
pause(robot);
verifyBounds(String.format("Popup position (%d, %d) after robot's pause\n", x2, y2), x2, y2, w, h, pixelThreshold);
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice device = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = device.getDefaultConfiguration();
Insets insets = toolkit.getScreenInsets(gc);
int x3 = (int) (toolkit.getScreenSize().width / uiScale - 10 - insets.right);
int y3 = (int) (toolkit.getScreenSize().height / uiScale - 10 - insets.bottom);
System.out.printf("Set popup to (%d, %d). (to the bottom right corner) \n", x3, y3);
SwingUtilities.invokeAndWait(() -> {
popup.setBounds(x2, y2, w, h);
});
int x3Relocated = x3 - w;
int y3Relocated = y3 - h;
verifyBounds(String.format("Popup position after setting to (%d, %d)\n", x3, y3), x3Relocated, y3Relocated, w, h, pixelThreshold);
pause(robot);
verifyBounds(String.format("Popup position (%d, %d) after robot's pause\n", x3, y3), x3Relocated, y3Relocated, w, h, pixelThreshold);
SwingUtilities.invokeAndWait(frame::dispose);
}
private static Double getUiScale() {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice device = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = device.getDefaultConfiguration();
AffineTransform transform = gc.getDefaultTransform();
double scaleX = transform.getScaleX();
double scaleY = transform.getScaleY();
if (scaleX != scaleY) {
System.out.println("Skip test due to non-uniform display scale");
System.exit(0);
}
return scaleX;
}
private static void verifyBounds(String message, int x, int y, int w, int h, int pixelThreshold) {
Rectangle bounds = popup.getBounds();
System.out.printf("Check %s for bounds: %s\n", message, bounds);
boolean isCorrectPosition = x - pixelThreshold <= bounds.x && bounds.x <= x + pixelThreshold &&
y - pixelThreshold <= bounds.y && bounds.y <= y + pixelThreshold;
if (!isCorrectPosition) {
throw new RuntimeException(String.format("%s has wrong position. Expected: (%d, %d). Actual: (%d, %d)", message, x, y, bounds.x, bounds.y));
}
if (bounds.width != w || bounds.height != h) {
throw new RuntimeException(String.format("%s has wrong size. Expected: (%d, %d). Actual: (%d, %d)", message, w, h, bounds.width, bounds.height));
}
}
private static void pause(Robot robot) {
robot.waitForIdle();
robot.delay(500);
}
}

View File

@@ -1,3 +1,26 @@
/*
* Copyright 2024 JetBrains s.r.o.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
@@ -27,10 +50,8 @@ import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
import java.awt.Dimension;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.*;
import java.awt.geom.AffineTransform;
import static javax.swing.WindowConstants.EXIT_ON_CLOSE;
@@ -41,7 +62,11 @@ import static javax.swing.WindowConstants.EXIT_ON_CLOSE;
* @requires os.family == "linux"
* @key headful
* @modules java.desktop/sun.awt
* @run main WLPopupResize
* @run main/othervm WLPopupResize
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=1.0 WLPopupResize
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=1.25 WLPopupResize
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=1.5 WLPopupResize
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=2.0 WLPopupResize
*/
public class WLPopupResize {
private static JFrame frame;
@@ -80,15 +105,51 @@ public class WLPopupResize {
SwingUtilities.invokeAndWait(WLPopupResize::showPopup);
pause(robot);
SwingUtilities.invokeAndWait(() -> {
popup.setBounds(10, 20, 120, 80);
});
pause(robot);
double uiScale = getUiScale();
System.out.printf("UI scale: %.2f.\n", uiScale);
int pixelThreshold = uiScale == 1.0 ? 0 : (int) Math.ceil(uiScale);
System.out.printf("Pixel threshold for verifications: %d\n", pixelThreshold);
Dimension newSize = popup.getSize();
if (newSize.width != 120 || newSize.height != 80) {
throw new RuntimeException("Wrong popup size: " + newSize.width + ", " + newSize.height);
int x = 10, y = 20, w = 120, h = 80;
System.out.println("Set popup size to (120, 80)");
SwingUtilities.invokeAndWait(() -> {
popup.setBounds(x, y, w, h);
});
Rectangle bounds = popup.getBounds();
boolean isCorrectPosition = x - pixelThreshold <= bounds.x && bounds.x <= x + pixelThreshold &&
y - pixelThreshold <= bounds.y && bounds.y <= y + pixelThreshold;
if (!isCorrectPosition) {
throw new RuntimeException("Popup position has unexpectedly changed. Bounds: " + popup.getBounds());
}
if (popup.getBounds().width != w || popup.getBounds().height != h) {
throw new RuntimeException("Popup size wasn't correctly changed. Bounds: " + popup.getBounds());
}
pause(robot);
System.out.println("Next checks after robot's waiting for idle.");
isCorrectPosition = x - pixelThreshold <= bounds.x && bounds.x <= x + pixelThreshold &&
y - pixelThreshold <= bounds.y && bounds.y <= y + pixelThreshold;
if (!isCorrectPosition) {
throw new RuntimeException("Popup position has unexpectedly changed. Bounds: " + popup.getBounds());
}
if (popup.getBounds().width != w || popup.getBounds().height != h) {
throw new RuntimeException("Popup size wasn't correctly changed. Bounds: " + popup.getBounds());
}
SwingUtilities.invokeAndWait(frame::dispose);
}
private static Double getUiScale() {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice device = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = device.getDefaultConfiguration();
AffineTransform transform = gc.getDefaultTransform();
double scaleX = transform.getScaleX();
double scaleY = transform.getScaleY();
if (scaleX != scaleY) {
System.out.println("Skip test due to non-uniform display scale");
System.exit(0);
}
return scaleX;
}
private static void pause(Robot robot) {

View File

@@ -0,0 +1,133 @@
/*
* Copyright 2024 JetBrains s.r.o.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import javax.swing.*;
import java.awt.*;
import static javax.swing.WindowConstants.EXIT_ON_CLOSE;
/**
* @test
* @summary Verifies that the popup-style window can change its visibility
* @requires os.family == "linux"
* @key headful
* @modules java.desktop/sun.awt
* @run main/othervm WLPopupVisibility
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=1.0 WLPopupVisibility
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=2.0 WLPopupVisibility
*/
public class WLPopupVisibility {
private static JFrame frame;
private static JWindow popup;
private static void createAndShowUI() {
frame = new JFrame("WLPopupVisibility Test");
frame.setSize(300, 200);
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static void initPopup() {
JPanel popupContents = new JPanel();
popupContents.add(new JLabel("test popup"));
popup = new JWindow(frame);
popup.setType(Window.Type.POPUP);
sun.awt.AWTAccessor.getWindowAccessor().setPopupParent(popup, frame);
popup.add(popupContents);
}
public static void main(String[] args) throws Exception {
Toolkit toolkit = Toolkit.getDefaultToolkit();
if (!toolkit.getClass().getName().equals("sun.awt.wl.WLToolkit")) {
System.out.println("The test makes sense only for WLToolkit. Exiting...");
return;
}
Robot robot = new Robot();
SwingUtilities.invokeAndWait(WLPopupVisibility::createAndShowUI);
pause(robot);
SwingUtilities.invokeAndWait(WLPopupVisibility::initPopup);
pause(robot);
System.out.println("Action: set the popup visible");
SwingUtilities.invokeAndWait(() -> popup.setVisible(true));
boolean isVisible1 = popup.isVisible();
pause(robot);
boolean isVisible2 = popup.isVisible();
if (!isVisible1 || !isVisible2) {
throw new RuntimeException("Expected result: popup is visible");
}
System.out.println("Action: set the popup disabled");
SwingUtilities.invokeAndWait(() -> popup.setEnabled(false));
boolean isEnabled3 = popup.isEnabled();
boolean isVisible3 = popup.isVisible();
pause(robot);
boolean isEnabled4 = popup.isEnabled();
boolean isVisible4 = popup.isVisible();
if (isEnabled3 || isEnabled4) {
throw new RuntimeException("Expected result: popup is disabled");
}
if (!isVisible3 || !isVisible4) {
throw new RuntimeException("Expected result: disabled popup remains visible");
}
System.out.println("Action: set the popup invisible");
SwingUtilities.invokeAndWait(() -> popup.setVisible(false));
boolean isVisible5 = popup.isVisible();
pause(robot);
boolean isVisible6 = popup.isVisible();
if (isVisible5 && isVisible6) {
throw new RuntimeException("Expected result: disabled popup remains visible");
}
System.out.println("Action: set popup enabled and visible");
SwingUtilities.invokeAndWait(() -> {
popup.setVisible(true);
popup.setEnabled(true);
});
boolean isEnabled7 = popup.isEnabled();
boolean isVisible7 = popup.isVisible();
pause(robot);
boolean isEnabled8 = popup.isEnabled();
boolean isVisible8 = popup.isVisible();
if (!isEnabled7 || !isEnabled8) {
throw new RuntimeException("Expected result: popup is enabled");
}
if (!isVisible7 || !isVisible8) {
throw new RuntimeException("Expected result: popup becoming visible");
}
SwingUtilities.invokeAndWait(frame::dispose);
}
private static void pause(Robot robot) {
robot.waitForIdle();
robot.delay(500);
}
}

View File

@@ -1,5 +1,6 @@
java/awt/Choice/ChoiceHandleMouseEvent.java
java/awt/Choice/ChoiceHandleMouseEvent_2.java JBR-7174 windows-all
java/awt/dnd/RecognizedActionTest/RecognizedActionTest.java JBR-8017 windows-x64
java/awt/Graphics/XORPaint.java#id0 JBR-7499 windows-x64
java/awt/Toolkit/LWCToolkitInvokeAndWaitTest.java nobug macosx-all,linux-all,windows-all
java/awt/Toolkit/AWTThreadingTest.java nobug macosx-all,linux-all,windows-all
@@ -125,6 +126,7 @@ java/awt/Choice/ChoiceConsumeMouseEvents.java JBR-6951 windows-x64
java/awt/Choice/ChoiceFreezeTest.java JBR-6952 windows-x64
java/awt/Choice/ChoiceMouseEventOutbounds.java TBD windows-x64
java/awt/Choice/ChoicePopupLocation/ChoicePopupLocation.java JBR-6857,JBR-5505 macosx-all,windows-all
java/awt/Choice/NonFocusablePopupMenuTest.java JBR-7961 windows-x64
java/awt/Choice/RemoveAllShrinkTest/RemoveAllShrinkTest.java JBR-5510,8310487,JBR-6950 linux-5.18.2-arch1-1,linux-all,windows-x64
java/awt/Choice/ResizeAutoClosesChoice/ResizeAutoClosesChoice.java JBR-5510,JBR-5905 linux-5.18.2-arch1-1,linux-all,windows-x64
java/awt/Component/NativeInLightShow/NativeInLightShow.java JBR-7715 windows-x64

View File

@@ -130,7 +130,7 @@ java/awt/TextArea/UsingWithMouse/SelectionAutoscrollTest.java JBR-7779 linux-all
javax/swing/JEditorPane/TestBrowserBGColor.java JBR-6697 linux-aarch64
javax/swing/plaf/nimbus/8041642/ScrollBarThumbVisibleTest.java JBR-6485 linux-all
javax/swing/plaf/nimbus/8041642/bug8041642.java JBR-6698 linux-all
javax/swing/UI/UnninstallUIMemoryLeaks/UnninstallUIMemoryLeaks.java JBR-5952,JBR-6274 windows-x64,macosx-all
javax/swing/UI/UnninstallUIMemoryLeaks/UnninstallUIMemoryLeaks.java JBR-5952,JBR-6274 windows-all,macosx-all
jb/java/awt/Focus/PopupIncomingFocusTest.java JBR-2651 generic-all
jb/java/awt/Window/UndecoratedDialogInTransientsChain.java JBR-6924 windows-all

View File

@@ -155,7 +155,7 @@ java/awt/Frame/8158918/SetExtendedState.java JBR-6408 linux-all
java/awt/Frame/GetGraphicsStressTest/GetGraphicsStressTest.java JBR-6509 generic-all
java/awt/Frame/MaximizedUndecorated/MaximizedUndecorated.java 8022302 generic-all
java/awt/Frame/MaximizedToIconified/MaximizedToIconified.java 8296972 macosx-all
java/awt/Frame/MaximizedToMaximized/MaximizedToMaximized.java 8340595,JBR-7786 macosx-15.0,linux-all
java/awt/Frame/MaximizedToMaximized/MaximizedToMaximized.java JBR-7786,JBR-7947 linux-all,macosx-15.0,macosx-15.0.1,macosx-15.1.1
java/awt/Frame/MaximizedToOppositeScreen/MaximizedToOppositeScreenBig.java JBR-5303 windows-all
java/awt/Frame/RestoreToOppositeScreen/RestoreToOppositeScreen.java 8286840 linux-all
java/awt/dnd/AcceptDropMultipleTimes/AcceptDropMultipleTimes.java JBR-4880,JBR-6683 windows-all,linux-all
@@ -255,7 +255,7 @@ java/awt/EventDispatchThread/HandleExceptionOnEDT/HandleExceptionOnEDT.java JBR-
java/awt/EventDispatchThread/LoopRobustness/LoopRobustness.java 8073636,JBR-4211 macosx-all,linux-all
java/awt/EventDispatchThread/PropertyPermissionOnEDT/PropertyPermissionOnEDT.java JBR-5225 windows-all
java/awt/FullScreen/AltTabCrashTest/AltTabCrashTest.java JBR-4905 windows-all,linux-all
java/awt/FullScreen/FullScreenInsets/FullScreenInsets.java 7019055,8266245 windows-all,linux-all,macosx-aarch64
java/awt/FullScreen/FullScreenInsets/FullScreenInsets.java 7019055,8266245 windows-all,linux-all,macosx-all
java/awt/FullScreen/FullscreenWindowProps/FullscreenWindowProps.java JBR-4275,JBR-4880 linux-all,windows-all
java/awt/FullScreen/NoResizeEventOnDMChangeTest/NoResizeEventOnDMChangeTest.java 7188711,8253184 linux-all,windows-all
java/awt/FullScreen/UninitializedDisplayModeChangeTest/UninitializedDisplayModeChangeTest.java 7188711,8273617,JBR-4880,8253184 macosx-all,linux-all,windows-all
@@ -393,7 +393,7 @@ java/awt/Frame/MiscUndecorated/ActiveSwingWindowTest.java JBR-5210 windows-all
java/awt/Frame/MiscUndecorated/FrameCloseTest.java JBR-5210 windows-all
java/awt/Frame/MiscUndecorated/RepaintTest.java 8266244,JBR-5786 macosx-aarch64,generic-all
java/awt/Robot/HiDPIMouseClick/HiDPIRobotMouseClick.java 8253184 windows-all
java/awt/Robot/NonEmptyErrorStream.java 8340330,JBR-5510 macosx-15.0,linux-5.18.2-arch1-1
java/awt/Robot/NonEmptyErrorStream.java 8340330,JBR-5510 macosx-15.0.1,macosx-15.1.1,linux-5.18.2-arch1-1
java/awt/Robot/RobotExtraButton/RobotExtraButton.java JBR-6554 linux-all
java/awt/Modal/FileDialog/FileDialogAppModal1Test.java 7186009,8253184 macosx-all,windows-all
java/awt/Modal/FileDialog/FileDialogAppModal2Test.java 7186009,8253184 macosx-all,windows-all
@@ -709,7 +709,7 @@ java/awt/PopupMenu/PopupMenuLocation.java 8238720,JBR-7035 windows-all,macosx-al
java/awt/GridBagLayout/GridBagLayoutIpadXYTest/GridBagLayoutIpadXYTest.java 8253184 windows-all
java/awt/GridLayout/ChangeGridSize/ChangeGridSize.java 8238720,8324782 windows-all,macosx-all
java/awt/GridLayout/ComponentPreferredSize/ComponentPreferredSize.java 8238720,8324782 windows-all,macosx-all
java/awt/GridLayout/LayoutExtraGaps/LayoutExtraGaps.java JBR-7847 linux-aarch64
java/awt/GridLayout/LayoutExtraGaps/LayoutExtraGaps.java JBR-7847 linux-all
java/awt/event/MouseEvent/FrameMouseEventAbsoluteCoordsTest/FrameMouseEventAbsoluteCoordsTest.java 8238720 windows-all
java/awt/wakefield/RobotKeyboard.java JBR-5653 linux-all
@@ -943,7 +943,7 @@ javax/swing/JButton/4368790/bug4368790.java JBR-5210 windows-all
javax/swing/JButton/4659800/SpaceKeyActivatesButton.java JBR-4949 linux-all,windows-all
javax/swing/JButton/8151303/PressedIconTest.java JBR-5210,JBR-5510 windows-all,linux-5.18.2-arch1-1
javax/swing/JButton/PressedButtonRightClickTest.java JBR-5210 windows-all
javax/swing/JButton/TestMnemonicAction.java JBR-6508 windows-x64,linux-all
javax/swing/JButton/TestMnemonicAction.java JBR-6508 windows-all,linux-all
javax/swing/JColorChooser/Test6524757.java JBR-5210 windows-all
javax/swing/JColorChooser/Test6827032.java JBR-5210 windows-all
javax/swing/JComboBox/4199622/bug4199622.java JBR-6751 winows-x64
@@ -983,7 +983,7 @@ javax/swing/JOptionPane/8081019/bug8081019.java JBR-5767 windows-all
javax/swing/JPopupMenu/4634626/bug4634626.java 8253184 windows-all
javax/swing/JPopupMenu/4760494/bug4760494.java 8253184 windows-all
javax/swing/JPopupMenu/4870644/bug4870644.java 8194130 macosx-all,linux-all
javax/swing/JPopupMenu/4966112/bug4966112.java 8253184 windows-all
javax/swing/JPopupMenu/4966112/bug4966112.java 8253184,JBR-7946 windows-all,linux-all
javax/swing/JPopupMenu/6415145/bug6415145.java 8197552 windows-all
javax/swing/JPopupMenu/6495920/bug6495920.java JBR-6928 linux-all
javax/swing/JPopupMenu/6515446/bug6515446.java 8197552,JBR-6531 windows-all,linux-all

View File

@@ -1,3 +1,5 @@
java/awt/Choice/ChoiceMouseEventOutbounds.java JBR-7980 windows-all
java/awt/Choice/ChoiceMoveTest.java JBR-7980 windows-all
java/awt/Dialog/DialogLocationTest.java JBR-6090 windows-all
java/awt/datatransfer/DragUnicodeBetweenJVMTest/DragUnicodeBetweenJVMTest.java JBR-5538 windows-all
java/awt/datatransfer/MissedHtmlAndRtfBug/MissedHtmlAndRtfBug.java JBR-6027 windows-all