dinput: Clear DIA_APPNOMAP BuildActionMap flag with specific device semantic.
[wine.git] / dlls / winewayland.drv / display.c
blob5234a1f03aac1f7fe5d6ef75bcbfe03f6be02268
1 /*
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
21 #if 0
22 #pragma makedep unix
23 #endif
25 #include "config.h"
27 #include "waylanddrv.h"
29 #include "wine/debug.h"
31 #include "ntuser.h"
33 #include <stdlib.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);
44 struct output_info
46 int x, y;
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;
129 double rel_x, rel_y;
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
133 * our algorithm.) */
134 if (a == b) break;
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);
173 int steps = 0;
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)
186 continue;
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,
195 void *param)
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;
213 if (output_id == 0)
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);
266 if (mode_is_current)
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;
283 INT output_id = 0;
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);
311 output_id++;
314 wl_array_release(&output_info_array);
316 return TRUE;