1/* swc: libswc/screen.c
2 *
3 * Copyright (c) 2013, 2014 Michael Forney
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24#include "screen.h"
25#include "drm.h"
26#include "event.h"
27#include "internal.h"
28#include "mode.h"
29#include "output.h"
30#include "plane.h"
31#include "pointer.h"
32#include "util.h"
33
34#include "swc-server-protocol.h"
35#include <stdlib.h>
36
37#define INTERNAL(s) ((struct screen *)(s))
38
39static struct screen *active_screen;
40static const struct swc_screen_handler null_handler;
41
42static bool
43handle_motion(struct pointer_handler *handler,
44 uint32_t time,
45 wl_fixed_t x,
46 wl_fixed_t y);
47
48struct pointer_handler screens_pointer_handler = {
49 .motion = handle_motion,
50};
51
52EXPORT void
53swc_screen_set_handler(struct swc_screen *base,
54 const struct swc_screen_handler *handler,
55 void *data)
56{
57 struct screen *screen = INTERNAL(base);
58
59 screen->handler = handler;
60 screen->handler_data = data;
61}
62
63bool
64screens_initialize(void)
65{
66 wl_list_init(&swc.screens);
67
68 if (!drm_create_screens(&swc.screens)) {
69 return false;
70 }
71
72 if (wl_list_empty(&swc.screens)) {
73 return false;
74 }
75
76 return true;
77}
78
79void
80screens_finalize(void)
81{
82 struct screen *screen, *tmp;
83
84 wl_list_for_each_safe(screen, tmp, &swc.screens, link)
85 screen_destroy(screen);
86}
87
88static void
89bind_screen(struct wl_client *client, void *data, uint32_t version, uint32_t id)
90{
91 struct screen *screen = data;
92 struct wl_resource *resource;
93
94 resource = wl_resource_create(client, &swc_screen_interface, version, id);
95
96 if (!resource) {
97 wl_client_post_no_memory(client);
98 return;
99 }
100
101 wl_resource_set_implementation(resource, NULL, screen, &remove_resource);
102 wl_list_insert(&screen->resources, wl_resource_get_link(resource));
103}
104
105struct screen *
106screen_new(uint32_t crtc, struct output *output, struct plane *cursor_plane)
107{
108 struct screen *screen;
109 int32_t x = 0;
110
111 /* Simple heuristic for initial screen positioning. */
112 wl_list_for_each(screen, &swc.screens, link) x =
113 MAX(x, screen->base.geometry.x + screen->base.geometry.width);
114
115 if (!(screen = malloc(sizeof(*screen)))) {
116 goto error0;
117 }
118
119 screen->global = wl_global_create(
120 swc.display, &swc_screen_interface, 1, screen, &bind_screen);
121
122 if (!screen->global) {
123 ERROR("Failed to create screen global\n");
124 goto error1;
125 }
126
127 screen->crtc = crtc;
128
129 if (!primary_plane_initialize(&screen->planes.primary,
130 crtc,
131 output->preferred_mode,
132 &output->connector,
133 1)) {
134 ERROR("Failed to initialize primary plane\n");
135 goto error2;
136 }
137
138 cursor_plane->screen = screen;
139 screen->planes.cursor = cursor_plane;
140
141 screen->handler = &null_handler;
142 wl_signal_init(&screen->destroy_signal);
143 wl_list_init(&screen->resources);
144 wl_list_init(&screen->outputs);
145 wl_list_insert(&screen->outputs, &output->link);
146 wl_list_init(&screen->modifiers);
147
148 view_move(&screen->planes.primary.view, x, 0);
149 screen->base.geometry = screen->planes.primary.view.geometry;
150 screen->base.usable_geometry = screen->base.geometry;
151
152 swc.manager->new_screen(&screen->base);
153
154 return screen;
155
156error2:
157 wl_global_destroy(screen->global);
158error1:
159 free(screen);
160error0:
161 return NULL;
162}
163
164void
165screen_destroy(struct screen *screen)
166{
167 struct output *output, *next;
168
169 if (active_screen == screen) {
170 active_screen = NULL;
171 }
172 if (screen->handler->destroy) {
173 screen->handler->destroy(screen->handler_data);
174 }
175 wl_signal_emit(&screen->destroy_signal, NULL);
176 wl_list_for_each_safe(output, next, &screen->outputs, link)
177 output_destroy(output);
178 primary_plane_finalize(&screen->planes.primary);
179 plane_destroy(screen->planes.cursor);
180 free(screen);
181}
182
183void
184screen_update_usable_geometry(struct screen *screen)
185{
186 pixman_region32_t total_usable, usable;
187 pixman_box32_t *extents;
188 struct screen_modifier *modifier;
189 struct swc_rectangle *geom = &screen->base.geometry;
190
191 DEBUG("Updating usable geometry\n");
192
193 pixman_region32_init_rect(
194 &total_usable, geom->x, geom->y, geom->width, geom->height);
195 pixman_region32_init(&usable);
196
197 wl_list_for_each(modifier, &screen->modifiers, link)
198 {
199 modifier->modify(modifier, geom, &usable);
200 pixman_region32_intersect(&total_usable, &total_usable, &usable);
201 }
202
203 extents = pixman_region32_extents(&total_usable);
204
205 if (extents->x1 != screen->base.usable_geometry.x ||
206 extents->y1 != screen->base.usable_geometry.y ||
207 (extents->x2 - extents->x1) != screen->base.usable_geometry.width ||
208 (extents->y2 - extents->y1) != screen->base.usable_geometry.height) {
209 screen->base.usable_geometry.x = extents->x1;
210 screen->base.usable_geometry.y = extents->y1;
211 screen->base.usable_geometry.width = extents->x2 - extents->x1;
212 screen->base.usable_geometry.height = extents->y2 - extents->y1;
213
214 if (screen->handler->usable_geometry_changed) {
215 screen->handler->usable_geometry_changed(screen->handler_data);
216 }
217 }
218}
219
220bool
221handle_motion(struct pointer_handler *handler,
222 uint32_t time,
223 wl_fixed_t fx,
224 wl_fixed_t fy)
225{
226 struct screen *screen;
227 int32_t x = wl_fixed_to_int(fx), y = wl_fixed_to_int(fy);
228
229 wl_list_for_each(screen, &swc.screens, link)
230 {
231 if (rectangle_contains_point(&screen->base.geometry, x, y)) {
232 if (screen != active_screen) {
233 active_screen = screen;
234
235 if (screen->handler->entered) {
236 screen->handler->entered(screen->handler_data);
237 }
238 }
239 break;
240 }
241 }
242
243 return false;
244}