1/* swc: libswc/panel.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 "panel.h"
25#include "compositor.h"
26#include "internal.h"
27#include "keyboard.h"
28#include "output.h"
29#include "screen.h"
30#include "seat.h"
31#include "surface.h"
32#include "util.h"
33#include "view.h"
34
35#include "swc-server-protocol.h"
36#include <assert.h>
37#include <stdlib.h>
38
39struct panel {
40 struct wl_resource *resource;
41
42 struct wl_listener surface_destroy_listener;
43 struct compositor_view *view;
44 struct view_handler view_handler;
45 struct screen *screen;
46 struct screen_modifier modifier;
47 uint32_t edge;
48 uint32_t offset, strut_size;
49 uint32_t y_offset;
50 bool docked;
51};
52
53static void
54update_position(struct panel *panel)
55{
56 int32_t x, y;
57 struct swc_rectangle *screen = &panel->screen->base.geometry,
58 *view = &panel->view->base.geometry;
59
60 switch (panel->edge) {
61 case SWC_PANEL_EDGE_TOP:
62 x = screen->x + panel->offset;
63 y = screen->y + panel->y_offset;
64 break;
65 case SWC_PANEL_EDGE_BOTTOM:
66 x = screen->x + panel->offset;
67 y = screen->y + screen->height - view->height - panel->y_offset;
68 break;
69 case SWC_PANEL_EDGE_LEFT:
70 x = screen->x;
71 y = screen->y + screen->height - view->height - panel->offset;
72 break;
73 case SWC_PANEL_EDGE_RIGHT:
74 x = screen->x + screen->width - view->width;
75 y = screen->y + panel->offset;
76 break;
77 default:
78 return;
79 }
80
81 view_move(&panel->view->base, x, y);
82}
83
84static void
85dock(struct wl_client *client, struct wl_resource *resource, uint32_t edge,
86 struct wl_resource *screen_resource, uint32_t focus)
87{
88 struct panel *panel = wl_resource_get_user_data(resource);
89 struct screen *screen;
90 uint32_t length;
91
92 if (screen_resource) {
93 screen = wl_resource_get_user_data(screen_resource);
94 } else {
95 screen = wl_container_of(swc.screens.next, screen, link);
96 }
97
98 switch (edge) {
99 case SWC_PANEL_EDGE_TOP:
100 case SWC_PANEL_EDGE_BOTTOM:
101 length = screen->base.geometry.width;
102 break;
103 case SWC_PANEL_EDGE_LEFT:
104 case SWC_PANEL_EDGE_RIGHT:
105 length = screen->base.geometry.height;
106 break;
107 default:
108 return;
109 }
110
111 if (panel->screen && screen != panel->screen) {
112 wl_list_remove(&panel->modifier.link);
113 screen_update_usable_geometry(panel->screen);
114 }
115
116 panel->screen = screen;
117 panel->edge = edge;
118 panel->docked = true;
119
120 update_position(panel);
121 compositor_view_show(panel->view);
122 raise_window_top(panel->view);
123 wl_list_insert(&screen->modifiers, &panel->modifier.link);
124
125 if (focus) {
126 keyboard_set_focus(swc.seat->keyboard, panel->view);
127 }
128
129 swc_panel_send_docked(resource, length);
130}
131
132static void
133set_offset(struct wl_client *client, struct wl_resource *resource,
134 uint32_t offset)
135{
136 struct panel *panel = wl_resource_get_user_data(resource);
137
138 panel->offset = offset;
139 if (panel->docked) {
140 update_position(panel);
141 }
142}
143
144static void
145set_y_offset(struct wl_client *client, struct wl_resource *resource,
146 uint32_t offset)
147{
148 struct panel *panel = wl_resource_get_user_data(resource);
149
150 panel->y_offset = offset;
151 if (panel->docked) {
152 update_position(panel);
153 }
154}
155
156static void
157set_strut(struct wl_client *client, struct wl_resource *resource, uint32_t size,
158 uint32_t begin, uint32_t end)
159{
160 struct panel *panel = wl_resource_get_user_data(resource);
161
162 panel->strut_size = size;
163 if (panel->docked) {
164 screen_update_usable_geometry(panel->screen);
165 }
166}
167
168static const struct swc_panel_interface panel_impl = {
169 .dock = dock,
170 .set_offset = set_offset,
171 .set_y_offset = set_y_offset,
172 .set_strut = set_strut,
173};
174
175static void
176handle_resize(struct view_handler *handler, uint32_t old_width,
177 uint32_t old_height)
178{
179 struct panel *panel = wl_container_of(handler, panel, view_handler);
180 update_position(panel);
181}
182
183static const struct view_handler_impl view_handler_impl = {
184 .resize = handle_resize,
185};
186
187static void
188modify(struct screen_modifier *modifier, const struct swc_rectangle *geom,
189 pixman_region32_t *usable)
190{
191 struct panel *panel = wl_container_of(modifier, panel, modifier);
192 pixman_box32_t box = {.x1 = geom->x,
193 .y1 = geom->y,
194 .x2 = geom->x + geom->width,
195 .y2 = geom->y + geom->height};
196
197 assert(panel->docked);
198
199 DEBUG("Original geometry { x1: %d, y1: %d, x2: %d, y2: %d }\n", box.x1,
200 box.y1, box.x2, box.y2);
201
202 switch (panel->edge) {
203 case SWC_PANEL_EDGE_TOP:
204 box.y1 = MAX(box.y1, geom->y + panel->strut_size);
205 break;
206 case SWC_PANEL_EDGE_BOTTOM:
207 box.y2 = MIN(box.y2, geom->y + geom->height - panel->strut_size);
208 break;
209 case SWC_PANEL_EDGE_LEFT:
210 box.x1 = MAX(box.x1, geom->x + panel->strut_size);
211 break;
212 case SWC_PANEL_EDGE_RIGHT:
213 box.x2 = MIN(box.x2, geom->x + geom->width - panel->strut_size);
214 break;
215 }
216
217 DEBUG("Usable region { x1: %d, y1: %d, x2: %d, y2: %d }\n", box.x1, box.y1,
218 box.x2, box.y2);
219
220 pixman_region32_reset(usable, &box);
221}
222
223static void
224destroy_panel(struct wl_resource *resource)
225{
226 struct panel *panel = wl_resource_get_user_data(resource);
227
228 if (panel->docked) {
229 wl_list_remove(&panel->modifier.link);
230 screen_update_usable_geometry(panel->screen);
231 }
232
233 compositor_view_destroy(panel->view);
234 free(panel);
235}
236
237static void
238handle_surface_destroy(struct wl_listener *listener, void *data)
239{
240 struct panel *panel =
241 wl_container_of(listener, panel, surface_destroy_listener);
242 wl_resource_destroy(panel->resource);
243}
244
245struct panel *
246panel_new(struct wl_client *client, uint32_t version, uint32_t id,
247 struct surface *surface)
248{
249 struct panel *panel;
250
251 panel = malloc(sizeof(*panel));
252
253 if (!panel) {
254 goto error0;
255 }
256
257 panel->resource =
258 wl_resource_create(client, &swc_panel_interface, version, id);
259
260 if (!panel->resource) {
261 goto error0;
262 }
263
264 if (!surface_set_role(surface, panel->resource)) {
265 goto error2;
266 }
267
268 if (!(panel->view = compositor_create_view(surface))) {
269 goto error3;
270 }
271
272 wl_resource_set_implementation(panel->resource, &panel_impl, panel,
273 &destroy_panel);
274 panel->surface_destroy_listener.notify = &handle_surface_destroy;
275 panel->view_handler.impl = &view_handler_impl;
276 panel->view->always_top = true;
277 panel->modifier.modify = &modify;
278 panel->screen = NULL;
279 panel->offset = 0;
280 panel->y_offset = 0;
281 panel->strut_size = 0;
282 panel->docked = false;
283 wl_list_insert(&panel->view->base.handlers, &panel->view_handler.link);
284 wl_resource_add_destroy_listener(surface->resource,
285 &panel->surface_destroy_listener);
286
287 return panel;
288
289error3:
290 wl_resource_destroy(panel->resource);
291error2:
292 free(panel);
293error0:
294 return NULL;
295}