├── 80
├── .gitmodules
├── Makefile
├── README.md
├── protocols
└── wlr-layer-shell-unstable-v1.xml
├── screenshots
└── stage.png
├── stage.c
└── ws
├── meson.build
├── protocols
└── wlr-layer-shell-unstable-v1.xml
└── src
├── image.c
├── image.h
└── main.c
/80:
--------------------------------------------------------------------------------
1 | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
2 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "wlroots"]
2 | path = wlroots
3 | url = https://gitlab.freedesktop.org/wlroots/wlroots
4 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | WL_SCANNER = /usr/local/bin/wayland-scanner
2 | WLR_LAYER_SHELL = protocols/wlr-layer-shell-unstable-v1.xml
3 | XDG_SHELL = /usr/local/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml
4 |
5 | CFLAGS += -I/usr/local/include/pixman-1/ -I.
6 | CFLAGS += -I/usr/local/include/wlroots-0.19/
7 | CFLAGS += -I/usr/local/include/
8 | CFLAGS += -DWLR_USE_UNSTABLE
9 |
10 | LDFLAGS = -L/usr/local/lib -lwayland-server -lwlroots-0.19 -lxkbcommon -lm
11 |
12 | HEADERS = xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h
13 |
14 | all: ${HEADERS}
15 | cc ${CFLAGS} ${LDFLAGS} stage.c -o stage
16 |
17 | dev: ${HEADERS}
18 | cc ${CFLAGS} -DSTAGE_DEV ${LDFLAGS} stage.c -o stage
19 |
20 | xdg-shell-protocol.h:
21 | ${WL_SCANNER} server-header ${XDG_SHELL} $@
22 |
23 | wlr-layer-shell-unstable-v1-protocol.h:
24 | ${WL_SCANNER} server-header ${WLR_LAYER_SHELL} $@
25 |
26 | run:
27 | mkdir -p /tmp/wl
28 | sudo chmod 0777 /var/run/seatd.sock
29 | sudo chmod 0777 /dev/dri/card0 /dev/dri/renderD128
30 |
31 | clean:
32 | rm -f stage stage.o ${HEADERS}
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | The Stage window manager for Wayland.
2 |
3 | This is a minimalistic tiling window manager Stage, which is identical (in terms of features) rewrite of my previous X11 window manager that I had been using for about 10 years.
4 |
5 | I spend just a week to write it, but already using it on daily basis. So be careful, it has lots of bugs.
6 |
7 | Note this depends on development version of wlroots.
8 |
9 | All the features supported you can find reading the code. If you don't want to read code, this WM is not for you.
10 |
11 | The main reason for this WM is to define memory regions (slots) where a new terminal window appears once opened.
12 |
13 | The width limit of a terminal window is enforced: it is 80 characters per Linux/FreeBSD code style guides. Configure this in the stage.c for your font size.
14 |
15 | The default terminal is foot(1). I have the following configuration of foot in my ~/.config/foot/foot.ini:
16 | ```
17 | font=xos4 Terminus:size=13
18 | ```
19 |
20 | Keyboard bindings:
21 | - Mod + Enter: open a terminal
22 | - Mod + m: maximize/unmaximize a terminal window vertically
23 | - Mod + 0..9: change workspace
24 |
25 | Mouse buttons:
26 | - Mod + Left Button: move window
27 | - Mod + Right Button: resize window
28 |
29 | 
30 |
--------------------------------------------------------------------------------
/protocols/wlr-layer-shell-unstable-v1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Copyright © 2017 Drew DeVault
5 |
6 | Permission to use, copy, modify, distribute, and sell this
7 | software and its documentation for any purpose is hereby granted
8 | without fee, provided that the above copyright notice appear in
9 | all copies and that both that copyright notice and this permission
10 | notice appear in supporting documentation, and that the name of
11 | the copyright holders not be used in advertising or publicity
12 | pertaining to distribution of the software without specific,
13 | written prior permission. The copyright holders make no
14 | representations about the suitability of this software for any
15 | purpose. It is provided "as is" without express or implied
16 | warranty.
17 |
18 | THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
19 | SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 | FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 | SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
22 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
23 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
24 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
25 | THIS SOFTWARE.
26 |
27 |
28 |
29 |
30 | Clients can use this interface to assign the surface_layer role to
31 | wl_surfaces. Such surfaces are assigned to a "layer" of the output and
32 | rendered with a defined z-depth respective to each other. They may also be
33 | anchored to the edges and corners of a screen and specify input handling
34 | semantics. This interface should be suitable for the implementation of
35 | many desktop shell components, and a broad number of other applications
36 | that interact with the desktop.
37 |
38 |
39 |
40 |
41 | Create a layer surface for an existing surface. This assigns the role of
42 | layer_surface, or raises a protocol error if another role is already
43 | assigned.
44 |
45 | Creating a layer surface from a wl_surface which has a buffer attached
46 | or committed is a client error, and any attempts by a client to attach
47 | or manipulate a buffer prior to the first layer_surface.configure call
48 | must also be treated as errors.
49 |
50 | After creating a layer_surface object and setting it up, the client
51 | must perform an initial commit without any buffer attached.
52 | The compositor will reply with a layer_surface.configure event.
53 | The client must acknowledge it and is then allowed to attach a buffer
54 | to map the surface.
55 |
56 | You may pass NULL for output to allow the compositor to decide which
57 | output to use. Generally this will be the one that the user most
58 | recently interacted with.
59 |
60 | Clients can specify a namespace that defines the purpose of the layer
61 | surface.
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | These values indicate which layers a surface can be rendered in. They
79 | are ordered by z depth, bottom-most first. Traditional shell surfaces
80 | will typically be rendered between the bottom and top layers.
81 | Fullscreen shell surfaces are typically rendered at the top layer.
82 | Multiple surfaces can share a single layer, and ordering within a
83 | single layer is undefined.
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | This request indicates that the client will not use the layer_shell
97 | object any more. Objects that have been created through this instance
98 | are not affected.
99 |
100 |
101 |
102 |
103 |
104 |
105 | An interface that may be implemented by a wl_surface, for surfaces that
106 | are designed to be rendered as a layer of a stacked desktop-like
107 | environment.
108 |
109 | Layer surface state (layer, size, anchor, exclusive zone,
110 | margin, interactivity) is double-buffered, and will be applied at the
111 | time wl_surface.commit of the corresponding wl_surface is called.
112 |
113 | Attaching a null buffer to a layer surface unmaps it.
114 |
115 | Unmapping a layer_surface means that the surface cannot be shown by the
116 | compositor until it is explicitly mapped again. The layer_surface
117 | returns to the state it had right after layer_shell.get_layer_surface.
118 | The client can re-map the surface by performing a commit without any
119 | buffer attached, waiting for a configure event and handling it as usual.
120 |
121 |
122 |
123 |
124 | Sets the size of the surface in surface-local coordinates. The
125 | compositor will display the surface centered with respect to its
126 | anchors.
127 |
128 | If you pass 0 for either value, the compositor will assign it and
129 | inform you of the assignment in the configure event. You must set your
130 | anchor to opposite edges in the dimensions you omit; not doing so is a
131 | protocol error. Both values are 0 by default.
132 |
133 | Size is double-buffered, see wl_surface.commit.
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 | Requests that the compositor anchor the surface to the specified edges
142 | and corners. If two orthogonal edges are specified (e.g. 'top' and
143 | 'left'), then the anchor point will be the intersection of the edges
144 | (e.g. the top left corner of the output); otherwise the anchor point
145 | will be centered on that edge, or in the center if none is specified.
146 |
147 | Anchor is double-buffered, see wl_surface.commit.
148 |
149 |
150 |
151 |
152 |
153 |
154 | Requests that the compositor avoids occluding an area with other
155 | surfaces. The compositor's use of this information is
156 | implementation-dependent - do not assume that this region will not
157 | actually be occluded.
158 |
159 | A positive value is only meaningful if the surface is anchored to one
160 | edge or an edge and both perpendicular edges. If the surface is not
161 | anchored, anchored to only two perpendicular edges (a corner), anchored
162 | to only two parallel edges or anchored to all edges, a positive value
163 | will be treated the same as zero.
164 |
165 | A positive zone is the distance from the edge in surface-local
166 | coordinates to consider exclusive.
167 |
168 | Surfaces that do not wish to have an exclusive zone may instead specify
169 | how they should interact with surfaces that do. If set to zero, the
170 | surface indicates that it would like to be moved to avoid occluding
171 | surfaces with a positive exclusive zone. If set to -1, the surface
172 | indicates that it would not like to be moved to accommodate for other
173 | surfaces, and the compositor should extend it all the way to the edges
174 | it is anchored to.
175 |
176 | For example, a panel might set its exclusive zone to 10, so that
177 | maximized shell surfaces are not shown on top of it. A notification
178 | might set its exclusive zone to 0, so that it is moved to avoid
179 | occluding the panel, but shell surfaces are shown underneath it. A
180 | wallpaper or lock screen might set their exclusive zone to -1, so that
181 | they stretch below or over the panel.
182 |
183 | The default value is 0.
184 |
185 | Exclusive zone is double-buffered, see wl_surface.commit.
186 |
187 |
188 |
189 |
190 |
191 |
192 | Requests that the surface be placed some distance away from the anchor
193 | point on the output, in surface-local coordinates. Setting this value
194 | for edges you are not anchored to has no effect.
195 |
196 | The exclusive zone includes the margin.
197 |
198 | Margin is double-buffered, see wl_surface.commit.
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 | Types of keyboard interaction possible for layer shell surfaces. The
209 | rationale for this is twofold: (1) some applications are not interested
210 | in keyboard events and not allowing them to be focused can improve the
211 | desktop experience; (2) some applications will want to take exclusive
212 | keyboard focus.
213 |
214 |
215 |
216 |
217 | This value indicates that this surface is not interested in keyboard
218 | events and the compositor should never assign it the keyboard focus.
219 |
220 | This is the default value, set for newly created layer shell surfaces.
221 |
222 | This is useful for e.g. desktop widgets that display information or
223 | only have interaction with non-keyboard input devices.
224 |
225 |
226 |
227 |
228 | Request exclusive keyboard focus if this surface is above the shell surface layer.
229 |
230 | For the top and overlay layers, the seat will always give
231 | exclusive keyboard focus to the top-most layer which has keyboard
232 | interactivity set to exclusive. If this layer contains multiple
233 | surfaces with keyboard interactivity set to exclusive, the compositor
234 | determines the one receiving keyboard events in an implementation-
235 | defined manner. In this case, no guarantee is made when this surface
236 | will receive keyboard focus (if ever).
237 |
238 | For the bottom and background layers, the compositor is allowed to use
239 | normal focus semantics.
240 |
241 | This setting is mainly intended for applications that need to ensure
242 | they receive all keyboard events, such as a lock screen or a password
243 | prompt.
244 |
245 |
246 |
247 |
248 | This requests the compositor to allow this surface to be focused and
249 | unfocused by the user in an implementation-defined manner. The user
250 | should be able to unfocus this surface even regardless of the layer
251 | it is on.
252 |
253 | Typically, the compositor will want to use its normal mechanism to
254 | manage keyboard focus between layer shell surfaces with this setting
255 | and regular toplevels on the desktop layer (e.g. click to focus).
256 | Nevertheless, it is possible for a compositor to require a special
257 | interaction to focus or unfocus layer shell surfaces (e.g. requiring
258 | a click even if focus follows the mouse normally, or providing a
259 | keybinding to switch focus between layers).
260 |
261 | This setting is mainly intended for desktop shell components (e.g.
262 | panels) that allow keyboard interaction. Using this option can allow
263 | implementing a desktop shell that can be fully usable without the
264 | mouse.
265 |
266 |
267 |
268 |
269 |
270 |
271 | Set how keyboard events are delivered to this surface. By default,
272 | layer shell surfaces do not receive keyboard events; this request can
273 | be used to change this.
274 |
275 | This setting is inherited by child surfaces set by the get_popup
276 | request.
277 |
278 | Layer surfaces receive pointer, touch, and tablet events normally. If
279 | you do not want to receive them, set the input region on your surface
280 | to an empty region.
281 |
282 | Keyboard interactivity is double-buffered, see wl_surface.commit.
283 |
284 |
285 |
286 |
287 |
288 |
289 | This assigns an xdg_popup's parent to this layer_surface. This popup
290 | should have been created via xdg_surface::get_popup with the parent set
291 | to NULL, and this request must be invoked before committing the popup's
292 | initial state.
293 |
294 | See the documentation of xdg_popup for more details about what an
295 | xdg_popup is and how it is used.
296 |
297 |
298 |
299 |
300 |
301 |
302 | When a configure event is received, if a client commits the
303 | surface in response to the configure event, then the client
304 | must make an ack_configure request sometime before the commit
305 | request, passing along the serial of the configure event.
306 |
307 | If the client receives multiple configure events before it
308 | can respond to one, it only has to ack the last configure event.
309 |
310 | A client is not required to commit immediately after sending
311 | an ack_configure request - it may even ack_configure several times
312 | before its next surface commit.
313 |
314 | A client may send multiple ack_configure requests before committing, but
315 | only the last request sent before a commit indicates which configure
316 | event the client really is responding to.
317 |
318 |
319 |
320 |
321 |
322 |
323 | This request destroys the layer surface.
324 |
325 |
326 |
327 |
328 |
329 | The configure event asks the client to resize its surface.
330 |
331 | Clients should arrange their surface for the new states, and then send
332 | an ack_configure request with the serial sent in this configure event at
333 | some point before committing the new surface.
334 |
335 | The client is free to dismiss all but the last configure event it
336 | received.
337 |
338 | The width and height arguments specify the size of the window in
339 | surface-local coordinates.
340 |
341 | The size is a hint, in the sense that the client is free to ignore it if
342 | it doesn't resize, pick a smaller size (to satisfy aspect ratio or
343 | resize in steps of NxM pixels). If the client picks a smaller size and
344 | is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
345 | surface will be centered on this axis.
346 |
347 | If the width or height arguments are zero, it means the client should
348 | decide its own window dimension.
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 | The closed event is sent by the compositor when the surface will no
358 | longer be shown. The output may have been destroyed or the user may
359 | have asked for it to be removed. Further changes to the surface will be
360 | ignored. The client should destroy the resource after receiving this
361 | event, and create a new surface if they so choose.
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 | Change the layer that the surface is rendered on.
384 |
385 | Layer is double-buffered, see wl_surface.commit.
386 |
387 |
388 |
389 |
390 |
391 |
--------------------------------------------------------------------------------
/screenshots/stage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdepx/stage/f2b4a5229bf3862842a69449a1631c8921b3cb19/screenshots/stage.png
--------------------------------------------------------------------------------
/stage.c:
--------------------------------------------------------------------------------
1 | /*-
2 | * Copyright (c) 2022-2024 Ruslan Bukin
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions
7 | * are met:
8 | * 1. Redistributions of source code must retain the above copyright
9 | * notice, this list of conditions and the following disclaimer.
10 | * 2. Redistributions in binary form must reproduce the above copyright
11 | * notice, this list of conditions and the following disclaimer in the
12 | * documentation and/or other materials provided with the distribution.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 | * SUCH DAMAGE.
25 | */
26 |
27 | #include
28 | #include
29 | #include
30 |
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 | #include
38 | #include
39 | #include
40 | #include
41 | #include
42 | #include
43 | #include
44 | #include
45 | #include
46 | #include
47 | #include
48 | #include
49 | #include
50 | #include
51 | #include
52 | #include
53 | #include
54 | #include
55 | #include
56 | #include
57 | #include
58 | #include
59 | #include
60 | #include
61 | #include
62 | #include
63 | #include
64 | #include
65 | #include
66 | #include
67 | #include
68 | #include
69 | #include
70 | #include
71 |
72 | #include
73 | #include
74 | #include
75 | #include
76 | #include
77 |
78 | #define dbg_printf(args...)
79 |
80 | static const float color_focused[] = { 0.8, 0.4, 0.1, 0.1 };
81 | static const float color_default[] = { 0.4, 0.4, 0.4, 0.1 };
82 |
83 | #define SERVER_SOCK_FILE "/tmp/stage.sock"
84 |
85 | enum stage_cursor_mode {
86 | STAGE_CURSOR_PASSTHROUGH,
87 | STAGE_CURSOR_MOVE, /* mod + left mouse button + move */
88 | STAGE_CURSOR_RESIZE, /* mod + right mouse button + move */
89 | STAGE_CURSOR_SCROLL, /* left mouse button + move */
90 | };
91 |
92 | struct stage_server {
93 | struct wl_display *wl_disp;
94 | struct wlr_backend *backend;
95 | struct wlr_renderer *renderer;
96 | struct wlr_allocator *allocator;
97 | struct wlr_scene *scene;
98 | struct wlr_presentation *presentation;
99 | struct wl_listener new_output;
100 | struct wl_listener new_xdg_surface;
101 | struct wl_listener new_xdg_popup;
102 | struct wlr_xdg_shell *xdg_shell;
103 | struct wlr_output_layout *output_layout;
104 | struct wl_list outputs;
105 | struct wlr_scene_output_layout *scene_layout;
106 |
107 | struct wlr_cursor *cursor;
108 | struct wlr_input_device *device;
109 |
110 | struct wlr_xcursor_manager *cursor_mgr;
111 | struct wl_listener cursor_motion;
112 | struct wl_listener cursor_motion_absolute;
113 | struct wl_listener cursor_button;
114 | struct wl_listener cursor_axis;
115 | struct wl_listener cursor_frame;
116 |
117 | struct wlr_seat *seat;
118 | struct wl_listener new_input;
119 | struct wl_listener request_cursor;
120 | struct wl_listener request_set_selection;
121 | struct wl_listener request_set_primary_selection;
122 | struct wl_list keyboards;
123 | enum stage_cursor_mode cursor_mode;
124 |
125 | struct stage_view *grabbed_view;
126 | double grab_x, grab_y;
127 | struct wlr_box grab_geobox;
128 | uint32_t resize_edges;
129 | double cur_saved_x;
130 | double cur_saved_y;
131 |
132 | int current_layout;
133 | int oldws;
134 |
135 | struct wlr_layer_shell_v1 *shell;
136 | struct wl_listener new_layer_shell_surface;
137 |
138 | struct wlr_input_inhibit_manager *inhibit_manager;
139 | struct wlr_session_lock_manager_v1 *lock;
140 | struct wl_listener new_lock;
141 | struct wlr_idle *idle;
142 |
143 | struct wlr_compositor *compositor;
144 | struct wlr_xdg_activation_v1 *activation;
145 | struct wl_listener request_activate;
146 | struct wl_listener layout_change;
147 |
148 | struct wlr_output_manager_v1 *output_manager;
149 | struct wl_listener output_manager_apply;
150 | struct wl_listener output_manager_test;
151 |
152 | bool locked;
153 | };
154 |
155 | struct stage_output {
156 | struct wl_list link;
157 | struct stage_server *server;
158 | struct wlr_output *wlr_output;
159 | struct wl_listener frame;
160 | int curws;
161 | };
162 |
163 | enum stage_view_type {
164 | VIEW_XDG,
165 | VIEW_X11,
166 | VIEW_SLOCK,
167 | };
168 |
169 | struct stage_view {
170 | struct wl_list link;
171 | struct stage_server *server;
172 | struct wlr_xdg_toplevel *xdg_toplevel;
173 | struct wlr_xdg_surface *xdg_surface;
174 | struct wlr_scene_tree *scene_tree;
175 | struct wl_listener map;
176 | struct wl_listener unmap;
177 | struct wl_listener destroy;
178 | struct wl_listener request_move;
179 | struct wl_listener request_resize;
180 | struct wl_listener set_app_id;
181 | struct wl_listener commit;
182 | int x, y, w, h;
183 | int sx, sy, sw, sh; /* saved */
184 | int maxverted;
185 | int maximized;
186 | struct wlr_scene_rect *rect[4]; /* borders */
187 | bool was_focused;
188 |
189 | struct wlr_session_lock_surface_v1 *lock_surface;
190 |
191 | enum stage_view_type type;
192 | bool slot_set;
193 | };
194 |
195 | #define N_SLOTS 5
196 | #define N_WORKSPACES 16
197 |
198 | #ifdef STAGE_DEV
199 | #define STAGE_MODIFIER WLR_MODIFIER_ALT
200 | #else
201 | #define STAGE_MODIFIER WLR_MODIFIER_LOGO
202 | #endif
203 |
204 | static void cursor_focus(struct stage_server *server, uint32_t time);
205 |
206 | static struct terminal_slot {
207 | int x;
208 | int y;
209 | int w;
210 | int h;
211 | int flags;
212 | #define SLOT_NEW_WINDOW (1 << 0)
213 | } slots[N_SLOTS];
214 |
215 | static int nslots = 0;
216 |
217 | static struct stage_workspace {
218 | struct wl_list views;
219 | char name;
220 | } workspaces[N_WORKSPACES];
221 |
222 | static char terminal[] = "foot";
223 | static char ws[] = "ws";
224 | #define TERMINAL_FONT_WIDTH 15
225 |
226 | struct stage_keyboard {
227 | struct wl_list link;
228 | struct stage_server *server;
229 | struct wlr_keyboard *wlr_keyboard;
230 | struct wlr_input_device *device;
231 | struct wl_listener modifiers;
232 | struct wl_listener key;
233 | struct wl_listener destroy;
234 | };
235 |
236 | struct stage_popup {
237 | struct wlr_xdg_popup *xdg_popup;
238 | struct wl_listener commit;
239 | struct wl_listener destroy;
240 | };
241 |
242 | struct stage_lock {
243 | struct wlr_session_lock_v1 *lock;
244 | struct wl_listener new_surface;
245 | struct wl_listener unlock;
246 | struct wl_listener destroy;
247 | struct stage_server *server;
248 | };
249 |
250 | static bool
251 | view_is_slock(struct stage_view *view)
252 | {
253 |
254 | switch (view->type) {
255 | case VIEW_SLOCK:
256 | return (true);
257 | default:
258 | break;
259 | }
260 |
261 | return (false);
262 | }
263 |
264 | static struct stage_output *
265 | output_at(struct stage_server *server, double x, double y)
266 | {
267 | struct wlr_output *o;
268 |
269 | o = wlr_output_layout_output_at(server->output_layout, x, y);
270 | if (o)
271 | return (o->data);
272 |
273 | return (NULL);
274 | }
275 |
276 | static struct stage_output *
277 | cursor_at(struct stage_server *server)
278 | {
279 | struct stage_output *out;
280 |
281 | out = output_at(server, server->cursor->x, server->cursor->y);
282 |
283 | assert(out != NULL);
284 |
285 | return (out);
286 | }
287 |
288 | struct stage_layer_surface {
289 | struct wl_listener map;
290 | struct wl_listener unmap;
291 | struct wl_listener surface_commit;
292 | struct wl_listener output_destroy;
293 | struct wl_listener node_destroy;
294 | struct wl_listener new_popup;
295 |
296 | struct wlr_scene_layer_surface_v1 *scene;
297 | struct wlr_scene_tree *tree;
298 | struct wlr_layer_surface_v1 *layer_surface;
299 | };
300 |
301 | static struct stage_layer_surface *
302 | stage_layer_surface_create(struct wlr_scene_layer_surface_v1 *scene)
303 | {
304 | struct stage_layer_surface *surface;
305 |
306 | surface = calloc(1, sizeof(*surface));
307 |
308 | surface->tree = scene->tree;
309 | surface->scene = scene;
310 | surface->layer_surface = scene->layer_surface;
311 | surface->layer_surface->data = surface;
312 |
313 | return (surface);
314 | }
315 |
316 | /*
317 | * Layer surface.
318 | */
319 |
320 | static void
321 | handle_surface_commit(struct wl_listener *listener, void *data)
322 | {
323 |
324 | };
325 |
326 | static void
327 | handle_map(struct wl_listener *listener, void *data)
328 | {
329 |
330 | }
331 |
332 | void
333 | new_layer_shell_surface(struct wl_listener *listener, void *data)
334 | {
335 | struct wlr_scene_layer_surface_v1 *scene_surface;
336 | struct wlr_layer_surface_v1 *layer_surface;
337 | enum zwlr_layer_shell_v1_layer layer_type;
338 | struct stage_layer_surface *surface;
339 | struct wlr_scene_tree *output_layer;
340 | struct wlr_output *output;
341 | struct stage_server *server;
342 | struct stage_output *out;
343 | struct wlr_box full_area;
344 |
345 | dbg_printf("%s\n", __func__);
346 |
347 | layer_surface = data;
348 |
349 | printf("%s: new layer surface %p, namespace %s layer %d achor %d "
350 | "size %d %d margin %d %d %d %d\n", __func__, layer_surface,
351 | layer_surface->namespace,
352 | layer_surface->pending.layer,
353 | layer_surface->pending.anchor,
354 | layer_surface->pending.desired_width,
355 | layer_surface->pending.desired_height,
356 | layer_surface->pending.margin.top,
357 | layer_surface->pending.margin.right,
358 | layer_surface->pending.margin.bottom,
359 | layer_surface->pending.margin.left);
360 |
361 | server = wl_container_of(listener, server, new_layer_shell_surface);
362 |
363 | out = cursor_at(server);
364 | layer_type = layer_surface->pending.layer;
365 |
366 | layer_surface->output = out->wlr_output;
367 |
368 | wlr_output_effective_resolution(out->wlr_output, &full_area.width,
369 | &full_area.height);
370 | wlr_layer_surface_v1_configure(layer_surface, full_area.width,
371 | full_area.height);
372 |
373 | output_layer = &server->scene->tree;
374 |
375 | scene_surface = wlr_scene_layer_surface_v1_create(output_layer,
376 | layer_surface);
377 |
378 | surface = stage_layer_surface_create(scene_surface);
379 |
380 | printf("%s: new surface %p\n", __func__, surface);
381 |
382 | wlr_fractional_scale_v1_notify_scale(layer_surface->surface,
383 | layer_surface->output->scale);
384 | wlr_surface_set_preferred_buffer_scale(layer_surface->surface,
385 | ceil(layer_surface->output->scale));
386 |
387 | surface->surface_commit.notify = handle_surface_commit;
388 | wl_signal_add(&layer_surface->surface->events.commit,
389 | &surface->surface_commit);
390 |
391 | surface->map.notify = handle_map;
392 | wl_signal_add(&layer_surface->surface->events.map, &surface->map);
393 |
394 | #if 0
395 | /* TODO */
396 |
397 | surface->unmap.notify = handle_unmap;
398 | wl_signal_add(&layer_surface->surface->events.unmap, &surface->unmap);
399 |
400 | surface->new_popup.notify = handle_new_popup;
401 | wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup);
402 |
403 | surface->output_destroy.notify = handle_output_destroy;
404 | wl_signal_add(&output->events.disable, &surface->output_destroy);
405 |
406 | surface->node_destroy.notify = handle_node_destroy;
407 | wl_signal_add(&scene_surface->tree->node.events.destroy,
408 | &surface->node_destroy);
409 | #endif
410 | }
411 |
412 | static void
413 | create_borders(struct stage_view *view)
414 | {
415 | int i;
416 |
417 | for (i = 0; i < 4; i++)
418 | view->rect[i] = wlr_scene_rect_create(view->scene_tree, 0, 0,
419 | color_default);
420 | }
421 |
422 | static void
423 | update_borders(struct stage_view *view)
424 | {
425 | struct wlr_scene_rect *rect;
426 |
427 | /* left */
428 | wlr_scene_node_set_position(&view->rect[0]->node, 0, 0);
429 | wlr_scene_rect_set_size(view->rect[0], 1, view->h);
430 |
431 | /* top */
432 | wlr_scene_node_set_position(&view->rect[1]->node, 0, 0);
433 | wlr_scene_rect_set_size(view->rect[1], view->w, 1);
434 |
435 | /* bottom */
436 | wlr_scene_node_set_position(&view->rect[2]->node, 0, view->h - 1);
437 | wlr_scene_rect_set_size(view->rect[2], view->w, 1);
438 |
439 | /* right */
440 | wlr_scene_node_set_position(&view->rect[3]->node, view->w - 1, 0);
441 | wlr_scene_rect_set_size(view->rect[3], 1, view->h);
442 |
443 | wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y);
444 | wlr_xdg_toplevel_set_size(view->xdg_toplevel, view->w, view->h);
445 | }
446 |
447 | static void
448 | view_geometry(struct stage_view *view, struct wlr_box *geom)
449 | {
450 |
451 | if (view->type == VIEW_SLOCK) {
452 | geom->x = 0;
453 | geom->y = 0;
454 | //geom->width = 100;
455 | //geom->height = 100;
456 | } else
457 | geom = &view->xdg_toplevel->base->geometry;
458 | }
459 |
460 | static void
461 | view_set_borders_active(struct stage_view *view, bool active)
462 | {
463 | const float *color;
464 | int i;
465 |
466 | if (active)
467 | color = color_focused;
468 | else
469 | color = color_default;
470 |
471 | for (i = 0; i < 4; i++)
472 | wlr_scene_rect_set_color(view->rect[i], color);
473 | }
474 |
475 | static struct wlr_surface *
476 | view_surface(struct stage_view *view)
477 | {
478 | struct wlr_surface *surface;
479 |
480 | surface = NULL;
481 |
482 | switch (view->type) {
483 | case VIEW_XDG:
484 | surface = view->xdg_toplevel->base->surface;
485 | break;
486 | case VIEW_SLOCK:
487 | surface = view->lock_surface->surface;
488 | break;
489 | default:
490 | break;
491 | }
492 |
493 | return (surface);
494 | }
495 |
496 | static void
497 | focus_view(struct stage_view *view, struct wlr_surface *surface)
498 | {
499 | struct wlr_surface *prev_surface;
500 | struct wlr_xdg_surface *previous;
501 | struct wlr_scene_node *scene_node;
502 | struct stage_server *server;
503 | struct wlr_keyboard *kb;
504 | struct wlr_seat *seat;
505 | struct stage_view *prev_view;
506 |
507 | if (view == NULL)
508 | return;
509 |
510 | server = view->server;
511 | seat = server->seat;
512 |
513 | kb = wlr_seat_get_keyboard(seat);
514 |
515 | if (view_is_slock(view)) {
516 | wlr_seat_keyboard_notify_enter(seat, view_surface(view),
517 | kb->keycodes, kb->num_keycodes, &kb->modifiers);
518 | return;
519 | }
520 |
521 | prev_surface = seat->keyboard_state.focused_surface;
522 | if (prev_surface == surface)
523 | return;
524 |
525 | if (prev_surface) {
526 | previous =
527 | wlr_xdg_surface_try_from_wlr_surface(prev_surface);
528 | if (previous) {
529 | assert(previous->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL);
530 | if (previous->toplevel != NULL)
531 | wlr_xdg_toplevel_set_activated(
532 | previous->toplevel, false);
533 | scene_node = previous->data;
534 | prev_view = scene_node->data;
535 | view_set_borders_active(prev_view, false);
536 | }
537 | }
538 |
539 | wlr_xdg_toplevel_set_activated(view->xdg_toplevel, true);
540 |
541 | wlr_seat_keyboard_notify_enter(seat, view_surface(view),
542 | kb->keycodes, kb->num_keycodes, &kb->modifiers);
543 | view_set_borders_active(view, true);
544 | }
545 |
546 | static struct stage_view *
547 | desktop_view_at(struct stage_server *server, double lx, double ly,
548 | struct wlr_surface **surface, double *sx, double *sy)
549 | {
550 | struct wlr_scene_surface *scene_surface;
551 | struct wlr_scene_buffer *scene_buffer;
552 | struct wlr_scene_node *node;
553 | struct wlr_scene_tree *tree;
554 | struct stage_view *view;
555 | struct stage_output *out;
556 | struct stage_workspace *ws;
557 | const struct wlr_box *box;
558 | int i;
559 |
560 | #if 0
561 | out = output_at(server, lx, ly);
562 |
563 | ws = &workspaces[out->curws];
564 | wl_list_for_each(view, &ws->views, link) {
565 | box = &view->xdg_toplevel->base->current.geometry;
566 | if (wlr_box_contains_point(box, lx, ly))
567 | return (view);
568 | }
569 |
570 | return (NULL);
571 | #endif
572 |
573 | #if 1
574 | node = wlr_scene_node_at(&server->scene->tree.node, lx, ly, sx, sy);
575 | if (node == NULL)
576 | return (NULL);
577 |
578 | if (surface && node->type == WLR_SCENE_NODE_BUFFER) {
579 | scene_buffer = wlr_scene_buffer_from_node(node);
580 | scene_surface = wlr_scene_surface_try_from_buffer(scene_buffer);
581 | if (scene_surface == NULL)
582 | return (NULL);
583 |
584 | *surface = scene_surface->surface;
585 | }
586 |
587 | tree = node->parent;
588 |
589 | while (tree != NULL && tree->node.data == NULL)
590 | tree = tree->node.parent;
591 |
592 | if (tree == NULL)
593 | return (NULL);
594 |
595 | view = tree->node.data;
596 | #endif
597 |
598 | return (view);
599 | }
600 |
601 | static const char *
602 | get_app_id(struct stage_view *view)
603 | {
604 | const char *res;
605 |
606 | res = view->xdg_toplevel->app_id;
607 |
608 | return (res);
609 | }
610 |
611 | static void
612 | view_set_slot(struct stage_view *view)
613 | {
614 | struct terminal_slot *slot;
615 | struct stage_view *v;
616 | uint32_t h, w, tw;
617 | const char *app_id;
618 | int i;
619 |
620 | app_id = get_app_id(view);
621 | if (!app_id)
622 | return;
623 |
624 | v = NULL;
625 |
626 | dbg_printf("app id %s\n", app_id);
627 |
628 | if (strcmp(app_id, "foot") == 0 ||
629 | strcmp(app_id, "XTerm") == 0 ||
630 | strcmp(app_id, "URxvt") == 0) {
631 | for (i = 0; i < nslots; i++) {
632 | slot = &slots[i];
633 | if ((slot->flags & SLOT_NEW_WINDOW) == 0)
634 | continue;
635 |
636 | v = desktop_view_at(view->server, slot->x, slot->y,
637 | NULL, NULL, NULL);
638 | if (v != NULL)
639 | continue;
640 |
641 | view->x = slot->x;
642 | view->y = slot->y;
643 | view->w = slot->w;
644 | view->h = slot->h;
645 |
646 | view->slot_set = true;
647 | break;
648 | }
649 | }
650 | }
651 |
652 | static void
653 | view_align(struct stage_view *view)
654 | {
655 | struct wlr_box geom;
656 | struct stage_output *out;
657 | struct wlr_output *output;
658 |
659 | out = cursor_at(view->server);
660 | output = out->wlr_output;
661 |
662 | geom = view->xdg_toplevel->base->geometry;
663 |
664 | printf("%s: view geoms %d %d %d %d\n", __func__, geom.x, geom.y,
665 | geom.width, geom.height);
666 |
667 | view->w = geom.width;
668 | view->h = geom.height;
669 | view->x = (output->width - geom.width) / 2;
670 | view->y = (output->height - geom.height) / 2;
671 | }
672 |
673 | static void
674 | xdg_toplevel_map(struct wl_listener *listener, void *data)
675 | {
676 | struct stage_workspace *curws;
677 | struct stage_output *out;
678 | struct stage_view *view;
679 | struct wlr_box geom;
680 |
681 | view = wl_container_of(listener, view, map);
682 |
683 | out = cursor_at(view->server);
684 | curws = &workspaces[out->curws];
685 | wl_list_insert(&curws->views, &view->link);
686 |
687 | #if 0
688 | enum wlr_edges edges = WLR_EDGE_NONE;
689 | edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | WLR_EDGE_BOTTOM;
690 | wlr_xdg_toplevel_set_tiled(view->xdg_toplevel, edges);
691 | #endif
692 |
693 | if (view->slot_set == false)
694 | view_align(view);
695 |
696 | view_geometry(view, &geom);
697 | view->w = geom.width;
698 | view->h = geom.height;
699 |
700 | if (view->type == VIEW_SLOCK)
701 | return;
702 |
703 | update_borders(view);
704 | focus_view(view, view_surface(view));
705 | }
706 |
707 | void
708 | slock_destroy_view(struct wl_listener *listener, void *data)
709 | {
710 | struct wlr_session_lock_surface_v1 *lock_surface;
711 | struct stage_view *view;
712 |
713 | view = wl_container_of(listener, view, destroy);
714 |
715 | printf("%s\n", __func__);
716 |
717 | lock_surface = view->lock_surface;
718 |
719 | wl_list_remove(&view->map.link);
720 | wl_list_remove(&view->destroy.link);
721 | }
722 |
723 | static void
724 | slock_map_view(struct wl_listener *listener, void *data)
725 | {
726 | struct wlr_session_lock_surface_v1 *lock_surface;
727 | struct stage_view *view;
728 |
729 | printf("%s\n", __func__);
730 |
731 | view = wl_container_of(listener, view, map);
732 |
733 | lock_surface = view->lock_surface;
734 | }
735 |
736 | void
737 | slock_new_surface(struct wl_listener *listener, void *data)
738 | {
739 | struct wlr_session_lock_surface_v1 *lock_surface;
740 | struct stage_server *server;
741 | struct stage_lock *slock;
742 | struct stage_view *view;
743 | struct wlr_scene_surface *surface;
744 | struct wlr_session_lock_v1 *lock;
745 |
746 | printf("%s\n", __func__);
747 |
748 | lock_surface = data;
749 | slock = wl_container_of(listener, slock, new_surface);
750 | server = slock->server;
751 | lock = slock->lock;
752 |
753 | view = malloc(sizeof(struct stage_view));
754 | memset(view, 0, sizeof(struct stage_view));
755 | view->type = VIEW_SLOCK;
756 | view->server = server;
757 | view->lock_surface = lock_surface;
758 |
759 | view->map.notify = slock_map_view;
760 | wl_signal_add(&lock_surface->surface->events.map, &view->map);
761 | view->destroy.notify = slock_destroy_view;
762 | wl_signal_add(&lock_surface->events.destroy, &view->destroy);
763 |
764 | /* TODO: destroy surface when not needed? */
765 | surface = wlr_scene_surface_create(&server->scene->tree,
766 | lock_surface->surface);
767 | view->scene_tree = NULL;
768 | view->lock_surface->data = surface;
769 |
770 | struct wlr_output *output;
771 | struct stage_output *out;
772 | out = cursor_at(server);
773 | output = out->wlr_output;
774 | view->w = output->width;
775 | view->h = output->height;
776 | wlr_session_lock_surface_v1_configure(lock_surface, view->w, view->h);
777 |
778 | focus_view(view, view_surface(view));
779 | }
780 |
781 | void
782 | slock_unlock(struct wl_listener *listener, void *data)
783 | {
784 | struct stage_server *server;
785 | struct stage_lock *slock;
786 |
787 | slock = wl_container_of(listener, slock, unlock);
788 |
789 | printf("%s\n", __func__);
790 |
791 | server = slock->server;
792 | server->locked = false;
793 | }
794 |
795 | void
796 | slock_destroy(struct wl_listener *listener, void *data)
797 | {
798 | struct stage_lock *slock;
799 | struct wlr_session_lock_v1 *lock;
800 |
801 | slock = wl_container_of(listener, slock, destroy);
802 | lock = slock->lock;
803 |
804 | printf("%s\n", __func__);
805 |
806 | wl_list_remove(&slock->new_surface.link);
807 | wl_list_remove(&slock->unlock.link);
808 | wl_list_remove(&slock->destroy.link);
809 | }
810 |
811 | void
812 | new_lock(struct wl_listener *listener, void *data)
813 | {
814 | struct wlr_session_lock_v1 *lock;
815 | struct stage_lock *slock;
816 | struct stage_server *server;
817 |
818 | server = wl_container_of(listener, server, new_lock);
819 |
820 | lock = data;
821 |
822 | slock = malloc(sizeof(struct stage_lock));
823 | slock->lock = lock;
824 | slock->server = server;
825 | //lock->data = slock;
826 |
827 | slock->new_surface.notify = slock_new_surface;
828 | wl_signal_add(&lock->events.new_surface, &slock->new_surface);
829 |
830 | slock->unlock.notify = slock_unlock;
831 | wl_signal_add(&lock->events.unlock, &slock->unlock);
832 |
833 | slock->destroy.notify = slock_destroy;
834 | wl_signal_add(&lock->events.destroy, &slock->destroy);
835 |
836 | printf("%s\n", __func__);
837 | wlr_session_lock_v1_send_locked(lock);
838 | server->locked = true;
839 | }
840 |
841 | void
842 | request_activate(struct wl_listener *listener, void *data)
843 | {
844 |
845 | printf("%s\n", __func__);
846 | }
847 |
848 | void
849 | layout_change(struct wl_listener *listener, void *data)
850 | {
851 |
852 | printf("%s\n", __func__);
853 | }
854 |
855 | void
856 | output_manager_apply(struct wl_listener *listener, void *data)
857 | {
858 |
859 | printf("%s\n", __func__);
860 | }
861 |
862 | void
863 | output_manager_test(struct wl_listener *listener, void *data)
864 | {
865 |
866 | printf("%s\n", __func__);
867 | }
868 |
869 | static struct stage_view *
870 | view_from_surface(struct stage_server *server, struct wlr_surface *surface)
871 | {
872 | struct wlr_xdg_surface *xdg_surface;
873 | struct wlr_scene_node *scene_node;
874 | struct stage_view *view;
875 |
876 | xdg_surface = wlr_xdg_surface_try_from_wlr_surface(surface);
877 | assert(xdg_surface != NULL);
878 |
879 | scene_node = xdg_surface->data;
880 |
881 | view = scene_node->data;
882 |
883 | assert(view != NULL);
884 |
885 | return (view);
886 | }
887 |
888 | static void
889 | socket_send(char *str)
890 | {
891 | struct sockaddr_un addr;
892 | char send_msg[16];
893 | int fd;
894 |
895 | if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
896 | return;
897 |
898 | memset(&addr, 0, sizeof(addr));
899 | addr.sun_family = AF_UNIX;
900 | strcpy(addr.sun_path, SERVER_SOCK_FILE);
901 |
902 | sprintf(send_msg, "%s", str);
903 |
904 | sendto(fd, send_msg, strlen(send_msg) + 1, 0, (struct sockaddr *)&addr,
905 | sizeof(struct sockaddr_un));
906 |
907 | close(fd);
908 | }
909 |
910 | static void
911 | notify_ws_change(int oldws, int newws)
912 | {
913 | struct stage_workspace *ws;
914 | char str[32];
915 | char *cur;
916 | int error;
917 | int count;
918 | int i;
919 |
920 | memset(str, 0, 32);
921 |
922 | str[0] = 'W';
923 |
924 | cur = str + 1;
925 |
926 | /* Start from workspace 1. End with workspace 0. */
927 | i = 1;
928 | do {
929 | if (i == N_WORKSPACES)
930 | i = 0;
931 |
932 | ws = &workspaces[i];
933 | if (i == newws) {
934 | snprintf(cur, 3, "!%c", ws->name);
935 | cur += 2;
936 | }
937 | #if 1
938 | else if (!wl_list_empty(&ws->views)) {
939 | if (i == oldws) {
940 | snprintf(cur, 3, "?%c", ws->name);
941 | cur += 2;
942 | } else {
943 | snprintf(cur, 2, "%c", ws->name);
944 | cur += 1;
945 | }
946 | }
947 | #endif
948 |
949 | } while (i++);
950 |
951 | socket_send(str);
952 | }
953 |
954 | static void
955 | changeworkspace(struct stage_server *server, int newws)
956 | {
957 | struct stage_workspace *ws;
958 | struct stage_view *view, *tmpview;
959 | struct stage_view *focused_view;
960 | struct stage_output *out;
961 | struct wlr_surface *surface;
962 | struct wlr_seat *seat;
963 | int oldws;
964 |
965 | out = cursor_at(server);
966 | if (out->curws == newws)
967 | return;
968 |
969 | oldws = out->curws;
970 | server->oldws = oldws;
971 | out->curws = newws;
972 |
973 | focused_view = NULL;
974 | seat = server->seat;
975 | surface = seat->keyboard_state.focused_surface;
976 | if (surface)
977 | focused_view = view_from_surface(server, surface);
978 |
979 | dbg_printf("%s: ws %d -> %d\n", __func__, oldws, newws);
980 |
981 | ws = &workspaces[oldws];
982 | wl_list_for_each_safe(view, tmpview, &ws->views, link) {
983 | wlr_scene_node_set_enabled(&view->scene_tree->node, false);
984 | if (view == focused_view)
985 | view->was_focused = true;
986 | else
987 | view->was_focused = false;
988 | }
989 |
990 | ws = &workspaces[newws];
991 | wl_list_for_each_safe(view, tmpview, &ws->views, link) {
992 | wlr_scene_node_set_enabled(&view->scene_tree->node, true);
993 | if (view->was_focused)
994 | focus_view(view, view_surface(view));
995 | }
996 |
997 | cursor_focus(server, 0);
998 | notify_ws_change(oldws, newws);
999 | }
1000 |
1001 | static void
1002 | output_frame(struct wl_listener *listener, void *data)
1003 | {
1004 | struct wlr_scene_output *scene_output;
1005 | struct wlr_scene *scene;
1006 | struct stage_output *output;
1007 | struct timespec now;
1008 |
1009 | dbg_printf("%s\n", __func__);
1010 |
1011 | output = wl_container_of(listener, output, frame);
1012 |
1013 | scene = output->server->scene;
1014 | scene_output = wlr_scene_get_scene_output(scene, output->wlr_output);
1015 | wlr_scene_output_commit(scene_output, NULL);
1016 |
1017 | clock_gettime(CLOCK_MONOTONIC, &now);
1018 | wlr_scene_output_send_frame_done(scene_output, &now);
1019 | }
1020 |
1021 | static void
1022 | set_layout(struct stage_server *server)
1023 | {
1024 | struct xkb_context *context;
1025 | struct xkb_rule_names rules;
1026 | struct xkb_keymap *keymap;
1027 | struct wlr_keyboard *kbd;
1028 |
1029 | memset(&rules, 0, sizeof(struct xkb_rule_names));
1030 | rules.layout = "us,ru";
1031 |
1032 | context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
1033 | kbd = wlr_seat_get_keyboard(server->seat);
1034 |
1035 | keymap = xkb_keymap_new_from_names(context, &rules,
1036 | XKB_KEYMAP_COMPILE_NO_FLAGS);
1037 |
1038 | wlr_keyboard_set_keymap(kbd, keymap);
1039 | xkb_context_unref(context);
1040 | }
1041 |
1042 | static int
1043 | num_layouts(struct stage_server *server)
1044 | {
1045 | xkb_layout_index_t num_layouts;
1046 | struct wlr_keyboard *kbd;
1047 |
1048 | kbd = wlr_seat_get_keyboard(server->seat);
1049 |
1050 | num_layouts = xkb_keymap_num_layouts(kbd->keymap);
1051 |
1052 | return (num_layouts);
1053 | }
1054 |
1055 | static xkb_layout_index_t
1056 | get_current_layout_index(struct stage_server *server)
1057 | {
1058 | xkb_layout_index_t num_layouts;
1059 | xkb_layout_index_t layout_idx;
1060 | struct wlr_keyboard *kbd;
1061 |
1062 | kbd = wlr_seat_get_keyboard(server->seat);
1063 |
1064 | num_layouts = xkb_keymap_num_layouts(kbd->keymap);
1065 |
1066 | for (layout_idx = 0; layout_idx < num_layouts; layout_idx ++) {
1067 | if (xkb_state_layout_index_is_active(kbd->xkb_state,
1068 | layout_idx, XKB_STATE_LAYOUT_EFFECTIVE))
1069 | break;
1070 | }
1071 |
1072 | return (layout_idx);
1073 | }
1074 |
1075 | static void
1076 | switch_layout(struct stage_server *server, int layout)
1077 | {
1078 | struct wlr_keyboard *kbd;
1079 | xkb_layout_index_t idx;
1080 |
1081 | kbd = wlr_seat_get_keyboard(server->seat);
1082 |
1083 | idx = get_current_layout_index(server);
1084 |
1085 | wlr_keyboard_notify_modifiers(kbd, kbd->modifiers.depressed,
1086 | kbd->modifiers.latched, kbd->modifiers.locked, layout);
1087 |
1088 | server->current_layout = layout;
1089 | }
1090 |
1091 | static void
1092 | unmaxvert(struct stage_view *view)
1093 | {
1094 |
1095 | view->maxverted = false;
1096 |
1097 | view->x = view->sx;
1098 | view->y = view->sy;
1099 | view->w = view->sw;
1100 | view->h = view->sh;
1101 |
1102 | update_borders(view);
1103 | }
1104 |
1105 | static void
1106 | maxvert(struct stage_server *server)
1107 | {
1108 | struct wlr_surface *surface;
1109 | struct wlr_output *output;
1110 | struct stage_output *out;
1111 | struct stage_view *view;
1112 | struct wlr_seat *seat;
1113 |
1114 | seat = server->seat;
1115 |
1116 | surface = seat->keyboard_state.focused_surface;
1117 | if (!surface)
1118 | return;
1119 |
1120 | view = view_from_surface(server, surface);
1121 | if (view->maxverted) {
1122 | unmaxvert(view);
1123 | return;
1124 | }
1125 |
1126 | view->maxverted = true;
1127 |
1128 | view->sx = view->x;
1129 | view->sy = view->y;
1130 | view->sw = view->w;
1131 | view->sh = view->h;
1132 |
1133 | out = output_at(server, view->x, view->y);
1134 | output = out->wlr_output;
1135 |
1136 | view->y = 0;
1137 | view->h = output->height;
1138 |
1139 | update_borders(view);
1140 | wlr_scene_node_raise_to_top(&view->scene_tree->node);
1141 | }
1142 |
1143 | static void
1144 | unmaximize(struct stage_view *view)
1145 | {
1146 |
1147 | view->maximized = false;
1148 |
1149 | view->x = view->sx;
1150 | view->y = view->sy;
1151 | view->w = view->sw;
1152 | view->h = view->sh;
1153 |
1154 | update_borders(view);
1155 | }
1156 |
1157 | static void
1158 | maximize(struct stage_server *server)
1159 | {
1160 | struct wlr_surface *surface;
1161 | struct wlr_output *output;
1162 | struct stage_output *out;
1163 | struct stage_view *view;
1164 | struct wlr_seat *seat;
1165 |
1166 | seat = server->seat;
1167 |
1168 | surface = seat->keyboard_state.focused_surface;
1169 | if (!surface)
1170 | return;
1171 |
1172 | view = view_from_surface(server, surface);
1173 | if (view->maximized) {
1174 | unmaximize(view);
1175 | return;
1176 | }
1177 |
1178 | view->maximized = true;
1179 |
1180 | view->sx = view->x;
1181 | view->sy = view->y;
1182 | view->sw = view->w;
1183 | view->sh = view->h;
1184 |
1185 | out = output_at(server, view->x, view->y);
1186 | output = out->wlr_output;
1187 |
1188 | view->x = 0;
1189 | view->y = 0;
1190 | view->w = output->width;
1191 | view->h = output->height;
1192 |
1193 | update_borders(view);
1194 | wlr_scene_node_raise_to_top(&view->scene_tree->node);
1195 | }
1196 |
1197 | static bool
1198 | handle_keybinding2(struct stage_server *server,
1199 | struct wlr_keyboard_key_event *event, xkb_keysym_t sym)
1200 | {
1201 |
1202 | switch (sym) {
1203 | case XKB_KEY_Alt_R:
1204 | if (server->current_layout == 0)
1205 | switch_layout(server, 1);
1206 | else
1207 | switch_layout(server, 0);
1208 | break;
1209 | case XKB_KEY_Super_R:
1210 | break;
1211 | case WLR_MODIFIER_ALT:
1212 | break;
1213 | default:
1214 | return (false);
1215 | }
1216 |
1217 | return (true);
1218 | }
1219 |
1220 | static void
1221 | switch_light(char *arg)
1222 | {
1223 | struct stat st;
1224 | char *p;
1225 | int pid;
1226 |
1227 | p = "/usr/local/bin/python3.10";
1228 |
1229 | if (stat(p, &st) == -1)
1230 | return;
1231 |
1232 | pid = fork();
1233 | if (pid == 0)
1234 | execl(p, p, "/home/br/lights/test_client.py", arg, NULL);
1235 | }
1236 |
1237 | static bool
1238 | handle_keybinding(struct stage_server *server,
1239 | struct wlr_keyboard_key_event *event, xkb_keysym_t sym)
1240 | {
1241 |
1242 | if (server->locked)
1243 | return (false);
1244 |
1245 | switch (sym) {
1246 | case XKB_KEY_0:
1247 | changeworkspace(server, 0);
1248 | break;
1249 | case XKB_KEY_1:
1250 | changeworkspace(server, 1);
1251 | break;
1252 | case XKB_KEY_2:
1253 | changeworkspace(server, 2);
1254 | break;
1255 | case XKB_KEY_3:
1256 | changeworkspace(server, 3);
1257 | break;
1258 | case XKB_KEY_4:
1259 | changeworkspace(server, 4);
1260 | break;
1261 | case XKB_KEY_5:
1262 | changeworkspace(server, 5);
1263 | break;
1264 | case XKB_KEY_6:
1265 | changeworkspace(server, 6);
1266 | break;
1267 | case XKB_KEY_7:
1268 | changeworkspace(server, 7);
1269 | break;
1270 | case XKB_KEY_8:
1271 | changeworkspace(server, 8);
1272 | break;
1273 | case XKB_KEY_9:
1274 | changeworkspace(server, 9);
1275 | break;
1276 | case XKB_KEY_minus:
1277 | changeworkspace(server, 10);
1278 | break;
1279 | case XKB_KEY_equal:
1280 | changeworkspace(server, 11);
1281 | break;
1282 | case XKB_KEY_backslash:
1283 | changeworkspace(server, 12);
1284 | break;
1285 | case XKB_KEY_grave:
1286 | changeworkspace(server, 13);
1287 | break;
1288 | case XKB_KEY_t:
1289 | changeworkspace(server, 14);
1290 | break;
1291 | case XKB_KEY_s:
1292 | changeworkspace(server, 15);
1293 | break;
1294 | case XKB_KEY_Escape:
1295 | changeworkspace(server, server->oldws);
1296 | break;
1297 | case XKB_KEY_m:
1298 | maxvert(server);
1299 | break;
1300 | case XKB_KEY_f:
1301 | maximize(server);
1302 | break;
1303 | case XKB_KEY_q:
1304 | switch_light("0");
1305 | break;
1306 | case XKB_KEY_w:
1307 | switch_light("1");
1308 | break;
1309 | case XKB_KEY_e:
1310 | switch_light("2");
1311 | break;
1312 | case XKB_KEY_r:
1313 | switch_light("3");
1314 | break;
1315 | case XKB_KEY_Return:
1316 | if (fork() == 0)
1317 | execl("/bin/sh", "/bin/sh", "-c", terminal, NULL);
1318 | break;
1319 | default:
1320 | return (false);
1321 | };
1322 |
1323 | return (true);
1324 | }
1325 |
1326 | static void
1327 | keyboard_handle_key(struct wl_listener *listener, void *data)
1328 | {
1329 | struct stage_server *server;
1330 | struct wlr_keyboard_key_event *event;
1331 | struct stage_keyboard *keyboard;
1332 | struct wlr_keyboard *kb;
1333 | const xkb_keysym_t *syms;
1334 | xkb_keysym_t sym;
1335 | uint32_t keycode;
1336 | uint32_t mods;
1337 | bool handled;
1338 | int nsyms;
1339 |
1340 | keyboard = wl_container_of(listener, keyboard, key);
1341 | server = keyboard->server;
1342 | event = data;
1343 |
1344 | kb = wlr_seat_get_keyboard(server->seat);
1345 |
1346 | keycode = event->keycode + 8;
1347 | nsyms = xkb_state_key_get_syms(kb->xkb_state, keycode, &syms);
1348 |
1349 | assert(nsyms > 0);
1350 |
1351 | /* TODO: Handle first sym only. */
1352 | sym = syms[0];
1353 |
1354 | handled = false;
1355 |
1356 | mods = wlr_keyboard_get_modifiers(kb);
1357 | if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
1358 | dbg_printf("%s: nsyms %d mods %x\n", __func__, nsyms, mods);
1359 | if (mods == WLR_MODIFIER_CTRL) {
1360 | /* Ignore key binding. */
1361 | if (sym == XKB_KEY_Return)
1362 | handled = true;
1363 | } else if (mods == STAGE_MODIFIER)
1364 | handled = handle_keybinding(server, event, sym);
1365 | else
1366 | handled = handle_keybinding2(server, event, sym);
1367 |
1368 | if (sym == XKB_KEY_Print) {
1369 | if (fork() == 0)
1370 | execl("/bin/sh", "/bin/sh", "-c",
1371 | "/usr/local/bin/slurp", "|", "/usr/local/bin/grim", "-g",
1372 | "-", "-", "|", "/usr/local/bin/wl-copy", NULL);
1373 | handled = true;
1374 | }
1375 | }
1376 |
1377 | dbg_printf("sym %x\n", sym);
1378 |
1379 | if (!handled) {
1380 | wlr_seat_set_keyboard(server->seat, kb);
1381 | wlr_seat_keyboard_notify_key(server->seat, event->time_msec,
1382 | event->keycode, event->state);
1383 | }
1384 | }
1385 |
1386 | static void
1387 | keyboard_handle_destroy(struct wl_listener *listener, void *data)
1388 | {
1389 |
1390 | }
1391 |
1392 | static void
1393 | keyboard_handle_modifiers(struct wl_listener *listener, void *data)
1394 | {
1395 | struct stage_keyboard *keyboard;
1396 |
1397 | keyboard = wl_container_of(listener, keyboard, modifiers);
1398 | wlr_seat_set_keyboard(keyboard->server->seat, keyboard->wlr_keyboard);
1399 | wlr_seat_keyboard_notify_modifiers(keyboard->server->seat,
1400 | &keyboard->wlr_keyboard->modifiers);
1401 | }
1402 |
1403 | static void
1404 | server_new_keyboard(struct stage_server *server,
1405 | struct wlr_input_device *device)
1406 | {
1407 | struct stage_keyboard *keyboard;
1408 | struct wlr_keyboard *kb;
1409 | struct xkb_context *context;
1410 | struct xkb_keymap *keymap;
1411 |
1412 | printf("%s\n", __func__);
1413 |
1414 | keyboard = malloc(sizeof(struct stage_keyboard));
1415 | keyboard->server = server;
1416 | keyboard->device = device;
1417 | kb = wlr_keyboard_from_input_device(device);
1418 | keyboard->wlr_keyboard = kb;
1419 |
1420 | context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
1421 |
1422 | struct xkb_rule_names rules;
1423 | memset(&rules, 0, sizeof(struct xkb_rule_names));
1424 | rules.layout = "us,ru";
1425 |
1426 | keymap = xkb_keymap_new_from_names(context, &rules,
1427 | XKB_KEYMAP_COMPILE_NO_FLAGS);
1428 |
1429 | wlr_keyboard_set_keymap(kb, keymap);
1430 | xkb_keymap_unref(keymap);
1431 | xkb_context_unref(context);
1432 | wlr_keyboard_set_repeat_info(kb, 25, 600);
1433 |
1434 | keyboard->modifiers.notify = keyboard_handle_modifiers;
1435 | wl_signal_add(&kb->events.modifiers, &keyboard->modifiers);
1436 |
1437 | keyboard->key.notify = keyboard_handle_key;
1438 | wl_signal_add(&kb->events.key, &keyboard->key);
1439 | keyboard->destroy.notify = keyboard_handle_destroy;
1440 | wl_signal_add(&device->events.destroy, &keyboard->destroy);
1441 |
1442 | wlr_seat_set_keyboard(server->seat, kb);
1443 |
1444 | wl_list_insert(&server->keyboards, &keyboard->link);
1445 |
1446 | set_layout(server);
1447 | }
1448 |
1449 | static void
1450 | server_new_pointer(struct stage_server *server,
1451 | struct wlr_input_device *device)
1452 | {
1453 |
1454 | wlr_cursor_attach_input_device(server->cursor, device);
1455 | server->device = device;
1456 | }
1457 |
1458 | static void
1459 | server_new_input(struct wl_listener *listener, void *data)
1460 | {
1461 | struct wlr_input_device *device;
1462 | struct stage_server *server;
1463 | uint32_t caps;
1464 |
1465 | server = wl_container_of(listener, server, new_input);
1466 |
1467 | device = data;
1468 |
1469 | switch (device->type) {
1470 | case WLR_INPUT_DEVICE_KEYBOARD:
1471 | server_new_keyboard(server, device);
1472 | break;
1473 | case WLR_INPUT_DEVICE_POINTER:
1474 | server_new_pointer(server, device);
1475 | break;
1476 | case WLR_INPUT_DEVICE_TOUCH:
1477 | break;
1478 | case WLR_INPUT_DEVICE_TABLET_PAD:
1479 | break;
1480 | case WLR_INPUT_DEVICE_SWITCH:
1481 | break;
1482 | default:
1483 | break;
1484 | }
1485 |
1486 | caps = WL_SEAT_CAPABILITY_POINTER;
1487 | if (!wl_list_empty(&server->keyboards))
1488 | caps |= WL_SEAT_CAPABILITY_KEYBOARD;
1489 | wlr_seat_set_capabilities(server->seat, caps);
1490 | }
1491 |
1492 | static void
1493 | init_slots(struct wlr_output *wlr_output)
1494 | {
1495 | int i, w, h, tw;
1496 |
1497 | w = wlr_output->width;
1498 | h = wlr_output->height;
1499 |
1500 | tw = TERMINAL_FONT_WIDTH * 80 + 4;
1501 |
1502 | for (i = 0; i < N_SLOTS; i++) {
1503 | slots[i].w = tw > (w / 2) ? (w / 2) : tw;
1504 | slots[i].h = h / 2;
1505 | slots[i].flags = SLOT_NEW_WINDOW;
1506 | }
1507 |
1508 | slots[0].x = w / 2;
1509 | slots[0].y = h / 2;
1510 |
1511 | slots[1].x = tw > (w / 2) ? 0 : (w / 2) - tw;
1512 | slots[1].y = h / 2;
1513 |
1514 | slots[2].x = w / 2;
1515 | slots[2].y = 0;
1516 |
1517 | slots[3].x = tw > (w / 2) ? 0 : (w / 2) - tw;
1518 | slots[3].y = 0;
1519 |
1520 | slots[4].x = 0;
1521 | slots[4].y = 0;
1522 | slots[4].w = w;
1523 | slots[4].h = h;
1524 | slots[4].flags = 0;
1525 |
1526 | nslots += 5;
1527 | }
1528 |
1529 | static void
1530 | server_new_output(struct wl_listener *listener, void *data)
1531 | {
1532 | struct wlr_output *wlr_output;
1533 | struct wlr_output_layout_output *l_output;
1534 | struct wlr_output_state state;
1535 | struct wlr_output_mode *mode;
1536 | struct wlr_scene_output *scene_output;
1537 | struct stage_output *output;
1538 | struct stage_server *server;
1539 | bool found;
1540 |
1541 | printf("%s\n", __func__);
1542 |
1543 | server = wl_container_of(listener, server, new_output);
1544 |
1545 | wlr_output = data;
1546 | wlr_output_init_render(wlr_output, server->allocator, server->renderer);
1547 |
1548 | wlr_output_state_init(&state);
1549 | wlr_output_state_set_enabled(&state, true);
1550 |
1551 | mode = NULL;
1552 | found = false;
1553 | wl_list_for_each(mode, &wlr_output->modes, link) {
1554 | printf("found mode %dx%d\n", mode->width, mode->height);
1555 | #if 0
1556 | if (mode->width == 1920 && mode->height == 1080) {
1557 | found = true;
1558 | break;
1559 | }
1560 | #endif
1561 | }
1562 |
1563 | if (found == false)
1564 | mode = wlr_output_preferred_mode(wlr_output);
1565 | if (mode != NULL)
1566 | wlr_output_state_set_mode(&state, mode);
1567 |
1568 | /* Atomically applies the new output state. */
1569 | wlr_output_commit_state(wlr_output, &state);
1570 | wlr_output_state_finish(&state);
1571 |
1572 | output = malloc(sizeof(struct stage_output));
1573 | output->curws = 0; /* TODO */
1574 | output->wlr_output = wlr_output;
1575 | wlr_output->data = output;
1576 | output->server = server;
1577 | output->frame.notify = output_frame;
1578 | wl_signal_add(&wlr_output->events.frame, &output->frame);
1579 | wl_list_insert(&server->outputs, &output->link);
1580 |
1581 | l_output = wlr_output_layout_add_auto(server->output_layout,
1582 | wlr_output);
1583 | scene_output = wlr_scene_output_create(server->scene, wlr_output);
1584 | wlr_scene_output_layout_add_output(server->scene_layout, l_output,
1585 | scene_output);
1586 |
1587 | init_slots(wlr_output);
1588 | }
1589 |
1590 | static void
1591 | xdg_toplevel_unmap(struct wl_listener *listener, void *data)
1592 | {
1593 | struct stage_view *view;
1594 |
1595 | view = wl_container_of(listener, view, unmap);
1596 |
1597 | wl_list_remove(&view->link);
1598 | }
1599 |
1600 | static void
1601 | xdg_toplevel_request_move(struct wl_listener *listener, void *data)
1602 | {
1603 | struct stage_view *view;
1604 |
1605 | view = wl_container_of(listener, view, request_move);
1606 | }
1607 |
1608 | static void
1609 | xdg_toplevel_destroy(struct wl_listener *listener, void *data)
1610 | {
1611 | struct stage_view *view;
1612 |
1613 | view = wl_container_of(listener, view, destroy);
1614 |
1615 | wl_list_remove(&view->map.link);
1616 | wl_list_remove(&view->unmap.link);
1617 | wl_list_remove(&view->destroy.link);
1618 |
1619 | wl_list_remove(&view->request_move.link);
1620 | wl_list_remove(&view->request_resize.link);
1621 |
1622 | free(view);
1623 | }
1624 |
1625 | static void
1626 | xdg_toplevel_request_resize(struct wl_listener *listener, void *data)
1627 | {
1628 | struct wlr_xdg_toplevel_resize_event *event;
1629 | struct stage_view *view;
1630 |
1631 | printf("%s\n", __func__);
1632 |
1633 | event = data;
1634 | view = wl_container_of(listener, view, request_resize);
1635 | }
1636 |
1637 | static void
1638 | handle_set_app_id(struct wl_listener *listener, void *data)
1639 | {
1640 | struct stage_view *view;
1641 | char *app_id;
1642 |
1643 | view = wl_container_of(listener, view, set_app_id);
1644 |
1645 | app_id = view->xdg_toplevel->app_id;
1646 |
1647 | create_borders(view);
1648 | view_set_slot(view);
1649 | update_borders(view);
1650 | }
1651 |
1652 | static void
1653 | server_new_xdg_surface(struct wl_listener *listener, void *data)
1654 | {
1655 | struct wlr_xdg_surface *xdg_surface;
1656 | struct wlr_xdg_surface *parent;
1657 | struct wlr_xdg_toplevel *xdg_toplevel;
1658 | struct wlr_scene_node *parent_node;
1659 | struct stage_server *server;
1660 | struct stage_view *view;
1661 |
1662 | printf("%s\n", __func__);
1663 |
1664 | server = wl_container_of(listener, server, new_xdg_surface);
1665 | xdg_toplevel = data;
1666 | printf("%s: app_id %s\n", __func__, xdg_toplevel->app_id);
1667 |
1668 | view = malloc(sizeof(struct stage_view));
1669 | memset(view, 0, sizeof(struct stage_view));
1670 | view->type = VIEW_XDG;
1671 | view->server = server;
1672 | view->xdg_toplevel = xdg_toplevel;
1673 | view->scene_tree = wlr_scene_xdg_surface_create(
1674 | &view->server->scene->tree, xdg_toplevel->base);
1675 | view->scene_tree->node.data = view;
1676 | xdg_toplevel->base->data = view->scene_tree;
1677 |
1678 | view->map.notify = xdg_toplevel_map;
1679 | wl_signal_add(&xdg_toplevel->base->surface->events.map, &view->map);
1680 | view->unmap.notify = xdg_toplevel_unmap;
1681 | wl_signal_add(&xdg_toplevel->base->surface->events.unmap, &view->unmap);
1682 | view->destroy.notify = xdg_toplevel_destroy;
1683 | wl_signal_add(&xdg_toplevel->events.destroy, &view->destroy);
1684 |
1685 | view->request_move.notify = xdg_toplevel_request_move;
1686 | wl_signal_add(&xdg_toplevel->events.request_move,
1687 | &view->request_move);
1688 | view->request_resize.notify = xdg_toplevel_request_resize;
1689 | wl_signal_add(&xdg_toplevel->events.request_resize,
1690 | &view->request_resize);
1691 |
1692 | view->set_app_id.notify = handle_set_app_id;
1693 | wl_signal_add(&xdg_toplevel->events.set_app_id,
1694 | &view->set_app_id);
1695 | }
1696 |
1697 | static void
1698 | xdg_popup_commit(struct wl_listener *listener, void *data)
1699 | {
1700 | struct stage_popup *popup;
1701 |
1702 | popup = wl_container_of(listener, popup, commit);
1703 | if (popup->xdg_popup->base->initial_commit)
1704 | wlr_xdg_surface_schedule_configure(popup->xdg_popup->base);
1705 | }
1706 |
1707 | static void
1708 | xdg_popup_destroy(struct wl_listener *listener, void *data)
1709 | {
1710 | struct stage_popup *popup;
1711 |
1712 | popup = wl_container_of(listener, popup, destroy);
1713 |
1714 | wl_list_remove(&popup->commit.link);
1715 | wl_list_remove(&popup->destroy.link);
1716 |
1717 | free(popup);
1718 | }
1719 |
1720 | static void
1721 | server_new_xdg_popup(struct wl_listener *listener, void *data)
1722 | {
1723 | struct wlr_xdg_popup *xdg_popup;
1724 | struct wlr_xdg_surface *parent;
1725 | struct stage_popup *popup;
1726 |
1727 | xdg_popup = data;
1728 |
1729 | popup = calloc(1, sizeof(*popup));
1730 | popup->xdg_popup = xdg_popup;
1731 |
1732 | parent = wlr_xdg_surface_try_from_wlr_surface(xdg_popup->parent);
1733 | assert(parent != NULL);
1734 |
1735 | struct wlr_scene_tree *parent_tree;
1736 |
1737 | parent_tree = parent->data;
1738 | xdg_popup->base->data = wlr_scene_xdg_surface_create(parent_tree,
1739 | xdg_popup->base);
1740 |
1741 | popup->commit.notify = xdg_popup_commit;
1742 | wl_signal_add(&xdg_popup->base->surface->events.commit, &popup->commit);
1743 |
1744 | popup->destroy.notify = xdg_popup_destroy;
1745 | wl_signal_add(&xdg_popup->events.destroy, &popup->destroy);
1746 | }
1747 |
1748 | static void
1749 | server_cursor_axis(struct wl_listener *listener, void *data)
1750 | {
1751 | struct wlr_pointer_axis_event *event;
1752 | struct stage_server *server;
1753 |
1754 | server = wl_container_of(listener, server, cursor_axis);
1755 |
1756 | event = data;
1757 |
1758 | dbg_printf("%s\n", __func__);
1759 | printf("%s\n", __func__);
1760 |
1761 | wlr_seat_pointer_notify_axis(server->seat,
1762 | event->time_msec, event->orientation, event->delta,
1763 | event->delta_discrete, event->source, event->relative_direction);
1764 | }
1765 |
1766 | static void
1767 | server_cursor_frame(struct wl_listener *listener, void *data)
1768 | {
1769 | struct stage_server *server;
1770 |
1771 | server = wl_container_of(listener, server, cursor_frame);
1772 | dbg_printf("%s\n", __func__);
1773 |
1774 | wlr_seat_pointer_notify_frame(server->seat);
1775 | }
1776 |
1777 | static void
1778 | process_cursor_move(struct stage_server *server, uint32_t time)
1779 | {
1780 | struct terminal_slot *slot;
1781 | struct stage_view *view;
1782 | int delta_x, delta_y;
1783 | int i;
1784 |
1785 | view = server->grabbed_view;
1786 | view->x = server->cursor->x - server->grab_x;
1787 | view->y = server->cursor->y - server->grab_y;
1788 |
1789 | dbg_printf("move %d %d\n", view->x, view->y);
1790 |
1791 | for (i = 0; i < nslots; i++) {
1792 | slot = &slots[i];
1793 | if (slot->w == view->w &&
1794 | slot->h == view->h) {
1795 | }
1796 |
1797 | delta_x = abs(slot->x - view->x);
1798 | delta_y = abs(slot->y - view->y);
1799 |
1800 | if (delta_x <= 20 && delta_y <= 20) {
1801 | view->x = slot->x;
1802 | view->y = slot->y;
1803 | break;
1804 | }
1805 | }
1806 |
1807 | wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y);
1808 | }
1809 |
1810 | static void
1811 | process_cursor_resize(struct stage_server *server, uint32_t time)
1812 | {
1813 | struct terminal_slot *slot;
1814 | double border_x, border_y;
1815 | int new_w, new_h;
1816 | struct stage_view *view;
1817 | int new_left;
1818 | int new_right;
1819 | int new_top;
1820 | int new_bottom;
1821 | int delta_x, delta_y;
1822 | int slot_w, slot_h;
1823 | int i;
1824 |
1825 | view = server->grabbed_view;
1826 |
1827 | new_w = server->cursor->x - view->x;
1828 | new_h = server->cursor->y - view->y;
1829 |
1830 | for (i = 0; i < nslots; i++) {
1831 | slot = &slots[i];
1832 | if (slot->w == view->w &&
1833 | slot->h == view->h) {
1834 | }
1835 |
1836 | slot_w = slot->x + slot->w;
1837 | slot_h = slot->y + slot->h;
1838 |
1839 | delta_x = fabs(slot_w - server->cursor->x);
1840 | delta_y = fabs(slot_h - server->cursor->y);
1841 |
1842 | if (delta_x <= 20 && delta_y <= 20) {
1843 | new_w = slot_w - view->x;
1844 | new_h = slot_h - view->y;
1845 | break;
1846 | }
1847 | }
1848 |
1849 | view->w = new_w;
1850 | view->h = new_h;
1851 |
1852 | update_borders(view);
1853 | }
1854 |
1855 | static void
1856 | cursor_focus(struct stage_server *server, uint32_t time)
1857 | {
1858 | struct wlr_layer_surface_v1 *ls;
1859 | struct wlr_surface *surface;
1860 | struct wlr_seat *seat;
1861 | struct stage_view *view;
1862 | double sx, sy;
1863 |
1864 | seat = server->seat;
1865 |
1866 | surface = NULL;
1867 |
1868 | view = desktop_view_at(server, server->cursor->x, server->cursor->y,
1869 | &surface, &sx, &sy);
1870 | if (!view)
1871 | wlr_cursor_set_xcursor(server->cursor, server->cursor_mgr,
1872 | "left_ptr");
1873 |
1874 | if (!view && surface) {
1875 | ls = wlr_layer_surface_v1_try_from_wlr_surface(surface);
1876 | if (ls) {
1877 | wlr_seat_pointer_notify_motion(seat, time, sx, sy);
1878 | wlr_seat_pointer_notify_enter(seat, ls->surface,
1879 | sx, sy);
1880 | return;
1881 | }
1882 | }
1883 |
1884 | if (surface) {
1885 | wlr_seat_pointer_notify_motion(seat, time, sx, sy);
1886 | wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
1887 | focus_view(view, surface);
1888 | } else
1889 | wlr_seat_pointer_clear_focus(seat);
1890 | }
1891 |
1892 | static void
1893 | process_cursor_motion(struct stage_server *server, uint32_t time)
1894 | {
1895 | struct stage_view *view;
1896 | double sx, sy;
1897 |
1898 | dbg_printf("%s: mode %d\n", __func__, server->cursor_mode);
1899 |
1900 | switch (server->cursor_mode) {
1901 | case STAGE_CURSOR_MOVE:
1902 | process_cursor_move(server, time);
1903 | return;
1904 | case STAGE_CURSOR_RESIZE:
1905 | process_cursor_resize(server, time);
1906 | return;
1907 | case STAGE_CURSOR_SCROLL:
1908 | sx = server->cursor->x - server->grabbed_view->x;
1909 | sy = server->cursor->y - server->grabbed_view->y;
1910 | wlr_seat_pointer_notify_motion(server->seat, time, sx, sy);
1911 | return;
1912 | default:
1913 | break;
1914 | }
1915 |
1916 | cursor_focus(server, time);
1917 | }
1918 |
1919 | static void
1920 | server_cursor_motion(struct wl_listener *listener, void *data)
1921 | {
1922 | struct wlr_pointer_motion_event *event;
1923 | struct stage_server *server;
1924 |
1925 | event = data;
1926 |
1927 | server = wl_container_of(listener, server, cursor_motion);
1928 | if (server->locked)
1929 | return;
1930 |
1931 | dbg_printf("%s: dx dy %f %f\n", __func__, event->x, event->y);
1932 |
1933 | wlr_cursor_move(server->cursor, server->device, event->delta_x,
1934 | event->delta_y);
1935 | process_cursor_motion(server, event->time_msec);
1936 | }
1937 |
1938 | static void
1939 | server_cursor_motion_absolute(struct wl_listener *listener, void *data)
1940 | {
1941 | struct wlr_pointer_motion_absolute_event *event;
1942 | struct stage_server *server;
1943 |
1944 | event = data;
1945 |
1946 | server = wl_container_of(listener, server, cursor_motion_absolute);
1947 | if (server->locked)
1948 | return;
1949 |
1950 | dbg_printf("%s: dx dy %f %f\n", __func__, event->x, event->y);
1951 |
1952 | wlr_cursor_warp_absolute(server->cursor, server->device, event->x,
1953 | event->y);
1954 | process_cursor_motion(server, event->time_msec);
1955 | }
1956 |
1957 | static void
1958 | server_cursor_button(struct wl_listener *listener, void *data)
1959 | {
1960 | struct wlr_pointer_button_event *event;
1961 | struct wlr_layer_surface_v1 *ls;
1962 | struct wlr_keyboard *keyboard;
1963 | struct stage_server *server;
1964 | uint32_t mods;
1965 |
1966 | server = wl_container_of(listener, server, cursor_button);
1967 | if (server->locked)
1968 | return;
1969 |
1970 | keyboard = wlr_seat_get_keyboard(server->seat);
1971 | event = data;
1972 |
1973 | double sx, sy;
1974 | struct wlr_surface *surface;
1975 | struct stage_view *view;
1976 | double x,y;
1977 |
1978 | view = desktop_view_at(server, server->cursor->x, server->cursor->y,
1979 | &surface, &sx, &sy);
1980 |
1981 | if (!view && surface) {
1982 | ls = wlr_layer_surface_v1_try_from_wlr_surface(surface);
1983 | if (ls) {
1984 | wlr_seat_pointer_notify_button(server->seat,
1985 | event->time_msec, event->button, event->state);
1986 | return;
1987 | }
1988 | }
1989 |
1990 | if (!view) {
1991 | server->cursor_mode = STAGE_CURSOR_PASSTHROUGH;
1992 | return;
1993 | }
1994 |
1995 | mods = wlr_keyboard_get_modifiers(keyboard);
1996 | if ((mods & STAGE_MODIFIER) &&
1997 | event->state == WL_POINTER_BUTTON_STATE_PRESSED) {
1998 | server->grabbed_view = view;
1999 | server->grab_x = server->cursor->x - view->x;
2000 | server->grab_y = server->cursor->y - view->y;
2001 | server->cur_saved_x = server->cursor->x;
2002 | server->cur_saved_y = server->cursor->y;
2003 |
2004 | if (event->button == BTN_LEFT) {
2005 | server->cursor_mode = STAGE_CURSOR_MOVE;
2006 | wlr_scene_node_raise_to_top(&view->scene_tree->node);
2007 | } else if (event->button == BTN_RIGHT) {
2008 | server->cursor_mode = STAGE_CURSOR_RESIZE;
2009 |
2010 | #if 0
2011 | x = view->xdg_toplevel->base->current.geometry.width +
2012 | view->x - server->cursor->x;
2013 | y = view->xdg_toplevel->base->current.geometry.height +
2014 | view->y - server->cursor->y;
2015 | wlr_cursor_move(server->cursor, NULL, x, y);
2016 | #endif
2017 | }
2018 |
2019 | return;
2020 | }
2021 |
2022 | if ((event->state == WL_POINTER_BUTTON_STATE_RELEASED) &&
2023 | ((server->cursor_mode == STAGE_CURSOR_MOVE) ||
2024 | (server->cursor_mode == STAGE_CURSOR_RESIZE))) {
2025 |
2026 | server->cursor_mode = STAGE_CURSOR_PASSTHROUGH;
2027 |
2028 | if (event->button == BTN_LEFT) {
2029 | } else if (event->button == BTN_RIGHT) {
2030 | #if 0
2031 | x = view->xdg_toplevel->base->current.geometry.width /
2032 | 2 + view->x - server->cursor->x;
2033 | y = view->xdg_toplevel->base->current.geometry.height /
2034 | 2 + view->y - server->cursor->y;
2035 | #if 0
2036 | x = server->cur_saved_x - server->cursor->x;
2037 | y = server->cur_saved_y - server->cursor->y;
2038 | #endif
2039 | wlr_cursor_move(server->cursor, NULL, x, y);
2040 | #endif
2041 | }
2042 |
2043 | return;
2044 | }
2045 |
2046 | if (mods == 0) {
2047 | if (event->button == BTN_LEFT) {
2048 | if (event->state == WL_POINTER_BUTTON_STATE_PRESSED) {
2049 | server->grabbed_view = view;
2050 | server->grab_x = server->cursor->x - view->x;
2051 | server->grab_y = server->cursor->y - view->y;
2052 | server->cursor_mode = STAGE_CURSOR_SCROLL;
2053 | } else
2054 | server->cursor_mode = STAGE_CURSOR_PASSTHROUGH;
2055 | }
2056 | }
2057 |
2058 | wlr_seat_pointer_notify_button(server->seat, event->time_msec,
2059 | event->button, event->state);
2060 | }
2061 |
2062 | static void
2063 | seat_request_cursor(struct wl_listener *listener, void *data)
2064 | {
2065 | struct stage_server *server;
2066 |
2067 | printf("%s\n", __func__);
2068 |
2069 | server = wl_container_of(listener, server, request_cursor);
2070 |
2071 | wlr_cursor_set_xcursor(server->cursor, server->cursor_mgr, "left_ptr");
2072 | }
2073 |
2074 | static void
2075 | seat_request_set_selection(struct wl_listener *listener, void *data)
2076 | {
2077 | struct wlr_seat_request_set_selection_event *event;
2078 | struct stage_server *server;
2079 |
2080 | printf("%s\n", __func__);
2081 |
2082 | server = wl_container_of(listener, server, request_set_selection);
2083 | event = data;
2084 |
2085 | wlr_seat_set_selection(server->seat, event->source, event->serial);
2086 | }
2087 |
2088 | static void
2089 | seat_request_set_primary_selection(struct wl_listener *listener, void *data)
2090 | {
2091 | struct wlr_seat_request_set_primary_selection_event *event;
2092 | struct stage_server *server;
2093 |
2094 | printf("%s\n", __func__);
2095 |
2096 | server = wl_container_of(listener, server,
2097 | request_set_primary_selection);
2098 |
2099 | event = data;
2100 |
2101 | wlr_seat_set_primary_selection(server->seat, event->source,
2102 | event->serial);
2103 | }
2104 |
2105 | int
2106 | main(int argc, char *argv[])
2107 | {
2108 | struct stage_server server;
2109 | const char *socket;
2110 | int error;
2111 | int i;
2112 |
2113 | wlr_log_init(WLR_DEBUG, NULL);
2114 |
2115 | memset(&server, 0, sizeof(struct stage_server));
2116 |
2117 | server.wl_disp = wl_display_create();
2118 | server.backend = wlr_backend_autocreate(wl_display_get_event_loop(server.wl_disp), 0);
2119 | server.renderer = wlr_renderer_autocreate(server.backend);
2120 | wlr_renderer_init_wl_display(server.renderer, server.wl_disp);
2121 |
2122 | server.allocator = wlr_allocator_autocreate(server.backend,
2123 | server.renderer);
2124 | server.compositor = wlr_compositor_create(server.wl_disp, 5,
2125 | server.renderer);
2126 |
2127 | #if 0
2128 | wlr_text_input_manager_v3_create(server.wl_disp);
2129 | #endif
2130 | wlr_export_dmabuf_manager_v1_create(server.wl_disp);
2131 | wlr_screencopy_manager_v1_create(server.wl_disp);
2132 | wlr_data_control_manager_v1_create(server.wl_disp);
2133 | wlr_data_device_manager_create(server.wl_disp);
2134 | wlr_gamma_control_manager_v1_create(server.wl_disp);
2135 | wlr_primary_selection_v1_device_manager_create(server.wl_disp);
2136 | wlr_viewporter_create(server.wl_disp);
2137 | wlr_subcompositor_create(server.wl_disp);
2138 |
2139 | server.activation = wlr_xdg_activation_v1_create(server.wl_disp);
2140 | server.request_activate.notify = request_activate;
2141 | wl_signal_add(&server.activation->events.request_activate,
2142 | &server.request_activate);
2143 |
2144 | server.output_layout = wlr_output_layout_create(server.wl_disp);
2145 | server.layout_change.notify = layout_change;
2146 | wl_signal_add(&server.output_layout->events.change,
2147 | &server.layout_change);
2148 | wlr_xdg_output_manager_v1_create(server.wl_disp, server.output_layout);
2149 |
2150 | wl_list_init(&server.outputs);
2151 | server.new_output.notify = server_new_output;
2152 | wl_signal_add(&server.backend->events.new_output, &server.new_output);
2153 |
2154 | server.scene = wlr_scene_create();
2155 | server.scene_layout = wlr_scene_attach_output_layout(server.scene,
2156 | server.output_layout);
2157 |
2158 | #if 0
2159 | struct wlr_scene_tree *tree;
2160 | tree = wlr_scene_tree_create(&server.scene->tree);
2161 | #endif
2162 |
2163 | server.xdg_shell = wlr_xdg_shell_create(server.wl_disp, 3);
2164 | server.new_xdg_surface.notify = server_new_xdg_surface;
2165 | wl_signal_add(&server.xdg_shell->events.new_toplevel,
2166 | &server.new_xdg_surface);
2167 |
2168 | server.new_xdg_popup.notify = server_new_xdg_popup;
2169 | wl_signal_add(&server.xdg_shell->events.new_popup,
2170 | &server.new_xdg_popup);
2171 |
2172 | server.output_manager = wlr_output_manager_v1_create(server.wl_disp);
2173 | server.output_manager_apply.notify = output_manager_apply;
2174 | wl_signal_add(&server.output_manager->events.apply,
2175 | &server.output_manager_apply);
2176 | server.output_manager_test.notify = output_manager_test;
2177 | wl_signal_add(&server.output_manager->events.test,
2178 | &server.output_manager_test);
2179 |
2180 | server.presentation = wlr_presentation_create(server.wl_disp,
2181 | server.backend, 2);
2182 |
2183 | server.cursor = wlr_cursor_create();
2184 | wlr_cursor_attach_output_layout(server.cursor, server.output_layout);
2185 |
2186 | #if 1
2187 | wlr_virtual_keyboard_manager_v1_create(server.wl_disp);
2188 | #endif
2189 |
2190 | server.cursor_mgr = wlr_xcursor_manager_create(NULL, 24);
2191 | wlr_xcursor_manager_load(server.cursor_mgr, 1);
2192 |
2193 | server.cursor_motion.notify = server_cursor_motion;
2194 | wl_signal_add(&server.cursor->events.motion, &server.cursor_motion);
2195 | server.cursor_motion_absolute.notify = server_cursor_motion_absolute;
2196 | wl_signal_add(&server.cursor->events.motion_absolute,
2197 | &server.cursor_motion_absolute);
2198 | server.cursor_button.notify = server_cursor_button;
2199 | wl_signal_add(&server.cursor->events.button, &server.cursor_button);
2200 | server.cursor_axis.notify = server_cursor_axis;
2201 | wl_signal_add(&server.cursor->events.axis, &server.cursor_axis);
2202 | server.cursor_frame.notify = server_cursor_frame;
2203 | wl_signal_add(&server.cursor->events.frame, &server.cursor_frame);
2204 |
2205 | for (i = 0; i < N_WORKSPACES; i++) {
2206 | wl_list_init(&workspaces[i].views);
2207 | workspaces[i].name = XKB_KEY_0 + i;
2208 | }
2209 |
2210 | workspaces[10].name = XKB_KEY_minus;
2211 | workspaces[11].name = XKB_KEY_equal;
2212 | workspaces[12].name = XKB_KEY_backslash;
2213 | workspaces[13].name = XKB_KEY_grave;
2214 | workspaces[14].name = XKB_KEY_t;
2215 | workspaces[15].name = XKB_KEY_s;
2216 |
2217 | wl_list_init(&server.keyboards);
2218 | server.new_input.notify = server_new_input;
2219 | wl_signal_add(&server.backend->events.new_input, &server.new_input);
2220 | server.seat = wlr_seat_create(server.wl_disp, "seat0");
2221 | server.request_cursor.notify = seat_request_cursor;
2222 | wl_signal_add(&server.seat->events.request_set_cursor,
2223 | &server.request_cursor);
2224 | server.request_set_selection.notify = seat_request_set_selection;
2225 | wl_signal_add(&server.seat->events.request_set_selection,
2226 | &server.request_set_selection);
2227 |
2228 | server.request_set_primary_selection.notify =
2229 | seat_request_set_primary_selection;
2230 | wl_signal_add(&server.seat->events.request_set_primary_selection,
2231 | &server.request_set_primary_selection);
2232 |
2233 | wlr_server_decoration_manager_set_default_mode(
2234 | wlr_server_decoration_manager_create(server.wl_disp),
2235 | WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
2236 | wlr_xdg_decoration_manager_v1_create(server.wl_disp);
2237 |
2238 | server.lock = wlr_session_lock_manager_v1_create(server.wl_disp);
2239 | server.new_lock.notify = new_lock;
2240 | wl_signal_add(&server.lock->events.new_lock, &server.new_lock);
2241 |
2242 | server.shell = wlr_layer_shell_v1_create(server.wl_disp, 4);
2243 | server.new_layer_shell_surface.notify = new_layer_shell_surface;
2244 | wl_signal_add(&server.shell->events.new_surface,
2245 | &server.new_layer_shell_surface);
2246 |
2247 | #if 0
2248 | server.inhibit_manager =
2249 | wlr_input_inhibit_manager_create(server.wl_disp);
2250 | server.idle = wlr_idle_create(server.wl_disp);
2251 | #endif
2252 |
2253 | socket = wl_display_add_socket_auto(server.wl_disp);
2254 | if (socket == NULL) {
2255 | wlr_backend_destroy(server.backend);
2256 | return (1);
2257 | }
2258 |
2259 | error = wlr_backend_start(server.backend);
2260 | if (error == 0) {
2261 | wlr_backend_destroy(server.backend);
2262 | wl_display_destroy(server.wl_disp);
2263 | return (2);
2264 | }
2265 |
2266 | setenv("WAYLAND_DISPLAY", socket, true);
2267 | if (fork() == 0)
2268 | execl("/bin/sh", "/bin/sh", "-c", terminal, NULL);
2269 | #ifndef STAGE_DEV
2270 | if (fork() == 0)
2271 | execl("/bin/sh", "/bin/sh", "-c", ws, NULL);
2272 | #endif
2273 | wl_display_run(server.wl_disp);
2274 |
2275 | wl_display_destroy_clients(server.wl_disp);
2276 | wl_display_destroy(server.wl_disp);
2277 |
2278 | return (0);
2279 | }
2280 |
--------------------------------------------------------------------------------
/ws/meson.build:
--------------------------------------------------------------------------------
1 | project(
2 | 'ws',
3 | 'c',
4 | version: '0.0.1',
5 | license: 'BSD',
6 | default_options: ['c_std=c99'],
7 | meson_version: '>=1.1.0',
8 | )
9 |
10 | cc = meson.get_compiler('c')
11 | wayland_protos = dependency('wayland-protocols', version: '>=1.13')
12 | wl_protocol_dir = wayland_protos.get_variable('pkgdatadir')
13 | wayland_scanner = find_program('wayland-scanner')
14 | wayland_client = dependency('wayland-client')
15 | rt = cc.find_library('rt')
16 | pixman = dependency('pixman-1')
17 | fcft = dependency('fcft')
18 |
19 | wayland_scanner_code = generator(
20 | wayland_scanner,
21 | output: '@BASENAME@-protocol.c',
22 | arguments: ['private-code', '@INPUT@', '@OUTPUT@'],
23 | )
24 |
25 | wayland_scanner_client = generator(
26 | wayland_scanner,
27 | output: '@BASENAME@-client-protocol.h',
28 | arguments: ['client-header', '@INPUT@', '@OUTPUT@'],
29 | )
30 |
31 | client_protocols = [
32 | [wl_protocol_dir + '/stable/xdg-shell', 'xdg-shell.xml'],
33 | [meson.project_source_root() + '/protocols', 'wlr-layer-shell-unstable-v1.xml'],]
34 |
35 | foreach p : client_protocols
36 | xml = join_paths(p)
37 | src = wayland_scanner_code.process(xml)
38 | header = wayland_scanner_client.process(xml)
39 |
40 | name = p[1].split('.')[0].underscorify()
41 |
42 | lib = static_library(
43 | name,
44 | [src, header],
45 | dependencies: [wayland_client],
46 | )
47 |
48 | dep = declare_dependency(
49 | link_with: lib,
50 | sources: header,
51 | )
52 |
53 | set_variable(name, dep)
54 | endforeach
55 |
56 | ws_sources = ['src/image.c', 'src/main.c']
57 | ws_dependencies = [wayland_client, wlr_layer_shell_unstable_v1, xdg_shell,
58 | pixman, fcft]
59 |
60 | executable(
61 | 'ws',
62 | ws_sources,
63 | dependencies: ws_dependencies,
64 | install: true
65 | )
66 |
--------------------------------------------------------------------------------
/ws/protocols/wlr-layer-shell-unstable-v1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Copyright © 2017 Drew DeVault
5 |
6 | Permission to use, copy, modify, distribute, and sell this
7 | software and its documentation for any purpose is hereby granted
8 | without fee, provided that the above copyright notice appear in
9 | all copies and that both that copyright notice and this permission
10 | notice appear in supporting documentation, and that the name of
11 | the copyright holders not be used in advertising or publicity
12 | pertaining to distribution of the software without specific,
13 | written prior permission. The copyright holders make no
14 | representations about the suitability of this software for any
15 | purpose. It is provided "as is" without express or implied
16 | warranty.
17 |
18 | THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
19 | SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 | FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 | SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
22 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
23 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
24 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
25 | THIS SOFTWARE.
26 |
27 |
28 |
29 |
30 | Clients can use this interface to assign the surface_layer role to
31 | wl_surfaces. Such surfaces are assigned to a "layer" of the output and
32 | rendered with a defined z-depth respective to each other. They may also be
33 | anchored to the edges and corners of a screen and specify input handling
34 | semantics. This interface should be suitable for the implementation of
35 | many desktop shell components, and a broad number of other applications
36 | that interact with the desktop.
37 |
38 |
39 |
40 |
41 | Create a layer surface for an existing surface. This assigns the role of
42 | layer_surface, or raises a protocol error if another role is already
43 | assigned.
44 |
45 | Creating a layer surface from a wl_surface which has a buffer attached
46 | or committed is a client error, and any attempts by a client to attach
47 | or manipulate a buffer prior to the first layer_surface.configure call
48 | must also be treated as errors.
49 |
50 | You may pass NULL for output to allow the compositor to decide which
51 | output to use. Generally this will be the one that the user most
52 | recently interacted with.
53 |
54 | Clients can specify a namespace that defines the purpose of the layer
55 | surface.
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | These values indicate which layers a surface can be rendered in. They
73 | are ordered by z depth, bottom-most first. Traditional shell surfaces
74 | will typically be rendered between the bottom and top layers.
75 | Fullscreen shell surfaces are typically rendered at the top layer.
76 | Multiple surfaces can share a single layer, and ordering within a
77 | single layer is undefined.
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | An interface that may be implemented by a wl_surface, for surfaces that
90 | are designed to be rendered as a layer of a stacked desktop-like
91 | environment.
92 |
93 | Layer surface state (layer, size, anchor, exclusive zone,
94 | margin, interactivity) is double-buffered, and will be applied at the
95 | time wl_surface.commit of the corresponding wl_surface is called.
96 |
97 |
98 |
99 |
100 | Sets the size of the surface in surface-local coordinates. The
101 | compositor will display the surface centered with respect to its
102 | anchors.
103 |
104 | If you pass 0 for either value, the compositor will assign it and
105 | inform you of the assignment in the configure event. You must set your
106 | anchor to opposite edges in the dimensions you omit; not doing so is a
107 | protocol error. Both values are 0 by default.
108 |
109 | Size is double-buffered, see wl_surface.commit.
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 | Requests that the compositor anchor the surface to the specified edges
118 | and corners. If two orthogonal edges are specified (e.g. 'top' and
119 | 'left'), then the anchor point will be the intersection of the edges
120 | (e.g. the top left corner of the output); otherwise the anchor point
121 | will be centered on that edge, or in the center if none is specified.
122 |
123 | Anchor is double-buffered, see wl_surface.commit.
124 |
125 |
126 |
127 |
128 |
129 |
130 | Requests that the compositor avoids occluding an area with other
131 | surfaces. The compositor's use of this information is
132 | implementation-dependent - do not assume that this region will not
133 | actually be occluded.
134 |
135 | A positive value is only meaningful if the surface is anchored to one
136 | edge or an edge and both perpendicular edges. If the surface is not
137 | anchored, anchored to only two perpendicular edges (a corner), anchored
138 | to only two parallel edges or anchored to all edges, a positive value
139 | will be treated the same as zero.
140 |
141 | A positive zone is the distance from the edge in surface-local
142 | coordinates to consider exclusive.
143 |
144 | Surfaces that do not wish to have an exclusive zone may instead specify
145 | how they should interact with surfaces that do. If set to zero, the
146 | surface indicates that it would like to be moved to avoid occluding
147 | surfaces with a positive exclusive zone. If set to -1, the surface
148 | indicates that it would not like to be moved to accommodate for other
149 | surfaces, and the compositor should extend it all the way to the edges
150 | it is anchored to.
151 |
152 | For example, a panel might set its exclusive zone to 10, so that
153 | maximized shell surfaces are not shown on top of it. A notification
154 | might set its exclusive zone to 0, so that it is moved to avoid
155 | occluding the panel, but shell surfaces are shown underneath it. A
156 | wallpaper or lock screen might set their exclusive zone to -1, so that
157 | they stretch below or over the panel.
158 |
159 | The default value is 0.
160 |
161 | Exclusive zone is double-buffered, see wl_surface.commit.
162 |
163 |
164 |
165 |
166 |
167 |
168 | Requests that the surface be placed some distance away from the anchor
169 | point on the output, in surface-local coordinates. Setting this value
170 | for edges you are not anchored to has no effect.
171 |
172 | The exclusive zone includes the margin.
173 |
174 | Margin is double-buffered, see wl_surface.commit.
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 | Set to 1 to request that the seat send keyboard events to this layer
185 | surface. For layers below the shell surface layer, the seat will use
186 | normal focus semantics. For layers above the shell surface layers, the
187 | seat will always give exclusive keyboard focus to the top-most layer
188 | which has keyboard interactivity set to true.
189 |
190 | Layer surfaces receive pointer, touch, and tablet events normally. If
191 | you do not want to receive them, set the input region on your surface
192 | to an empty region.
193 |
194 | Events is double-buffered, see wl_surface.commit.
195 |
196 |
197 |
198 |
199 |
200 |
201 | This assigns an xdg_popup's parent to this layer_surface. This popup
202 | should have been created via xdg_surface::get_popup with the parent set
203 | to NULL, and this request must be invoked before committing the popup's
204 | initial state.
205 |
206 | See the documentation of xdg_popup for more details about what an
207 | xdg_popup is and how it is used.
208 |
209 |
210 |
211 |
212 |
213 |
214 | When a configure event is received, if a client commits the
215 | surface in response to the configure event, then the client
216 | must make an ack_configure request sometime before the commit
217 | request, passing along the serial of the configure event.
218 |
219 | If the client receives multiple configure events before it
220 | can respond to one, it only has to ack the last configure event.
221 |
222 | A client is not required to commit immediately after sending
223 | an ack_configure request - it may even ack_configure several times
224 | before its next surface commit.
225 |
226 | A client may send multiple ack_configure requests before committing, but
227 | only the last request sent before a commit indicates which configure
228 | event the client really is responding to.
229 |
230 |
231 |
232 |
233 |
234 |
235 | This request destroys the layer surface.
236 |
237 |
238 |
239 |
240 |
241 | The configure event asks the client to resize its surface.
242 |
243 | Clients should arrange their surface for the new states, and then send
244 | an ack_configure request with the serial sent in this configure event at
245 | some point before committing the new surface.
246 |
247 | The client is free to dismiss all but the last configure event it
248 | received.
249 |
250 | The width and height arguments specify the size of the window in
251 | surface-local coordinates.
252 |
253 | The size is a hint, in the sense that the client is free to ignore it if
254 | it doesn't resize, pick a smaller size (to satisfy aspect ratio or
255 | resize in steps of NxM pixels). If the client picks a smaller size and
256 | is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
257 | surface will be centered on this axis.
258 |
259 | If the width or height arguments are zero, it means the client should
260 | decide its own window dimension.
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 | The closed event is sent by the compositor when the surface will no
270 | longer be shown. The output may have been destroyed or the user may
271 | have asked for it to be removed. Further changes to the surface will be
272 | ignored. The client should destroy the resource after receiving this
273 | event, and create a new surface if they so choose.
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 | Change the layer that the surface is rendered on.
295 |
296 | Layer is double-buffered, see wl_surface.commit.
297 |
298 |
299 |
300 |
301 |
302 |
--------------------------------------------------------------------------------
/ws/src/image.c:
--------------------------------------------------------------------------------
1 | /*-
2 | * Copyright (c) 2023-2024 Ruslan Bukin
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions
7 | * are met:
8 | * 1. Redistributions of source code must retain the above copyright
9 | * notice, this list of conditions and the following disclaimer.
10 | * 2. Redistributions in binary form must reproduce the above copyright
11 | * notice, this list of conditions and the following disclaimer in the
12 | * documentation and/or other materials provided with the distribution.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 | * SUCH DAMAGE.
25 | */
26 |
27 | #include
28 |
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 |
36 | #include
37 | #include
38 | #include
39 |
40 | #include "image.h"
41 |
42 | static const char *font_list = "ubuntu mono:size=120,xos4 Terminus:size=120";
43 | static struct fcft_font *font = NULL;
44 | static enum fcft_subpixel subpixel_mode = FCFT_SUBPIXEL_DEFAULT;
45 |
46 | struct ws_image *
47 | ws_image_create(char *name, size_t width, size_t height)
48 | {
49 | char shm_name[NAME_MAX];
50 | struct ws_image *image;
51 | pixman_image_t *pixman;
52 | void *buffer;
53 | size_t size;
54 | int shmid;
55 | int i;
56 |
57 | shmid = -1;
58 | size = width * height * 8;
59 |
60 | for (i = 0; i < UCHAR_MAX; ++i) {
61 | if (snprintf(shm_name, NAME_MAX, "/%s-%d", name, i) >= NAME_MAX)
62 | break;
63 |
64 | shmid = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, 0600);
65 | if (shmid > 0 || errno != EEXIST)
66 | break;
67 | }
68 |
69 | if (shmid < 0) {
70 | printf("shm_open() failed: %s", strerror(errno));
71 | return (NULL);
72 | }
73 |
74 | if (shm_unlink(shm_name) != 0) {
75 | printf("shm_unlink() failed: %s", strerror(errno));
76 | return (NULL);
77 | }
78 |
79 | if (ftruncate(shmid, size) != 0) {
80 | printf("ftruncate() failed: %s", strerror(errno));
81 | return (NULL);
82 | }
83 |
84 | buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, shmid, 0);
85 | if (buffer == MAP_FAILED) {
86 | printf("mmap() failed: %s", strerror(errno));
87 | return (NULL);
88 | }
89 |
90 | pixman = pixman_image_create_bits_no_clear(PIXMAN_a8r8g8b8, width,
91 | height, buffer, width * 4);
92 |
93 | image = malloc(sizeof(struct ws_image));
94 | image->shmid = shmid;
95 | image->width = width;
96 | image->height = height;
97 | image->size_in_bytes = width * height * 4;
98 | image->stride = width * 4;
99 | image->pixman = pixman;
100 |
101 | return (image);
102 | }
103 |
104 | void
105 | ws_image_destroy(struct ws_image *image)
106 | {
107 | pixman_image_t *pixman;
108 |
109 | pixman = image->pixman;
110 | pixman_image_unref(pixman);
111 |
112 | free(image);
113 | }
114 |
115 | void
116 | ws_font_init(void)
117 | {
118 | char **names;
119 | char *copy;
120 | char *name;
121 | size_t len;
122 | size_t i;
123 |
124 | /* Instantiate font, and fallbacks. */
125 | tll(const char *)font_names = tll_init();
126 |
127 | copy = strdup(font_list);
128 | for (name = strtok(copy, ",");
129 | name != NULL;
130 | name = strtok(NULL, ",")) {
131 | while (isspace(*name))
132 | name++;
133 |
134 | len = strlen(name);
135 | while (len > 0 && isspace(name[len - 1]))
136 | name[--len] = '\0';
137 | tll_push_back(font_names, name);
138 | }
139 |
140 | i = 0;
141 | names = malloc(sizeof (char *) * tll_length(font_names));
142 | tll_foreach(font_names, it)
143 | names[i++] = (char *)it->item;
144 |
145 | font = fcft_from_name(tll_length(font_names), (const char **)names,
146 | NULL);
147 | assert(font != NULL);
148 | fcft_set_emoji_presentation(font, FCFT_EMOJI_PRESENTATION_DEFAULT);
149 | tll_free(font_names);
150 | free(copy);
151 | }
152 |
153 | void
154 | ws_image_clear(struct ws_image *image, pixman_color_t *color, int x, int y,
155 | int w, int h)
156 | {
157 | pixman_image_t *pixman;
158 |
159 | pixman = image->pixman;
160 |
161 | pixman_image_fill_rectangles(PIXMAN_OP_SRC, pixman, color, 1,
162 | &(pixman_rectangle16_t){x, y, w, h});
163 | }
164 |
165 | void
166 | ws_image_draw(struct ws_image *image, pixman_color_t *color,
167 | char c, int offset_x, int offset_y)
168 | {
169 | const struct fcft_glyph *g;
170 | pixman_image_t *pixman;
171 | pixman_image_t *clr_pix;
172 |
173 | g = fcft_rasterize_char_utf32(font, c, subpixel_mode);
174 | if (g == NULL)
175 | return;
176 |
177 | pixman = image->pixman;
178 |
179 | if (pixman_image_get_format(g->pix) == PIXMAN_a8r8g8b8) {
180 | pixman_image_composite32(
181 | PIXMAN_OP_OVER, g->pix, NULL, pixman, 0, 0, 0, 0,
182 | offset_x + g->x, offset_y + font->ascent - g->y,
183 | g->width, g->height);
184 | } else {
185 | clr_pix = pixman_image_create_solid_fill(color);
186 | pixman_image_composite32(
187 | PIXMAN_OP_OVER, clr_pix, g->pix, pixman, 0, 0, 0, 0,
188 | offset_x + g->x, offset_y + font->ascent - g->y,
189 | g->width, g->height);
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/ws/src/image.h:
--------------------------------------------------------------------------------
1 | /*-
2 | * Copyright (c) 2023-2024 Ruslan Bukin
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions
7 | * are met:
8 | * 1. Redistributions of source code must retain the above copyright
9 | * notice, this list of conditions and the following disclaimer.
10 | * 2. Redistributions in binary form must reproduce the above copyright
11 | * notice, this list of conditions and the following disclaimer in the
12 | * documentation and/or other materials provided with the distribution.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 | * SUCH DAMAGE.
25 | */
26 |
27 | #ifndef _IMAGE_H_
28 | #define _IMAGE_H_
29 |
30 | #define MAX_WIDTH 250
31 | #define MAX_HEIGHT 1920
32 | #define SERVER_SOCK_FILE "/tmp/stage.sock"
33 |
34 | struct ws_image {
35 | size_t width;
36 | size_t height;
37 | size_t size_in_bytes;
38 | size_t stride;
39 | int shmid;
40 | void *pixman;
41 | };
42 |
43 | struct ws_image *ws_image_create(char *name, size_t width, size_t height);
44 | void ws_image_destroy(struct ws_image *image);
45 | void ws_image_draw(struct ws_image *image, pixman_color_t *color, char c,
46 | int offset_x, int offset_y);
47 | void ws_font_init(void);
48 | void ws_image_clear(struct ws_image *image, pixman_color_t *color, int x,
49 | int y, int w, int h);
50 |
51 | #endif /* !_IMAGE_H_ */
52 |
--------------------------------------------------------------------------------
/ws/src/main.c:
--------------------------------------------------------------------------------
1 | /*-
2 | * Copyright (c) 2023-2024 Ruslan Bukin
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions
7 | * are met:
8 | * 1. Redistributions of source code must retain the above copyright
9 | * notice, this list of conditions and the following disclaimer.
10 | * 2. Redistributions in binary form must reproduce the above copyright
11 | * notice, this list of conditions and the following disclaimer in the
12 | * documentation and/or other materials provided with the distribution.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 | * SUCH DAMAGE.
25 | */
26 |
27 | #include
28 | #include
29 |
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 |
37 | #include
38 |
39 | #include "image.h"
40 | #include "wlr-layer-shell-unstable-v1-client-protocol.h"
41 |
42 | static pixman_color_t fg = {0xffff, 0xffff, 0xffff, 0xffff};
43 | static pixman_color_t bg = {0x0000, 0x0000, 0x0000, 0xffff};
44 | static pixman_color_t mg = {0x4444, 0x4444, 0x4444, 0xffff};
45 | static pixman_color_t og = {0x9999, 0x9999, 0x9999, 0xffff};
46 | static pixman_color_t xy = {0x1fff, 0x1fff, 0x1fff, 0xffff};
47 |
48 | struct ws_surface {
49 | struct zwlr_layer_surface_v1 *wlr_layer_surface;
50 | struct wl_surface *wl_surface;
51 | };
52 |
53 | struct ws_output {
54 | struct wl_list link;
55 | struct wl_output *wl_output;
56 | struct ws_surface *ws_surface;
57 | };
58 |
59 | struct ws {
60 | struct ws_image *image;
61 | struct wl_buffer *wl_buffer;
62 | struct wl_compositor *wl_compositor;
63 | struct wl_display *wl_display;
64 | struct wl_list ws_outputs;
65 | struct wl_registry *wl_registry;
66 | struct wl_shm *wl_shm;
67 | struct zwlr_layer_shell_v1 *wlr_layer_shell;
68 | char *name;
69 | int margin_top;
70 | int margin_right;
71 | int margin_bottom;
72 | int margin_left;
73 | int width;
74 | int height;
75 | int anchor;
76 | };
77 |
78 | void
79 | layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface,
80 | uint32_t serial, uint32_t w, uint32_t h)
81 | {
82 |
83 | zwlr_layer_surface_v1_ack_configure(surface, serial);
84 | }
85 |
86 | void
87 | layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface)
88 | {
89 |
90 | }
91 |
92 | const static struct zwlr_layer_surface_v1_listener
93 | zwlr_layer_surface_listener = {
94 | .configure = layer_surface_configure,
95 | .closed = layer_surface_closed,
96 | };
97 |
98 | struct ws_surface *
99 | ws_surface_create(struct ws *app, struct wl_output *wl_output)
100 | {
101 | struct ws_surface *ws_surface;
102 |
103 | ws_surface = calloc(1, sizeof(struct ws_surface));
104 | if (ws_surface == NULL) {
105 | printf("calloc failed");
106 | return (NULL);
107 | }
108 |
109 | ws_surface->wl_surface =
110 | wl_compositor_create_surface(app->wl_compositor);
111 | if (ws_surface->wl_surface == NULL) {
112 | printf("wl_compositor_create_surface failed");
113 | return (NULL);
114 | }
115 |
116 | ws_surface->wlr_layer_surface =
117 | zwlr_layer_shell_v1_get_layer_surface(app->wlr_layer_shell,
118 | ws_surface->wl_surface, wl_output,
119 | ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "ws");
120 | if (ws_surface->wlr_layer_surface == NULL) {
121 | printf("wlr_layer_shell_v1_get_layer_surface failed");
122 | return (NULL);
123 | }
124 |
125 | zwlr_layer_surface_v1_set_margin(ws_surface->wlr_layer_surface,
126 | app->margin_top, app->margin_right, app->margin_bottom,
127 | app->margin_left);
128 | zwlr_layer_surface_v1_set_size(ws_surface->wlr_layer_surface,
129 | app->width, app->height);
130 | zwlr_layer_surface_v1_set_anchor(ws_surface->wlr_layer_surface,
131 | app->anchor);
132 | zwlr_layer_surface_v1_add_listener(ws_surface->wlr_layer_surface,
133 | &zwlr_layer_surface_listener, app);
134 |
135 | return (ws_surface);
136 | }
137 |
138 | void
139 | ws_surface_destroy(struct ws_surface *ws_surface)
140 | {
141 |
142 | zwlr_layer_surface_v1_destroy(ws_surface->wlr_layer_surface);
143 | wl_surface_destroy(ws_surface->wl_surface);
144 |
145 | free(ws_surface);
146 | }
147 |
148 | void
149 | ws_output_destroy(struct ws_output *output)
150 | {
151 |
152 | if (output->ws_surface != NULL)
153 | ws_surface_destroy(output->ws_surface);
154 |
155 | if (output->wl_output != NULL)
156 | wl_output_destroy(output->wl_output);
157 |
158 | free(output);
159 | }
160 |
161 | void
162 | handle_global(void *data, struct wl_registry *registry, uint32_t name,
163 | const char *interface, uint32_t version)
164 | {
165 | struct ws_output *output;
166 | struct ws *app;
167 |
168 | app = (struct ws *)data;
169 |
170 | if (strcmp(interface, wl_shm_interface.name) == 0)
171 | app->wl_shm = wl_registry_bind(registry, name,
172 | &wl_shm_interface, 1);
173 | else if (strcmp(interface, wl_compositor_interface.name) == 0)
174 | app->wl_compositor = wl_registry_bind(registry, name,
175 | &wl_compositor_interface, 1);
176 | else if (strcmp(interface, wl_output_interface.name) == 0) {
177 |
178 | if (version < 4) {
179 | printf("Unsupported version\n");
180 | return;
181 | }
182 |
183 | output = calloc(1, sizeof(struct ws_output));
184 | output->wl_output = wl_registry_bind(registry, name,
185 | &wl_output_interface, version);
186 |
187 | wl_list_insert(&app->ws_outputs, &output->link);
188 | } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0)
189 | app->wlr_layer_shell = wl_registry_bind(registry, name,
190 | &zwlr_layer_shell_v1_interface, 1);
191 | }
192 |
193 | void
194 | handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
195 | {
196 |
197 | /* No support. */
198 | }
199 |
200 | void
201 | ws_flush(struct ws *app)
202 | {
203 | struct ws_output *output;
204 |
205 | wl_list_for_each (output, &app->ws_outputs, link) {
206 | if (output->ws_surface != NULL)
207 | continue;
208 |
209 | output->ws_surface = ws_surface_create(app,
210 | output->wl_output);
211 | wl_surface_commit(output->ws_surface->wl_surface);
212 | }
213 |
214 | if (wl_display_roundtrip(app->wl_display) < 0)
215 | printf("wl_display_roundtrip failed");
216 |
217 | wl_list_for_each (output, &(app->ws_outputs), link) {
218 | if (output->ws_surface == NULL)
219 | continue;
220 |
221 | wl_surface_attach(output->ws_surface->wl_surface,
222 | app->wl_buffer, 0, 0);
223 | wl_surface_damage(output->ws_surface->wl_surface, 0, 0,
224 | INT32_MAX, INT32_MAX);
225 | wl_surface_commit(output->ws_surface->wl_surface);
226 | }
227 |
228 | if (wl_display_roundtrip(app->wl_display) < 1)
229 | printf("wl_display_roundtrip failed");
230 | }
231 |
232 | static void
233 | draw_cursor_xy(struct ws *app, char *buf)
234 | {
235 | static pixman_color_t *color;
236 | int voffs;
237 | int i;
238 | char c;
239 |
240 | i = 0;
241 | voffs = 0;
242 |
243 | ws_image_clear(app->image, &bg, 150, 0, app->width-150, app->height);
244 |
245 | while (1) {
246 | c = buf[i++];
247 |
248 | if (c == '\0')
249 | break;
250 |
251 | color = &xy;
252 |
253 | if (c == ',')
254 | ws_image_draw(app->image, color, ' ', 150, voffs);
255 | else
256 | ws_image_draw(app->image, color, c, 150, voffs);
257 |
258 | voffs += 120;
259 | }
260 |
261 | ws_flush(app);
262 | }
263 |
264 | static void
265 | draw_numbers(struct ws *app, char *buf)
266 | {
267 | static pixman_color_t *color;
268 | int oldflag;
269 | int newflag;
270 | int voffs;
271 | int i;
272 | char c;
273 |
274 | i = 0;
275 | voffs = 100;
276 | oldflag = newflag = 0;
277 |
278 | ws_image_clear(app->image, &bg, 0, 0, 150, app->height);
279 |
280 | while (1) {
281 | c = buf[i++];
282 |
283 | if (c == '\0')
284 | break;
285 |
286 | if (c == '?') {
287 | oldflag = 1;
288 | continue;
289 | }
290 |
291 | if (c == '!') {
292 | newflag = 1;
293 | continue;
294 | }
295 |
296 | /* printf("c %c ws %d\n", c, ws); */
297 |
298 | if (newflag) {
299 | color = &fg;
300 | newflag = 0;
301 | } else if (oldflag) {
302 | color = &og;
303 | oldflag = 0;
304 | } else
305 | color = &mg;
306 |
307 | ws_image_draw(app->image, color, c, 50, voffs);
308 |
309 | voffs += 120;
310 | }
311 |
312 | ws_flush(app);
313 | }
314 |
315 | int
316 | ws_main_loop(struct ws *app)
317 | {
318 | struct sockaddr_un addr;
319 | struct sockaddr_un from;
320 | socklen_t fromlen;
321 | char buf[128];
322 | int len;
323 | int fd;
324 |
325 | fromlen = sizeof(struct sockaddr_un);
326 |
327 | if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
328 | perror("socket");
329 |
330 | memset(buf, 0, 128);
331 | memset(&addr, 0, sizeof(addr));
332 | addr.sun_family = AF_UNIX;
333 | strcpy(addr.sun_path, SERVER_SOCK_FILE);
334 | unlink(SERVER_SOCK_FILE);
335 |
336 | if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
337 | perror("bind");
338 |
339 | do {
340 | len = recvfrom(fd, buf, 8192, 0, (struct sockaddr *)&from,
341 | &fromlen);
342 | /* printf("recvfrom: %s, len %d\n", buf, len); */
343 | if (buf[0] == 'W')
344 | draw_numbers(app, &buf[1]);
345 | #if 0
346 | else if (buf[0] == 'C')
347 | draw_cursor_xy(app, &buf[1]);
348 | #endif
349 | } while (len > 0);
350 |
351 | close(fd);
352 |
353 | return (0);
354 | }
355 |
356 | static int
357 | ws_startup_app(struct ws *app)
358 | {
359 | struct ws_output *output;
360 | struct wl_shm_pool *pool;
361 | struct ws_image *image;
362 |
363 | image = ws_image_create(app->name, app->width, app->height);
364 |
365 | app->image = image;
366 |
367 | wl_list_init(&app->ws_outputs);
368 | output = calloc(1, sizeof(struct ws_output));
369 | output->wl_output = NULL;
370 | wl_list_insert(&app->ws_outputs, &output->link);
371 |
372 | const static struct wl_registry_listener wl_registry_listener = {
373 | .global = handle_global,
374 | .global_remove = handle_global_remove,
375 | };
376 |
377 | app->wl_display = wl_display_connect(NULL);
378 | if (app->wl_display == NULL) {
379 | printf("wl_display_connect failed");
380 | return (-1);
381 | }
382 |
383 | app->wl_registry = wl_display_get_registry(app->wl_display);
384 | if (app->wl_registry == NULL) {
385 | printf("wl_display_get_registry failed");
386 | return (-1);
387 | }
388 |
389 | wl_registry_add_listener(app->wl_registry, &wl_registry_listener, app);
390 |
391 | if (wl_display_roundtrip(app->wl_display) < 0) {
392 | printf("wl_display_roundtrip failed");
393 | return (-1);
394 | }
395 |
396 | if (wl_display_roundtrip(app->wl_display) < 0) {
397 | printf("wl_display_roundtrip failed");
398 | return (-1);
399 | }
400 |
401 | if (app->wlr_layer_shell == NULL) {
402 | printf("No layer shell available\n");
403 | return (-1);
404 | }
405 |
406 | if (app->wl_shm == NULL) {
407 | printf("No wl_shm available\n");
408 | return (-1);
409 | }
410 |
411 | if (app->wl_compositor == NULL) {
412 | printf("No wl_compositor available\n");
413 | return (-1);
414 | }
415 |
416 | pool = wl_shm_create_pool(app->wl_shm, app->image->shmid,
417 | app->image->size_in_bytes);
418 | if (pool == NULL) {
419 | printf("wl_shm_create_pool failed");
420 | return (-1);
421 | }
422 |
423 | app->wl_buffer = wl_shm_pool_create_buffer(pool, 0, app->image->width,
424 | app->image->height, app->image->stride, WL_SHM_FORMAT_ARGB8888);
425 | wl_shm_pool_destroy(pool);
426 | if (app->wl_buffer == NULL) {
427 | printf("wl_shm_pool_create_buffer failed");
428 | return (-1);
429 | }
430 |
431 | return (0);
432 | }
433 |
434 | static int
435 | ws_destroy_app(struct ws *app)
436 | {
437 |
438 | zwlr_layer_shell_v1_destroy(app->wlr_layer_shell);
439 | wl_buffer_destroy(app->wl_buffer);
440 | wl_compositor_destroy(app->wl_compositor);
441 | wl_shm_destroy(app->wl_shm);
442 | wl_registry_destroy(app->wl_registry);
443 | wl_display_roundtrip(app->wl_display);
444 | wl_display_disconnect(app->wl_display);
445 | ws_image_destroy(app->image);
446 | free(app);
447 |
448 | return (0);
449 | }
450 |
451 | int
452 | main(int argc, char **argv)
453 | {
454 | struct ws *app;
455 |
456 | app = calloc(1, sizeof(struct ws));
457 | app->name = "app";
458 | app->margin_top = 0;
459 | app->margin_right = 0;
460 | app->margin_bottom = 0;
461 | app->margin_left = 0;
462 | app->width = MAX_WIDTH;
463 | app->height = MAX_HEIGHT;
464 | app->anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT;
465 |
466 | fcft_init(FCFT_LOG_COLORIZE_AUTO, false, FCFT_LOG_CLASS_DEBUG);
467 | ws_font_init();
468 |
469 | ws_startup_app(app);
470 | ws_main_loop(app);
471 | ws_destroy_app(app);
472 |
473 | return (0);
474 | }
475 |
--------------------------------------------------------------------------------