├── 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 | ![alt text](https://raw.githubusercontent.com/mdepx/stage/main/screenshots/stage.png) 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 | --------------------------------------------------------------------------------