2 * WAYLAND display device functions
4 * Copyright 2020 Alexandros Frantzis for Collabora Ltd
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "waylanddrv.h"
29 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv
);
37 void wayland_init_display_devices(void)
39 UINT32 num_path
, num_mode
;
40 /* Trigger refresh in win32u */
41 NtUserGetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS
, &num_path
, &num_mode
);
47 struct wayland_output
*output
;
50 static int output_info_cmp_primary_x_y(const void *va
, const void *vb
)
52 const struct output_info
*a
= va
;
53 const struct output_info
*b
= vb
;
54 BOOL a_is_primary
= a
->x
== 0 && a
->y
== 0;
55 BOOL b_is_primary
= b
->x
== 0 && b
->y
== 0;
57 if (a_is_primary
&& !b_is_primary
) return -1;
58 if (!a_is_primary
&& b_is_primary
) return 1;
59 if (a
->x
< b
->x
) return -1;
60 if (a
->x
> b
->x
) return 1;
61 if (a
->y
< b
->y
) return -1;
62 if (a
->y
> b
->y
) return 1;
63 return strcmp(a
->output
->name
, b
->output
->name
);
66 static inline BOOL
output_info_overlap(struct output_info
*a
, struct output_info
*b
)
68 return b
->x
< a
->x
+ a
->output
->current_mode
->width
&&
69 b
->x
+ b
->output
->current_mode
->width
> a
->x
&&
70 b
->y
< a
->y
+ a
->output
->current_mode
->height
&&
71 b
->y
+ b
->output
->current_mode
->height
> a
->y
;
74 /* Map a point to one of the four quadrants of our 2d coordinate space:
75 * 0: bottom right (x >= 0, y >= 0)
76 * 1: top right (x >= 0, y < 0)
77 * 2: bottom left (x < 0, y >= 0)
78 * 3: top left (x < 0, y < 0) */
79 static inline int point_to_quadrant(int x
, int y
)
81 return (x
< 0) * 2 + (y
< 0);
84 /* Decide which of two outputs to keep stationary in order
85 * to resolve an overlap. */
86 static struct output_info
*output_info_get_overlap_anchor(struct output_info
*a
,
87 struct output_info
*b
)
89 /* Preferences for the direction of growth in each quadrant, with a
90 * lower value signifying a higher preference. */
91 static const int quadrant_prefs
[4][4] =
93 {0, 1, 2, 3}, /* quadrant 0 */
94 {3, 0, 2, 1}, /* quadrant 1 */
95 {2, 3, 0, 1}, /* quadrant 2 */
96 {3, 2, 1, 0}, /* quadrant 3 */
98 int qa
= point_to_quadrant(a
->output
->logical_x
, a
->output
->logical_y
);
99 int qb
= point_to_quadrant(b
->output
->logical_x
, b
->output
->logical_y
);
100 /* Direction of growth if a is the anchor. */
101 int qab
= point_to_quadrant(b
->output
->logical_x
- a
->output
->logical_x
,
102 b
->output
->logical_y
- a
->output
->logical_y
);
103 /* Direction of growth if b is the anchor. */
104 int qba
= point_to_quadrant(a
->output
->logical_x
- b
->output
->logical_x
,
105 a
->output
->logical_y
- b
->output
->logical_y
);
107 /* If the two output origins are in different quadrants, use the output
108 * in the lower valued quadrant as the anchor (so effectively outputs
109 * grow/move away from quadrant 0). */
110 if (qa
!= qb
) return (qa
< qb
) ? a
: b
;
112 /* If the outputs are in the same quadrant, use the preference for the
113 * direction of growth in that quadrant to select the anchor. Again the
114 * intended effect is to grow/move outputs away from the origin. */
115 return (quadrant_prefs
[qa
][qab
] < quadrant_prefs
[qa
][qba
]) ? a
: b
;
118 static BOOL
output_info_array_resolve_overlaps(struct wl_array
*output_info_array
)
120 struct output_info
*a
, *b
;
121 BOOL found_overlap
= FALSE
;
123 wl_array_for_each(a
, output_info_array
)
125 wl_array_for_each(b
, output_info_array
)
127 struct output_info
*anchor
, *move
;
128 BOOL x_use_end
, y_use_end
;
131 /* Break if we reach the same output in the inner loop, so that we
132 * don't process output pairs twice (since order doesn't matter for
136 if (!output_info_overlap(a
, b
)) continue;
137 found_overlap
= TRUE
;
139 /* Decide which output to move to resolve the overlap. */
140 anchor
= output_info_get_overlap_anchor(a
, b
);
141 move
= anchor
== a
? b
: a
;
143 /* Move the selected output on the X axis to resolve the overlap,
144 * while maintaining the same relative positioning of the outputs as
145 * the one they have in logical space. Use either the start or end
146 * of the moved output as the point to maintain the relative
147 * position of, depending on whether the anchor is before or after
148 * the moved output on the axis. */
149 x_use_end
= move
->output
->logical_x
< anchor
->output
->logical_x
;
150 rel_x
= (move
->output
->logical_x
- anchor
->output
->logical_x
+
151 (x_use_end
? move
->output
->logical_w
: 0)) /
152 (double)anchor
->output
->logical_w
;
153 move
->x
= anchor
->x
+ anchor
->output
->current_mode
->width
* rel_x
-
154 (x_use_end
? move
->output
->current_mode
->width
: 0);
156 /* Similarly for the Y axis. */
157 y_use_end
= move
->output
->logical_y
< anchor
->output
->logical_y
;
158 rel_y
= (move
->output
->logical_y
- anchor
->output
->logical_y
+
159 (y_use_end
? move
->output
->logical_h
: 0)) /
160 (double)anchor
->output
->logical_h
;
161 move
->y
= anchor
->y
+ anchor
->output
->current_mode
->height
* rel_y
-
162 (y_use_end
? move
->output
->current_mode
->height
: 0);
166 return found_overlap
;
169 static void output_info_array_arrange_physical_coords(struct wl_array
*output_info_array
)
171 struct output_info
*info
;
172 size_t num_outputs
= output_info_array
->size
/ sizeof(struct output_info
);
175 /* Set the initial physical pixel coordinates. */
176 wl_array_for_each(info
, output_info_array
)
178 info
->x
= info
->output
->logical_x
;
179 info
->y
= info
->output
->logical_y
;
182 /* Try to iteratively resolve overlaps, but be defensive and set an upper
183 * iteration bound to ensure we avoid infinite loops. */
184 while (output_info_array_resolve_overlaps(output_info_array
) &&
185 ++steps
< num_outputs
)
188 /* Now that we have our physical pixel coordinates, sort from physical left
189 * to right, but ensure the primary output is first. */
190 qsort(output_info_array
->data
, num_outputs
, sizeof(struct output_info
),
191 output_info_cmp_primary_x_y
);
194 static void wayland_add_device_gpu(const struct gdi_device_manager
*device_manager
,
197 static const WCHAR wayland_gpuW
[] = {'W','a','y','l','a','n','d','G','P','U',0};
198 struct gdi_gpu gpu
= {0};
199 lstrcpyW(gpu
.name
, wayland_gpuW
);
201 TRACE("id=0x%s name=%s\n",
202 wine_dbgstr_longlong(gpu
.id
), wine_dbgstr_w(gpu
.name
));
204 device_manager
->add_gpu(&gpu
, param
);
207 static void wayland_add_device_adapter(const struct gdi_device_manager
*device_manager
,
208 void *param
, INT output_id
)
210 struct gdi_adapter adapter
;
211 adapter
.id
= output_id
;
212 adapter
.state_flags
= DISPLAY_DEVICE_ATTACHED_TO_DESKTOP
;
214 adapter
.state_flags
|= DISPLAY_DEVICE_PRIMARY_DEVICE
;
216 TRACE("id=0x%s state_flags=0x%x\n",
217 wine_dbgstr_longlong(adapter
.id
), (UINT
)adapter
.state_flags
);
219 device_manager
->add_adapter(&adapter
, param
);
222 static void wayland_add_device_monitor(const struct gdi_device_manager
*device_manager
,
223 void *param
, struct output_info
*output_info
)
225 struct gdi_monitor monitor
= {0};
227 SetRect(&monitor
.rc_monitor
, output_info
->x
, output_info
->y
,
228 output_info
->x
+ output_info
->output
->current_mode
->width
,
229 output_info
->y
+ output_info
->output
->current_mode
->height
);
231 /* We don't have a direct way to get the work area in Wayland. */
232 monitor
.rc_work
= monitor
.rc_monitor
;
234 monitor
.state_flags
= DISPLAY_DEVICE_ATTACHED
| DISPLAY_DEVICE_ACTIVE
;
236 TRACE("name=%s rc_monitor=rc_work=%s state_flags=0x%x\n",
237 output_info
->output
->name
, wine_dbgstr_rect(&monitor
.rc_monitor
),
238 (UINT
)monitor
.state_flags
);
240 device_manager
->add_monitor(&monitor
, param
);
243 static void populate_devmode(struct wayland_output_mode
*output_mode
, DEVMODEW
*mode
)
245 mode
->dmFields
= DM_DISPLAYORIENTATION
| DM_BITSPERPEL
| DM_PELSWIDTH
| DM_PELSHEIGHT
|
246 DM_DISPLAYFLAGS
| DM_DISPLAYFREQUENCY
;
247 mode
->dmDisplayOrientation
= DMDO_DEFAULT
;
248 mode
->dmDisplayFlags
= 0;
249 mode
->dmBitsPerPel
= 32;
250 mode
->dmPelsWidth
= output_mode
->width
;
251 mode
->dmPelsHeight
= output_mode
->height
;
252 mode
->dmDisplayFrequency
= output_mode
->refresh
/ 1000;
255 static void wayland_add_device_modes(const struct gdi_device_manager
*device_manager
,
256 void *param
, struct output_info
*output_info
)
258 struct wayland_output_mode
*output_mode
;
260 RB_FOR_EACH_ENTRY(output_mode
, &output_info
->output
->modes
,
261 struct wayland_output_mode
, entry
)
263 DEVMODEW mode
= {.dmSize
= sizeof(mode
)};
264 BOOL mode_is_current
= output_mode
== output_info
->output
->current_mode
;
265 populate_devmode(output_mode
, &mode
);
268 mode
.dmFields
|= DM_POSITION
;
269 mode
.dmPosition
.x
= output_info
->x
;
270 mode
.dmPosition
.y
= output_info
->y
;
272 device_manager
->add_mode(&mode
, mode_is_current
, param
);
276 /***********************************************************************
277 * UpdateDisplayDevices (WAYLAND.@)
279 BOOL
WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager
*device_manager
,
280 BOOL force
, void *param
)
282 struct wayland_output
*output
;
284 struct wl_array output_info_array
;
285 struct output_info
*output_info
;
287 if (!force
) return TRUE
;
289 TRACE("force=%d\n", force
);
291 wl_array_init(&output_info_array
);
293 wl_list_for_each(output
, &process_wayland
->output_list
, link
)
295 if (!output
->current_mode
) continue;
296 output_info
= wl_array_add(&output_info_array
, sizeof(*output_info
));
297 if (output_info
) output_info
->output
= output
;
298 else ERR("Failed to allocate space for output_info\n");
301 output_info_array_arrange_physical_coords(&output_info_array
);
303 /* Populate GDI devices. */
304 wayland_add_device_gpu(device_manager
, param
);
306 wl_array_for_each(output_info
, &output_info_array
)
308 wayland_add_device_adapter(device_manager
, param
, output_id
);
309 wayland_add_device_monitor(device_manager
, param
, output_info
);
310 wayland_add_device_modes(device_manager
, param
, output_info
);
314 wl_array_release(&output_info_array
);