diff --git a/README.md b/README.md
index f8b86bc..68feac7 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,13 @@
+## about
+`mwc` came to existence out of pure will to create a compositor tailored to my taste and needs. the whole point of `mwc` is to be as simple and predictable to use and not get in the way of its user with any confusing behaviour. i choose master (stack) layout (hence the name `master wayland compositor`), because it is dead simple - tiling is done in a really easy-to-understand manner - toplevels are stacked horizontally until the `master_count`-th one, and then vertically.
+
+all the features implemented up to this point (and all that will be implemented it the future) are done in the simplest possible way i could think off, and all the features requested are added only if they provide something to the end user while maintaining the current simplicity of the compositor.
+
+although `mwc` is aiming to be really simple in its behaviour, it does provide a lot of (opt-in) features to improve its looks such as animations, transparency, rounded corners, blur etc. these are here for all the users who like thinkering with their setup and can be disabled completely, or used in any capacity.
+
## features
- tiling and floating toplevels
- master layout with support for multiple masters, ideal for wide monitors
@@ -12,7 +19,7 @@
- great multitasking with multimonitor and workspaces support
- smooth and customizable animations
- easy configuration with hot reloading on save
-- eye-candy (opacity, blur, rounded cornders and shadows)
+- eye-candy (opacity, blur, rounded corners and shadows)
- portals and an ipc for integrating with other apps
## dependencies
@@ -23,14 +30,13 @@
- libinput
- libdrm
- pixman
-- libxkbcommmon
+- libxkbcommon
- wlroots 18.0
- scenefx 0.2
> \* compile-time dependencies
## building
-install the dependencies and then run
```bash
git clone https://github.com/dqrk0jeste/mwc
cd mwc
@@ -62,7 +68,7 @@ if you need to interact with sandboxed applications and/or screenshare you will
## usage
```bash
-mwc
+mwc [--debug]
```
> you probably want to run it from a tty
@@ -80,3 +86,5 @@ for detailed documentation see `examples/example.conf`. you can also find the de
+## donate
+if you want to support this project financially you can do so [here](https://ko-fi.com/darkonikolic)
diff --git a/examples/example.conf b/examples/example.conf
index 4a23f26..5cfcfe2 100644
--- a/examples/example.conf
+++ b/examples/example.conf
@@ -128,7 +128,7 @@ workspace 6 eDP-1
# '--------'
# some toplevels dont have the min size, so you can force that behaviour here; recommended to keep at least at 1
min_toplevel_size 10
-# whether of not to use client side decorations; YOU SHOULD NOT use csd as they are really messing with a lot of things, notably animations and border_radius. i may try to hack a solution to those problems in the future (also see a note before border_radius), but for now you are advised to not use them.
+# whether of not to use client side decorations
client_side_decorations 0
outer_gaps 12
inner_gaps 6
@@ -143,9 +143,6 @@ master_ratio 0.6
# '-----------'
# self explanatory
border_width 2
-# you should not use this option if you use client_side_decorations. there is no reliable way to handle titlebars.
-# thats a issue in core wayland - all the things that are not the main window are so called subsurfaces and only
-# guesses could be made to check if something is or isnt a titlebar, leading to inconsistent and wrong results.
border_radius 12
# set where to draw the rounded corners, multiple options allowed
# - all
@@ -157,7 +154,6 @@ border_radius 12
# - top-left
# - bottom-right
# - bottom-left
-# note: options other then all currently dont work well
border_radius_location all
# example
# border_radius_location top_left bottom_right
diff --git a/meson.build b/meson.build
index 05ba329..0c96e61 100644
--- a/meson.build
+++ b/meson.build
@@ -16,8 +16,9 @@ protocol_dir = wayland_protocols.get_variable('pkgdatadir')
protocol_files = [
protocol_dir / 'stable/xdg-shell/xdg-shell.xml',
protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml',
+ protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml',
+ protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml',
'protocols/wlr-layer-shell-unstable-v1.xml',
- 'protocols/cursor-shape-v1.xml',
]
generated = []
@@ -86,8 +87,8 @@ executable('mwc-ipc',
install: true
)
-install_data('default.conf', install_dir: '/usr/share/mwc')
-install_data('LICENSE', install_dir: '/usr/share/licenses/mwc')
-install_data('mwc.desktop', install_dir: '/usr/share/wayland-sessions')
-install_data('mwc-portals.conf', install_dir: '/usr/share/xdg-desktop-portal')
+install_data('default.conf', install_dir: get_option('datadir') / 'mwc')
+install_data('LICENSE', install_dir: get_option('datadir') / 'licenses/mwc')
+install_data('mwc.desktop', install_dir: get_option('datadir') / 'wayland-sessions')
+install_data('mwc-portals.conf', install_dir: get_option('datadir') / 'xdg-desktop-portal')
diff --git a/protocols/cursor-shape-v1.xml b/protocols/cursor-shape-v1.xml
deleted file mode 100644
index 56f6a1a..0000000
--- a/protocols/cursor-shape-v1.xml
+++ /dev/null
@@ -1,147 +0,0 @@
-
-
-
- Copyright 2018 The Chromium Authors
- Copyright 2023 Simon Ser
-
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice (including the next
- paragraph) shall be included in all copies or substantial portions of the
- Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
-
-
-
-
- This global offers an alternative, optional way to set cursor images. This
- new way uses enumerated cursors instead of a wl_surface like
- wl_pointer.set_cursor does.
-
- Warning! The protocol described in this file is currently in the testing
- phase. Backward compatible changes may be added together with the
- corresponding interface version bump. Backward incompatible changes can
- only be done by creating a new major version of the extension.
-
-
-
-
- Destroy the cursor shape manager.
-
-
-
-
-
- Obtain a wp_cursor_shape_device_v1 for a wl_pointer object.
-
-
-
-
-
-
-
- Obtain a wp_cursor_shape_device_v1 for a zwp_tablet_tool_v2 object.
-
-
-
-
-
-
-
-
- This interface advertises the list of supported cursor shapes for a
- device, and allows clients to set the cursor shape.
-
-
-
-
- This enum describes cursor shapes.
-
- The names are taken from the CSS W3C specification:
- https://w3c.github.io/csswg-drafts/css-ui/#cursor
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Destroy the cursor shape device.
-
- The device cursor shape remains unchanged.
-
-
-
-
-
- Sets the device cursor to the specified shape. The compositor will
- change the cursor image based on the specified shape.
-
- The cursor actually changes only if the input device focus is one of
- the requesting client's surfaces. If any, the previous cursor image
- (surface or shape) is replaced.
-
- The "shape" argument must be a valid enum entry, otherwise the
- invalid_shape protocol error is raised.
-
- This is similar to the wl_pointer.set_cursor and
- zwp_tablet_tool_v2.set_cursor requests, but this request accepts a
- shape instead of contents in the form of a surface. Clients can mix
- set_cursor and set_shape requests.
-
- The serial parameter must match the latest wl_pointer.enter or
- zwp_tablet_tool_v2.proximity_in serial number sent to the client.
- Otherwise the request will be ignored.
-
-
-
-
-
-
diff --git a/src/config.c b/src/config.c
index 2c77d38..b9c9815 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1223,8 +1223,10 @@ config_reload() {
struct wlr_box output_box;
wlr_output_layout_get_box(server.output_layout, out->wlr_output, &output_box);
- if(o->width != output_box.width || o->height != output_box.height
- || o->refresh_rate - out->wlr_output->refresh > 1000) {
+ if(o->width != output_box.width
+ || o->height != output_box.height
+ || abs((int32_t)o->refresh_rate - (int32_t)out->wlr_output->refresh) > 1000
+ || o->scale != out->wlr_output->scale) {
output_initialize(out->wlr_output, o);
}
@@ -1343,7 +1345,9 @@ config_reload() {
snprintf(cursor_size, sizeof(cursor_size), "%u", server.config->cursor_size);
cursor_size[7] = 0;
- setenv("XCURSOR_THEME", server.config->cursor_theme, true);
+ if(server.config->cursor_theme != NULL) {
+ setenv("XCURSOR_THEME", server.config->cursor_theme, true);
+ }
setenv("XCURSOR_SIZE", cursor_size, true);
config_destroy(old_config);
diff --git a/src/keybinds.c b/src/keybinds.c
index 2edf594..176238d 100644
--- a/src/keybinds.c
+++ b/src/keybinds.c
@@ -199,7 +199,7 @@ keybind_close_keyboard_focused_toplevel(void *data) {
struct mwc_toplevel *toplevel = server.focused_toplevel;
if(toplevel == NULL) return;
- xdg_toplevel_send_close(toplevel->xdg_toplevel->resource);
+ wlr_xdg_toplevel_send_close(toplevel->xdg_toplevel);
}
void
diff --git a/src/keyboard.c b/src/keyboard.c
index a94e2bc..9327a63 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -110,8 +110,15 @@ keyboard_configure(struct mwc_keyboard *keyboard) {
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, &rule_names,
XKB_KEYMAP_COMPILE_NO_FLAGS);
- /* TODO: use defaults here if something is wrong */
- if(keymap == NULL) return false;
+ if(keymap == NULL) {
+ wlr_log(WLR_ERROR, "could not apply the desired configuration to the keyboard");
+ keymap = xkb_keymap_new_from_names(context, NULL,
+ XKB_KEYMAP_COMPILE_NO_FLAGS);
+ if(keymap == NULL) {
+ wlr_log(WLR_ERROR, "could not apply the default configuration to the keyboard");
+ return false;
+ }
+ }
wlr_keyboard_set_keymap(keyboard->wlr_keyboard, keymap);
xkb_keymap_unref(keymap);
diff --git a/src/mwc.c b/src/mwc.c
index 35dff7d..6c08511 100644
--- a/src/mwc.c
+++ b/src/mwc.c
@@ -9,6 +9,7 @@
#include "keyboard.h"
#include "config.h"
#include "output.h"
+#include "pointer.h"
#include "toplevel.h"
#include "popup.h"
#include "layer_surface.h"
@@ -48,6 +49,7 @@
#include
#include
#include
+#include
/* we initialize an instance of our global state */
struct mwc_server server;
@@ -214,7 +216,7 @@ main(int argc, char *argv[]) {
* to dig your fingers in and play with their behavior if you want. Note that
* the clients cannot set the selection directly without compositor approval,
* see the handling of the request_set_selection event below.*/
- wlr_compositor_create(server.wl_display, 5, server.renderer);
+ wlr_compositor_create(server.wl_display, 6, server.renderer);
wlr_subcompositor_create(server.wl_display);
wlr_data_device_manager_create(server.wl_display);
@@ -383,6 +385,22 @@ main(int argc, char *argv[]) {
wl_signal_add(&server.cursor_shape_manager->events.request_set_shape, &server.request_cursor_shape);
wl_signal_add(&server.cursor_shape_manager->events.destroy, &server.cursor_shape_manager_destroy);
+ server.relative_pointer_manager = wlr_relative_pointer_manager_v1_create(server.wl_display);
+ server.relative_pointer_manager_destroy.notify = server_handle_relative_pointer_manager_destroy;
+ wl_signal_add(&server.relative_pointer_manager->events.destroy, &server.relative_pointer_manager_destroy);
+
+ server.pointer_contrains_manager = wlr_pointer_constraints_v1_create(server.wl_display);
+ server.new_contraint.notify = server_handle_new_constraint;
+ wl_signal_add(&server.pointer_contrains_manager->events.new_constraint, &server.new_contraint);
+
+ server.xdg_activation = wlr_xdg_activation_v1_create(server.wl_display);
+
+ server.xdg_activation_request.notify = xdg_activation_handle_request;
+ wl_signal_add(&server.xdg_activation->events.request_activate, &server.xdg_activation_request);
+
+ server.xdg_activation_new_token.notify = xdg_activation_handle_new_token;
+ wl_signal_add(&server.xdg_activation->events.new_token, &server.xdg_activation_new_token);
+
/* Add a Unix socket to the Wayland display. */
const char *socket = wl_display_add_socket_auto(server.wl_display);
if(!socket) {
diff --git a/src/mwc.h b/src/mwc.h
index a326e48..9a21f6b 100644
--- a/src/mwc.h
+++ b/src/mwc.h
@@ -11,6 +11,8 @@
#include
#include
#include
+#include
+#include
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
@@ -123,8 +125,18 @@ struct mwc_server {
struct wl_listener lock_manager_destroy;
struct mwc_lock *lock;
+ struct wlr_pointer_constraints_v1 *pointer_contrains_manager;
+ struct wl_listener new_contraint;
+ struct mwc_pointer_constraint *current_constraint;
+
+ struct wlr_relative_pointer_manager_v1 *relative_pointer_manager;
+ struct wl_listener relative_pointer_manager_destroy;
+
+ struct wlr_xdg_activation_v1 *xdg_activation;
+ struct wl_listener xdg_activation_request;
+ struct wl_listener xdg_activation_new_token;
+
struct mwc_config *config;
- char *config_path;
int *ipc_clients;
bool ipc_running;
diff --git a/src/output.c b/src/output.c
index c026066..142e09f 100644
--- a/src/output.c
+++ b/src/output.c
@@ -4,6 +4,7 @@
#include "mwc.h"
#include "config.h"
#include "layout.h"
+#include "pointer.h"
#include "rendering.h"
#include "workspace.h"
#include "toplevel.h"
@@ -152,7 +153,7 @@ output_transfer_existing_workspaces(struct mwc_output *output) {
struct mwc_workspace *w, *tmp;
wl_list_for_each(o, &server.outputs, link) {
wl_list_for_each_safe(w, tmp, &o->workspaces, link) {
- if(strcmp(w->config->output, output->wlr_output->name) == 0) {
+ if(w->config != NULL && strcmp(w->config->output, output->wlr_output->name) == 0) {
/* fix that outputs state */
if(w == o->active_workspace) {
struct mwc_workspace *owned_workspace = output_find_owned_workspace(o);
@@ -334,6 +335,11 @@ cursor_jump_output(struct mwc_output *output) {
wlr_cursor_warp(server.cursor, NULL,
output_box.x + output_box.width / 2.0,
output_box.y + output_box.height / 2.0);
+
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ pointer_handle_focus(now.tv_sec * 1000 + now.tv_nsec / 1000, false);
}
void
diff --git a/src/pointer.c b/src/pointer.c
index 8ad7765..1598639 100644
--- a/src/pointer.c
+++ b/src/pointer.c
@@ -13,11 +13,14 @@
#include "workspace.h"
#include
+#include
#include
#include
#include
#include
+#include
#include
+#include
extern struct mwc_server server;
@@ -141,8 +144,8 @@ server_reset_cursor_mode() {
void
cursor_handle_motion(uint32_t time) {
/* get the output that the cursor is on currently */
- struct wlr_output *wlr_output = wlr_output_layout_output_at(
- server.output_layout, server.cursor->x, server.cursor->y);
+ struct wlr_output *wlr_output = wlr_output_layout_output_at(server.output_layout,
+ server.cursor->x, server.cursor->y);
struct mwc_output *output = wlr_output->data;
/* set global active workspace and stop moving resizing if there is a fullscreened toplevel */
@@ -182,12 +185,17 @@ cursor_handle_motion(uint32_t time) {
dnd_icons_move(server.cursor->x, server.cursor->y);
}
+ pointer_handle_focus(time, true);
+}
+
+void
+pointer_handle_focus(uint32_t time, bool handle_keyboard_focus) {
/* find something under the pointer and send the event along. */
double sx, sy;
struct wlr_seat *seat = server.seat;
struct wlr_surface *surface = NULL;
- struct mwc_something *something =
- something_at(server.cursor->x, server.cursor->y, &surface, &sx, &sy);
+ struct mwc_something *something = something_at(server.cursor->x, server.cursor->y,
+ &surface, &sx, &sy);
if(something == NULL) {
wlr_cursor_set_xcursor(server.cursor, server.cursor_mgr, "default");
@@ -197,12 +205,23 @@ cursor_handle_motion(uint32_t time) {
return;
}
- if(something->type == MWC_TOPLEVEL) {
- focus_toplevel(something->toplevel);
- } else if(something->type == MWC_LAYER_SURFACE){
- focus_layer_surface(something->layer_surface);
- } else if(something->type == MWC_LOCK_SURFACE) {
- focus_lock_surface(something->lock_surface);
+ if(handle_keyboard_focus) {
+ if(something->type == MWC_TOPLEVEL) {
+ focus_toplevel(something->toplevel);
+ } else if(something->type == MWC_LAYER_SURFACE){
+ focus_layer_surface(something->layer_surface);
+ } else if(something->type == MWC_LOCK_SURFACE) {
+ focus_lock_surface(something->lock_surface);
+ }
+ }
+
+ struct wlr_pointer_constraint_v1 *wlr_constraint =
+ wlr_pointer_constraints_v1_constraint_for_surface(server.pointer_contrains_manager,
+ surface, server.seat);
+ if(wlr_constraint == NULL || wlr_constraint->data == NULL) {
+ server.current_constraint = NULL;
+ } else {
+ constraint_set_as_current(wlr_constraint->data);
}
wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
@@ -212,15 +231,35 @@ cursor_handle_motion(uint32_t time) {
void
server_handle_cursor_motion(struct wl_listener *listener, void *data) {
struct wlr_pointer_motion_event *event = data;
- wlr_cursor_move(server.cursor, &event->pointer->base,
- event->delta_x, event->delta_y);
+
+ constrain_apply_to_move(&event->delta_x, &event->delta_y);
+
+ wlr_relative_pointer_manager_v1_send_relative_motion(server.relative_pointer_manager,
+ server.seat,
+ (uint64_t)event->time_msec * 1000,
+ event->delta_x, event->delta_y,
+ event->unaccel_dx, event->unaccel_dy);
+
+ wlr_cursor_move(server.cursor, &event->pointer->base, event->delta_x, event->delta_y);
cursor_handle_motion(event->time_msec);
}
void
-server_handle_cursor_motion_absolute(
- struct wl_listener *listener, void *data) {
+server_handle_cursor_motion_absolute(struct wl_listener *listener, void *data) {
struct wlr_pointer_motion_absolute_event *event = data;
+
+ double lx, ly;
+ wlr_cursor_absolute_to_layout_coords(server.cursor, &event->pointer->base,
+ event->x, event->y, &lx, &ly);
+
+ double dx = lx - server.cursor->x;
+ double dy = ly - server.cursor->y;
+
+ wlr_relative_pointer_manager_v1_send_relative_motion(server.relative_pointer_manager,
+ server.seat,
+ (uint64_t)event->time_msec * 1000,
+ dx, dy, dx, dy);
+
wlr_cursor_warp_absolute(server.cursor, &event->pointer->base, event->x, event->y);
cursor_handle_motion(event->time_msec);
}
@@ -296,3 +335,103 @@ server_handle_cursor_frame(struct wl_listener *listener, void *data) {
wlr_seat_pointer_notify_frame(server.seat);
}
+/* a lot of the code was stolen of labwc's implemenetation, big props to them */
+void
+server_handle_new_constraint(struct wl_listener *listener, void *data) {
+ struct wlr_pointer_constraint_v1 *wlr_constraint = data;
+
+ /* if there is already a constraint on this surface we ignore it */
+ struct wlr_pointer_constraint_v1 *con;
+ wl_list_for_each(con, &server.pointer_contrains_manager->constraints, link) {
+ if(con != wlr_constraint && con->surface == wlr_constraint->surface) return;
+ }
+
+ struct mwc_pointer_constraint *constraint = calloc(1, sizeof(*constraint));
+ constraint->wlr_pointer_constraint = wlr_constraint;
+ constraint->wlr_pointer_constraint->data = constraint;
+
+ constraint->destroy.notify = constraint_handle_destroy;
+ wl_signal_add(&wlr_constraint->events.destroy, &constraint->destroy);
+}
+
+void
+constraint_remove_current(void) {
+ if(server.current_constraint == NULL) return;
+
+ constraint_move_to_hint(server.current_constraint);
+
+ server.current_constraint = NULL;
+ wlr_pointer_constraint_v1_send_deactivated(server.current_constraint->wlr_pointer_constraint);
+}
+
+void
+constraint_set_as_current(struct mwc_pointer_constraint *constraint) {
+ if(server.current_constraint == constraint) return;
+
+ if(server.current_constraint != NULL) {
+ wlr_pointer_constraint_v1_send_deactivated(server.current_constraint->wlr_pointer_constraint);
+ }
+
+ server.current_constraint = constraint;
+ constraint_move_to_hint(constraint);
+ wlr_pointer_constraint_v1_send_activated(constraint->wlr_pointer_constraint);
+}
+
+void
+constraint_move_to_hint(struct mwc_pointer_constraint *constraint) {
+ struct wlr_pointer_constraint_v1 *wlr_constraint = constraint->wlr_pointer_constraint;
+
+ if(wlr_constraint->current.committed & WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) {
+ double sx = wlr_constraint->current.cursor_hint.x;
+ double sy = wlr_constraint->current.cursor_hint.y;
+ wlr_cursor_warp(server.cursor, NULL,
+ X(server.focused_toplevel) + sx,
+ Y(server.focused_toplevel) + sy);
+
+ /* make sure we are not sending unnecessary surface movements (took from labwc)*/
+ wlr_seat_pointer_warp(server.seat, sx, sy);
+ }
+}
+
+void
+constraint_handle_destroy(struct wl_listener *listener, void *data) {
+ struct mwc_pointer_constraint *constraint = wl_container_of(listener, constraint, destroy);
+
+ wl_list_remove(&constraint->destroy.link);
+ if(server.current_constraint == constraint) {
+ constraint_move_to_hint(constraint);
+ server.current_constraint = NULL;
+ }
+
+ free(constraint);
+}
+
+void
+constrain_apply_to_move(double *dx, double *dy) {
+ if(server.current_constraint == NULL) return;
+
+ if(server.current_constraint->wlr_pointer_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) {
+ *dx = 0;
+ *dy = 0;
+ return;
+ }
+
+ if(server.seat->pointer_state.focused_surface == NULL) return;
+
+ double current_x = server.seat->pointer_state.sx;
+ double current_y = server.seat->pointer_state.sy;
+
+ double constrained_x, constrained_y;
+ if(wlr_region_confine(&server.current_constraint->wlr_pointer_constraint->region,
+ current_x, current_y,
+ current_x + *dx, current_y + *dy,
+ &constrained_x, &constrained_y)) {
+ *dx = constrained_x - current_x;
+ *dy = constrained_y - current_y;
+ }
+}
+
+void
+server_handle_relative_pointer_manager_destroy(struct wl_listener *listener, void *data) {
+ wl_list_remove(&server.relative_pointer_manager_destroy.link);
+}
diff --git a/src/pointer.h b/src/pointer.h
index 3a3edcb..fe62d2d 100644
--- a/src/pointer.h
+++ b/src/pointer.h
@@ -1,8 +1,10 @@
#pragma once
+#include
#include
#include
-#include
+#include
+#include
struct mwc_pointer {
struct wlr_pointer *wlr_pointer;
@@ -12,6 +14,12 @@ struct mwc_pointer {
struct wl_listener destroy;
};
+struct mwc_pointer_constraint {
+ struct wlr_pointer_constraint_v1 *wlr_pointer_constraint;
+
+ struct wl_listener destroy;
+};
+
enum mwc_cursor_mode {
MWC_CURSOR_PASSTHROUGH,
MWC_CURSOR_MOVE,
@@ -33,6 +41,9 @@ server_reset_cursor_mode(void);
void
cursor_handle_motion(uint32_t time);
+void
+pointer_handle_focus(uint32_t time, bool handle_keyboard_focus);
+
void
server_handle_cursor_motion(struct wl_listener *listener, void *data);
@@ -47,3 +58,27 @@ server_handle_cursor_axis(struct wl_listener *listener, void *data);
void
server_handle_cursor_frame(struct wl_listener *listener, void *data);
+
+void
+server_handle_new_constraint(struct wl_listener *listener, void *data);
+
+void
+constraint_handle_destroy(struct wl_listener *listener, void *data);
+
+void
+constrain_apply_to_move(double *dx, double *dy);
+
+void
+constraint_remove_current(void);
+
+void
+constraint_set_as_current(struct mwc_pointer_constraint *constraint);
+
+void
+constraint_move_to_hint(struct mwc_pointer_constraint *constraint);
+
+void
+server_handle_new_relative_pointer(struct wl_listener *listener, void *data);
+
+void
+server_handle_relative_pointer_manager_destroy(struct wl_listener *listener, void *data);
diff --git a/src/popup.c b/src/popup.c
index f15fb2e..2b8932e 100644
--- a/src/popup.c
+++ b/src/popup.c
@@ -27,6 +27,8 @@ server_handle_new_popup(struct wl_listener *listener, void *data) {
if(xdg_popup->parent != NULL) {
struct wlr_xdg_surface *parent = wlr_xdg_surface_try_from_wlr_surface(xdg_popup->parent);
struct wlr_scene_tree *parent_tree = parent->data;
+ if(parent_tree == NULL) return;
+
popup->scene_tree = wlr_scene_xdg_surface_create(parent_tree, xdg_popup->base);
xdg_popup->base->data = popup->scene_tree;
@@ -86,3 +88,17 @@ xdg_popup_handle_destroy(struct wl_listener *listener, void *data) {
free(popup);
}
+
+struct mwc_something *
+popup_get_root_parent(struct mwc_popup *popup) {
+ struct wlr_scene_tree *tree = popup->scene_tree;
+
+ struct mwc_something *something = tree->node.data;
+ while(something == NULL || something->type == MWC_POPUP) {
+ tree = tree->node.parent;
+ something = tree->node.data;
+ }
+
+ return something;
+}
+
diff --git a/src/popup.h b/src/popup.h
index 9b328d3..d776f70 100644
--- a/src/popup.h
+++ b/src/popup.h
@@ -9,6 +9,7 @@
struct mwc_popup {
struct wlr_xdg_popup *xdg_popup;
struct mwc_something something;
+
struct wlr_scene_tree *scene_tree;
struct wl_listener commit;
struct wl_listener destroy;
@@ -23,3 +24,5 @@ xdg_popup_handle_commit(struct wl_listener *listener, void *data);
void
xdg_popup_handle_destroy(struct wl_listener *listener, void *data);
+struct mwc_something *
+popup_get_root_parent(struct mwc_popup *popup);
diff --git a/src/rendering.c b/src/rendering.c
index 52d51ca..3f94b50 100644
--- a/src/rendering.c
+++ b/src/rendering.c
@@ -18,23 +18,24 @@
#include
#include
#include
+#include
extern struct mwc_server server;
void
toplevel_draw_borders(struct mwc_toplevel *toplevel) {
+ if(toplevel->border != NULL && toplevel->fullscreen) {
+ wlr_scene_node_set_enabled(&toplevel->border->node, false);
+ return;
+ }
+
uint32_t border_width = server.config->border_width;
uint32_t border_radius = server.config->border_radius;
enum corner_location border_radius_location = server.config->border_radius_location;
- float *border_color = toplevel->fullscreen
- ? (float[4]){ 0, 0, 0, 0 }
- : toplevel == server.focused_toplevel
- ? server.config->active_border_color
- : server.config->inactive_border_color;
-
- uint32_t width, height;
- toplevel_get_actual_size(toplevel, &width, &height);
+ float *border_color = toplevel == server.focused_toplevel
+ ? server.config->active_border_color
+ : server.config->inactive_border_color;
if(toplevel->border == NULL) {
toplevel->border = wlr_scene_rect_create(toplevel->scene_tree, 0, 0, border_color);
@@ -43,12 +44,16 @@ toplevel_draw_borders(struct mwc_toplevel *toplevel) {
wlr_scene_rect_set_corner_radius(toplevel->border, border_radius, border_radius_location);
}
- wlr_scene_rect_set_size(toplevel->border, width + 2 * border_width,
- height + 2 * border_width);
+ wlr_scene_node_set_enabled(&toplevel->border->node, true);
+
+ uint32_t width, height;
+ toplevel_get_actual_size(toplevel, &width, &height);
+
+ wlr_scene_rect_set_size(toplevel->border, width + 2 * border_width, height + 2 * border_width);
struct clipped_region clipped_region = {
.area = { border_width, border_width, width, height },
- .corner_radius = max(border_radius - border_width, 0),
+ .corner_radius = max((int32_t)border_radius - (int32_t)border_width, 0),
.corners = border_radius_location,
};
wlr_scene_rect_set_clipped_region(toplevel->border, clipped_region);
@@ -56,67 +61,81 @@ toplevel_draw_borders(struct mwc_toplevel *toplevel) {
wlr_scene_rect_set_color(toplevel->border, border_color);
}
+struct iter_scene_buffer_apply_blur_args {
+ int32_t root_x;
+ int32_t root_y;
+ struct wlr_box geometry;
+ uint32_t width;
+ uint32_t height;
+ double width_scale;
+ double height_scale;
+ double opacity;
+ uint32_t border_radius;
+};
+
void
-scene_buffer_apply_effects(struct wlr_scene_buffer *buffer,
- uint32_t width, uint32_t height,
- uint32_t geometry_width, uint32_t geometry_height,
- double opacity, uint32_t border_radius, bool is_popup) {
- wlr_scene_buffer_set_opacity(buffer, opacity);
+iter_scene_buffer_apply_effects(struct wlr_scene_buffer *buffer,
+ int lx, int ly, void *data) {
+ struct iter_scene_buffer_apply_blur_args *args = data;
- /* we dont blur or round popups */
- if(is_popup) return;
+ wlr_scene_buffer_set_opacity(buffer, args->opacity);
- /* some clients, notably firefox, have basically everything as subsurfaces.
- * this is a hacky way that will (usually) detect things that are not a main window and skip them */
- if(buffer->buffer == NULL) return;
+ struct wlr_scene_surface *scene_surface = wlr_scene_surface_try_from_buffer(buffer);
+ if(scene_surface == NULL) return;
- uint32_t buffer_width = buffer->buffer->width;
- uint32_t buffer_height = buffer->buffer->height;
+ struct wlr_surface *surface = scene_surface->surface;
- if(buffer_width < 0.75 * geometry_width || buffer_height < 0.75 * geometry_height) return;
+ uint32_t surface_width = surface->current.width;
+ uint32_t surface_height = surface->current.height;
- if(server.config->blur) {
- wlr_scene_buffer_set_backdrop_blur(buffer, true);
- wlr_scene_buffer_set_backdrop_blur_optimized(buffer, true);
- wlr_scene_buffer_set_backdrop_blur_ignore_transparent(buffer, true);
- } else {
- wlr_scene_buffer_set_backdrop_blur(buffer, false);
+ surface_width *= args->width_scale;
+ surface_height *= args->height_scale;
+
+ wlr_scene_buffer_set_dest_size(buffer, surface_width, surface_height);
+
+ /* we dont round or blur popups */
+ if(wlr_xdg_popup_try_from_wlr_surface(surface) != NULL) return;
+
+ int32_t x = lx - args->root_x;
+ int32_t y = ly - args->root_y;
+
+ enum corner_location corners = 0;
+
+ if(server.config->border_radius_location & CORNER_LOCATION_TOP_LEFT
+ && x == 0
+ && y == 0) {
+ corners |= CORNER_LOCATION_TOP_LEFT;
}
- wlr_scene_buffer_set_corner_radius(buffer, border_radius,
- server.config->border_radius_location);
+ if(server.config->border_radius_location & CORNER_LOCATION_BOTTOM_LEFT
+ && x == 0
+ && y + surface->current.height == args->geometry.height) {
+ corners |= CORNER_LOCATION_BOTTOM_LEFT;
+ }
- if(width != 0) {
- wlr_scene_buffer_set_dest_size(buffer, width, height);
+ if(server.config->border_radius_location & CORNER_LOCATION_TOP_RIGHT
+ && x + surface->current.width == args->geometry.width
+ && y == 0) {
+ corners |= CORNER_LOCATION_TOP_RIGHT;
}
-}
-void
-scene_tree_apply_effects(struct wlr_scene_tree *tree,
- uint32_t width, uint32_t height,
- uint32_t geometry_width, uint32_t geometry_height,
- double opacity, uint32_t border_radius, bool is_popup) {
- struct wlr_scene_node *node;
- wl_list_for_each(node, &tree->children, link) {
- switch(node->type) {
- case WLR_SCENE_NODE_BUFFER: {
- struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
- scene_buffer_apply_effects(buffer, width, height, geometry_width, geometry_height,
- opacity, border_radius, is_popup);
- break;
- }
- case WLR_SCENE_NODE_TREE: {
- struct wlr_scene_tree *t = wlr_scene_tree_from_node(node);
- struct mwc_something *something = t->node.data;
- if(something != NULL) {
- is_popup = something->type == MWC_POPUP;
- }
- scene_tree_apply_effects(t, width, height, geometry_width, geometry_height,
- opacity, border_radius, is_popup);
- break;
- }
- default: break;
- }
+ if(server.config->border_radius_location & CORNER_LOCATION_BOTTOM_RIGHT
+ && x + surface->current.width == args->geometry.width
+ && y + surface->current.height == args->geometry.height) {
+ corners |= CORNER_LOCATION_BOTTOM_RIGHT;
+ }
+
+ wlr_scene_buffer_set_corner_radius(buffer, args->border_radius, corners);
+
+ /* we dont blur subsurfaces */
+ if(wlr_subsurface_try_from_wlr_surface(surface) != NULL) return;
+
+ if(server.config->blur) {
+ wlr_scene_buffer_set_backdrop_blur(buffer, true);
+ wlr_scene_buffer_set_backdrop_blur_optimized(buffer, true);
+ wlr_scene_buffer_set_backdrop_blur_ignore_transparent(buffer, false);
+ } else {
+ wlr_scene_buffer_set_backdrop_blur(buffer, false);
}
}
@@ -135,13 +154,25 @@ toplevel_apply_effects(struct mwc_toplevel *toplevel) {
? 0
: max(server.config->border_radius - server.config->border_width, 0);
- uint32_t width = toplevel->animation.running ? toplevel->animation.current.width : 0;
- uint32_t height = toplevel->animation.running ? toplevel->animation.current.height : 0;
-
struct wlr_box geometry = toplevel_get_geometry(toplevel);
-
- scene_tree_apply_effects(toplevel->scene_tree, width, height, geometry.width, geometry.height,
- opacity, border_radius, false);
+
+ uint32_t width, height;
+ toplevel_get_actual_size(toplevel, &width, &height);
+
+ struct iter_scene_buffer_apply_blur_args args = {
+ .root_x = toplevel->scene_tree->node.x,
+ .root_y = toplevel->scene_tree->node.y,
+ .geometry = toplevel_get_geometry(toplevel),
+ .width = width,
+ .height = height,
+ .width_scale = (double)width / geometry.width,
+ .height_scale = (double)height / geometry.height,
+ .opacity = opacity,
+ .border_radius = border_radius,
+ };
+
+ wlr_scene_node_for_each_buffer(&toplevel->scene_tree->node,
+ iter_scene_buffer_apply_effects, &args);
}
void
@@ -244,7 +275,7 @@ toplevel_draw_shadow(struct mwc_toplevel *toplevel) {
.x = server.config->shadows_position.x,
.y = server.config->shadows_position.y,
.width = width + 2 * delta,
- .height = height + 2 *delta,
+ .height = height + 2 * delta,
};
struct wlr_box intersection_box;
@@ -270,6 +301,7 @@ toplevel_draw_shadow(struct mwc_toplevel *toplevel) {
}
wlr_scene_node_set_enabled(&toplevel->shadow->node, true);
+
wlr_scene_shadow_set_size(toplevel->shadow, shadow_box.width, shadow_box.height);
wlr_scene_shadow_set_clipped_region(toplevel->shadow, clipped_region);
}
@@ -286,7 +318,9 @@ toplevel_draw_frame(struct mwc_toplevel *toplevel) {
toplevel->current.x, toplevel->current.y);
}
- toplevel_draw_borders(toplevel);
+ if(server.config->border_width > 0) {
+ toplevel_draw_borders(toplevel);
+ }
if(server.config->shadows) {
toplevel_draw_shadow(toplevel);
}
diff --git a/src/something.c b/src/something.c
index 1076ce2..fc88e17 100644
--- a/src/something.c
+++ b/src/something.c
@@ -5,7 +5,6 @@
#include "mwc.h"
#include "layer_surface.h"
#include "session_lock.h"
-#include "wlr/util/log.h"
#include
#include
@@ -15,24 +14,22 @@ extern struct mwc_server server;
struct mwc_something *
root_parent_of_surface(struct wlr_surface *wlr_surface) {
- struct wlr_surface *root_wlr_surface =
- wlr_surface_get_root_surface(wlr_surface);
+ struct wlr_surface *root_surface = wlr_surface_get_root_surface(wlr_surface);
struct wlr_scene_tree *tree;
- struct wlr_xdg_surface *xdg_surface =
- wlr_xdg_surface_try_from_wlr_surface(root_wlr_surface);
+ struct wlr_xdg_surface *xdg_surface = wlr_xdg_surface_try_from_wlr_surface(root_surface);
if(xdg_surface != NULL) {
tree = xdg_surface->data;
} else {
struct wlr_layer_surface_v1 *wlr_layer_surface =
- wlr_layer_surface_v1_try_from_wlr_surface(root_wlr_surface);
+ wlr_layer_surface_v1_try_from_wlr_surface(root_surface);
if(wlr_layer_surface != NULL) {
struct mwc_layer_surface *layer_surface = wlr_layer_surface->data;
tree = layer_surface->scene->tree;
} else {
struct wlr_session_lock_surface_v1 *wlr_lock_surface =
- wlr_session_lock_surface_v1_try_from_wlr_surface(root_wlr_surface);
+ wlr_session_lock_surface_v1_try_from_wlr_surface(root_surface);
if(wlr_lock_surface != NULL) {
struct mwc_lock_surface *lock_surface = wlr_lock_surface->data;
tree = lock_surface->scene_tree;
diff --git a/src/toplevel.c b/src/toplevel.c
index c4f59f4..17f3996 100644
--- a/src/toplevel.c
+++ b/src/toplevel.c
@@ -6,12 +6,14 @@
#include "ipc.h"
#include "layout.h"
#include "mwc.h"
+#include "popup.h"
#include "rendering.h"
#include "something.h"
#include "workspace.h"
#include "output.h"
#include "helpers.h"
#include "layer_surface.h"
+#include "pointer.h"
#include
#include
@@ -23,6 +25,7 @@
#include
#include
#include
+#include
#include
#include
@@ -632,6 +635,11 @@ cursor_jump_focused_toplevel(void) {
wlr_cursor_warp(server.cursor, NULL,
toplevel->scene_tree->node.x + geo_box.x + toplevel->current.width / 2.0,
toplevel->scene_tree->node.y + geo_box.y + toplevel->current.height / 2.0);
+
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ pointer_handle_focus(now.tv_sec * 1000 + now.tv_nsec / 1000, false);
}
void
@@ -1058,3 +1066,50 @@ toplevel_tiled_insert_into_layout(struct mwc_toplevel *toplevel, uint32_t x, uin
}
}
}
+
+void
+xdg_activation_handle_token_destroy(struct wl_listener *listener, void *data) {
+ struct mwc_token *token_data = wl_container_of(listener, token_data, destroy);
+ wl_list_remove(&token_data->destroy.link);
+
+ free(token_data);
+}
+
+void
+xdg_activation_handle_new_token(struct wl_listener *listener, void *data) {
+ struct wlr_xdg_activation_token_v1 *wlr_token = data;
+ if(wlr_token->surface == NULL || wlr_token->seat == NULL) return;
+
+ struct mwc_token *token = calloc(1, sizeof(*token));
+ token->wlr_token = wlr_token;
+ wlr_token->data = token;
+
+ token->destroy.notify = xdg_activation_handle_token_destroy;
+ wl_signal_add(&wlr_token->events.destroy, &token->destroy);
+}
+
+void
+xdg_activation_handle_request(struct wl_listener *listener, void *data) {
+ const struct wlr_xdg_activation_v1_request_activate_event *event = data;
+
+ struct wlr_xdg_surface *xdg_surface = wlr_xdg_surface_try_from_wlr_surface(event->surface);
+ if(xdg_surface == NULL) return;
+
+ struct wlr_scene_tree *tree = xdg_surface->data;
+ /* this happens if the toplevel has not been mapped yet. anyway it does not make sense to
+ * request that i activate this surface that is not on the screen */
+ if(tree == NULL) return;
+
+ struct mwc_something *something = tree->node.data;
+ if(something == NULL) return;
+
+ if(something->type == MWC_POPUP) {
+ something = popup_get_root_parent(something->popup);
+ }
+
+ if(something->type != MWC_TOPLEVEL) return;
+
+ struct mwc_toplevel *toplevel = something->toplevel;
+
+ focus_toplevel(toplevel);
+}
diff --git a/src/toplevel.h b/src/toplevel.h
index 0bd1a00..535413a 100644
--- a/src/toplevel.h
+++ b/src/toplevel.h
@@ -56,8 +56,14 @@ struct mwc_toplevel {
struct wl_listener set_title;
};
-#define X(t) (t)->scene_tree->node.x
-#define Y(t) (t)->scene_tree->node.y
+#define X(t) ((t)->scene_tree->node.x)
+#define Y(t) ((t)->scene_tree->node.y)
+
+struct mwc_token {
+ struct wlr_xdg_activation_token_v1 *wlr_token;
+
+ struct wl_listener destroy;
+};
void
toplevel_get_actual_size(struct mwc_toplevel *toplevel, uint32_t *width, uint32_t *height);
@@ -171,3 +177,9 @@ get_pointer_focused_toplevel(void);
void
toplevel_recheck_opacity_rules(struct mwc_toplevel *toplevel);
+
+void
+xdg_activation_handle_new_token(struct wl_listener *listener, void *data);
+
+void
+xdg_activation_handle_request(struct wl_listener *listener, void *data);