diff --git a/.build.yml b/.build.yml
index 0bfafc9..b4f1396 100644
--- a/.build.yml
+++ b/.build.yml
@@ -3,20 +3,27 @@
#
image: archlinux
packages:
- - meson
- - wayland
- - wayland-protocols
+ - clang
+ - gcc
+ - libevdev
- libinput
- libxkbcommon
- libxml2
- - wlroots
+ - meson
+ - wayland
+ - wayland-protocols
+ - wlroots-git
- xorg-server-xwayland
sources:
- https://github.com/wizbright/waybox
tasks:
- setup: |
cd waybox
- meson setup build
+ CC=gcc meson setup build-gcc
- build: |
cd waybox
- ninja -C build
+ ninja -C build-gcc
+ - clang: |
+ cd waybox
+ CC=clang meson setup build-clang
+ ninja -C build-clang
diff --git a/.editorconfig b/.editorconfig
index ba4f2c4..516f2a0 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -14,7 +14,7 @@ block_comment_start = /*
block_comment_end = */
curly_bracket_next_line = false
-max_line_length = 80
+max_line_length = 100
spaces_around_brackets = outside
spaces_around_operators = true
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 061bdbc..8e49e0b 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -12,18 +12,18 @@ on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
- container: archlinux:base-devel
+ container: alpine:edge
steps:
- name: packages
run: |
- pacman-key --init
- pacman -Syu --noconfirm
- pacman -S --noconfirm git meson libxkbcommon libinput libxml2 wayland wayland-protocols wlroots xorg-server-xwayland
- # actions/checkout@v2 clones the repository
- - uses: actions/checkout@v2
- - name: setup
+ apk add clang gcc libevdev-dev libinput-dev libxkbcommon-dev libxml2-dev meson musl-dev wayland-dev wayland-protocols wlroots wlroots-dev xwayland
+ # actions/checkout@v4 clones the repository
+ - uses: actions/checkout@v4
+ - name: build-gcc
run: |
- meson setup build
- - name: build
+ CC=gcc meson setup build-gcc
+ ninja -C build-gcc
+ - name: build-clang
run: |
- ninja -C build
+ CC=clang meson setup build-clang
+ ninja -C build-clang
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index edd223a..bddb657 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -136,7 +136,7 @@ Try to break the line in the place which you think is the most appropriate.
### Line Length
-Try to keep your lines under 80 columns, but you can go up to 100 if it
+Try to keep your lines under 100 columns, but you can break this rule if it
improves readability. Don't break lines indiscriminately, try to find nice
breaking points so your code is easy to read.
diff --git a/README.md b/README.md
index 384f1de..ced8dbb 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,10 @@
# Waybox
-An Openbox clone on Wayland (WIP)
+
+A \*box-style (minimalist) Wayland compositor modeled largely on Openbox (WIP)
### Goals
-The main goal of this project is to provide a similar feel to Openbox but on Wayland
+
+The main goal of this project is to provide a similar feel to \*box-style window managers but on Wayland
### Contributing
@@ -11,14 +13,15 @@ contributing.](https://github.com/wizbright/waybox/blob/master/CONTRIBUTING.md)
### Dependencies
-* [Meson](https://mesonbuild.com/)
-* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots/)
-* [libxml2](http://xmlsoft.org/)
+* [Meson](https://mesonbuild.com/) or [muon](http://muon.build)
* [Wayland](https://wayland.freedesktop.org/)
+* [libevdev](https://www.freedesktop.org/wiki/Software/libevdev/)
+* [libinput](http://www.freedesktop.org/wiki/Software/libinput)
+* [libxml2](http://xmlsoft.org/)
+* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots/)
* [xkbcommon](https://xkbcommon.org/)
### Build instructions
-
```
meson setup build
cd build
@@ -27,6 +30,30 @@ ninja
After that, you should have an executable as waybox/waybox
+For those who don't want to use a Python-based build system, it's also possible
+to use muon instead of Meson.
+
+### Screenshots
+
+
+
+
+
+
+
+### Useful Programs
+
+Because \*box-style compositors are minimalist, most functionality is left to external programs. As such, Waybox only functions as a box in which you can put whatever you need. Here are some useful programs to complement Waybox if you desire:
+
+* Panel: You can use [Waybar](https://github.com/Alexays/Waybar) or [yambar](https://codeberg.org/dnkl/yambar), similar to tint2 or fbpanel in Openbox or Fluxbox.
+* Dock: You can use [Cairo Dock](https://www.glx-dock.org/) just like you did on Openbox. There's also a [port with Wayland-specific enhancements](https://github.com/dkondor/cairo-dock-core/) that you may want to try. A much more compact option is [LavaLauncher](https://sr.ht/~leon_plickat/LavaLauncher/), but it's much harder to configure.
+* Wallpaper utility: There are various utilities to set your wallpaper, each with their own advantages, including [wpaperd](https://github.com/danyspin97/wpaperd) (can select a random wallpaper from a directory), [swaybg](https://github.com/swaywm/swaybg) (can set the background color as well well as a wallpaper), and [hyprpaper](https://github.com/hyprwm/hyprpaper) (can change the wallpaper dynamically during runtime through IPC).
+* Notification client: [mako](https://wayland.emersion.fr/mako/)
+* [wl-clipboard](https://wayland.emersion.fr/mako/): Access the clipboard in scripts (also used by [neovim](https://neovim.io/))
+* Screenshots: [grim](https://git.sr.ht/~emersion/grim) and [slurp](https://github.com/emersion/slurp)
+* Screen recording: [wf-recorder](https://github.com/ammen99/wf-recorder)
+* Menu support: [rofi-wayland](https://github.com/lbonn/rofi)
+
### Contact
I can be found as wiz on Rizon and wizbright on Libera.
Join [#waybox](https://libera.chat/guides/connect) for discussion
diff --git a/data/autostart b/data/autostart
index 242f5be..f04a243 100644
--- a/data/autostart
+++ b/data/autostart
@@ -23,15 +23,48 @@ cairo-dock &
mako &
# Load a random wallpaper
-oldifs=$IFS
-IFS=:
-data_dirs=${XDG_DATA_DIRS:-${datadir:-/usr/share}}:${XDG_DATA_HOME:-~/.local/share}
-for data_dir in $data_dirs;
-do
- wpdir="$data_dir/wallpapers"
- test -d "$wpdir" && \
- find $wpdir -name '*.jpg' -o -name '*.png' -o -name '*.svg'
-done | (shuf -n 1 || tail -n 1) | xargs swaybg -m fill -i &
-IFS=$oldifs
+get_random_wallpaper()
+{
+ oldifs=$IFS
+ IFS=:
+ data_dirs=${XDG_DATA_DIRS:-${datadir:-/usr/share}}:${XDG_DATA_HOME:-~/.local/share}
+ for data_dir in $data_dirs;do
+ IFS=$oldifs
+ wpdir="$data_dir/wallpapers"
+ test -d "$wpdir" && \
+ find $wpdir -name '*.jpg' -o -name '*.png' -o -name '*.svg'
+ done | (shuf -n 1 || tail -n 1)
+}
+
+load_wallpaper() {
+ uses_wayland_info=0
+ if (which hyprpaper && which socat && (test "$uses_wayland_info" = "0" || which wayland-info)) >/dev/null 2>&1; then
+ hyprpaper &
+ HYPRPAPER_SOCKET="${XDG_RUNTIME_DIR:-"/run/user/$(id -u)"}"/hypr/.hyprpaper.sock
+ # Change the wallpaper every hour
+ while test -S "$HYPRPAPER_SOCKET"; do
+ if test "$uses_wayland_info" != "0";
+ then
+ current_output=$(wayland-info -i wl_output | \
+ grep 'name:' | tail -n 1 | cut -d : -f 2 | tr -d ' ')
+ fi
+ random_wallpaper="$(get_random_wallpaper)"
+ for cmd in "preload $random_wallpaper" \
+ "wallpaper $current_output,$random_wallpaper" \
+ 'unload all';
+ do
+ printf "$cmd" | socat UNIX-CONNECT:"$HYPRPAPER_SOCKET" -
+ done
+ [ $? -ne 0 ] && rm -f $HYPRPAPER_SOCKET
+ sleep 60m
+ done
+ elif which swaybg >/dev/null 2>&1; then
+ get_random_wallpaper | xargs swaybg -c '#303030' -m fill -i &
+ elif which wpaperd >/dev/null 2>&1; then
+ wpaperd &
+ fi
+}
+
+load_wallpaper &
# vim: ft=sh
diff --git a/data/environment b/data/environment
index a9953b8..9973533 100644
--- a/data/environment
+++ b/data/environment
@@ -30,17 +30,25 @@ if test -n "$DISPLAY" && test -d /etc/X11/xinit/xinitrc.d;
then
for f in /etc/X11/xinit/xinitrc.d/*.sh;
do
- test -f "$f" && . "$f"
+ test -r "$f" && . "$f"
done
fi
-# Get the preferred terminal from GNOME (if you use mostly GTK apps)
-TERMINAL=$(gsettings get org.gnome.desktop.default-applications.terminal exec | tr -d \')
+# Emerging standard, used by i.a. modern GTK versions instead of the obsolete
+# org.gnome.default-applications.terminal.exec gsetting (which is now ignored)
+# https://github.com/Vladimir-csp/xdg-terminal-exec (or create a symlink to
+# your preferred terminal emulator)
+TERMINAL=xdg-terminal-exec
# Get the preferred terminal from KDE (if you use mostly Qt apps)
#TERMINAL=$(kreadconfig5 --file kdeglobals --group General --key TerminalApplication --default "konsole")
# Or just set it hard:
#TERMINAL=kitty
export TERMINAL
-# Use autostart scripts for these environments
-export WB_AUTOSTART_ENVIRONMENT=GNOME:KDE
+if command -v xdg-autostart >/dev/null 2>&1;
+then
+ export XDG_CURRENT_DESTKOP=OPENBOX
+else
+ # Use autostart scripts for these environments
+ export WB_AUTOSTART_ENVIRONMENT=GNOME:KDE:OPENBOX
+fi
diff --git a/data/menu b/data/menu
new file mode 100644
index 0000000..a7f8a4f
--- /dev/null
+++ b/data/menu
@@ -0,0 +1,71 @@
+#!/bin/env python
+
+import os, subprocess, sys
+import shlex
+import xml.dom.minidom as dom
+
+def get_waybox_pid(name='waybox'):
+ lines = subprocess.Popen(['pgrep', '-x', name], stdout=subprocess.PIPE).stdout.read().splitlines()
+ if lines:
+ return int(lines[0])
+ else:
+ return 0
+
+def get_menu_items(menu, indent, expand):
+ menu_id = menu.getAttribute('id')
+ nonselectable = str(bool(menu_id == '')).lower()
+ print("%s%s ⇢\0info\x1f{\"menu\": \"%s\"}\x1fnonselectable\x1f%s" % (indent, menu.getAttribute('label').replace('_', '') or menu_id, menu_id, nonselectable))
+ indent = indent + " "
+ children = menu.childNodes
+ for i in range(0, children.length):
+ if expand:
+ if children[i].nodeType == dom.Node.ELEMENT_NODE and children[i].tagName == 'item':
+ action = children[i].getElementsByTagName('action')[0].getAttribute('name')
+ cmd = ""
+ icon = children[i].getAttribute('icon')
+ label = children[i].getAttribute('label').replace('_', '')
+ if action == 'Execute':
+ cmd = children[i].getElementsByTagName('execute')[0].firstChild.nodeValue
+ elif action == 'Exit':
+ cmd = "kill -s SIGTERM %d" % get_waybox_pid()
+ elif action == 'Reconfigure':
+ cmd = "kill -s SIGUSR2 %d" % get_waybox_pid()
+ print("%s\0info\x1f{\"exec\": \"%s\"}\x1ficon\x1f%s\x1fdisplay\x1f%s %s" % (cmd, cmd.replace('"', '\\"'), icon, indent, label))
+ elif children[i].nodeType == dom.Node.ELEMENT_NODE and children[i].tagName == 'menu':
+ if children[i].getAttribute('execute') == "":
+ get_menu_items(children[i], indent, os.getenv('ROFI_RETV') is None)
+ else:
+ try:
+ import xml.parsers.expat
+ lines = subprocess.Popen(shlex.split(children[i].getAttribute('execute')), stdout=subprocess.PIPE).stdout.read()
+ pipe = dom.parseString(lines)
+ pipes = pipe.getElementsByTagName('menu')
+ for j in range(0, pipes.length):
+ get_menu_items(pipes[j], indent, True)
+ # If a script doesn't function correctly anymore, don't stop rendering the menu
+ except xml.parsers.expat.ExpatError:
+ pass
+
+menu_xml = os.getenv("WB_MENU_XML") or "menu.xml"
+document = dom.parse(menu_xml)
+
+print("\0message\x1f%s" % os.path.abspath(menu_xml))
+if not (info := os.getenv('ROFI_INFO')) is None:
+ import json
+ obj = json.JSONDecoder().decode(s=info)
+ if 'exec' in info:
+ subprocess.Popen(shlex.split(obj['exec']), stdout=subprocess.PIPE)
+ sys.exit(0)
+ elif 'menu' in obj:
+ # Eh document.getElementById() seems not to work
+ menus = document.getElementsByTagName('menu')
+ for i in range(0, menus.length):
+ if menus[i].getAttribute('id') == obj['menu']:
+ menu = menus[i]
+ if not menu is None:
+ get_menu_items(menu, "", True)
+ print("---\0nonselectable\x1ftrue")
+ break
+
+root_menu = document.getElementsByTagName('menu')[0]
+get_menu_items(root_menu, "", True)
diff --git a/data/meson.build b/data/meson.build
index 7f6f74a..574cb0a 100644
--- a/data/meson.build
+++ b/data/meson.build
@@ -1,4 +1,5 @@
cfdata = configuration_data()
+cfdata.set('bindir', get_option('prefix') / get_option('bindir'))
cfdata.set('libexecdir', get_option('prefix') / get_option('libexecdir'))
cfdata.set('localedir', get_option('prefix') / get_option('localedir'))
cfdata.set('sysconfdir', get_option('prefix') / get_option('sysconfdir'))
@@ -33,8 +34,21 @@ install_data(
install_mode: 'rw-r--r--',
)
+install_data(
+ 'waybox.svg',
+ install_dir: get_option('prefix') / get_option('datadir') / 'icons',
+ install_mode: 'rw-r--r--',
+ )
+
install_data(
'waybox.desktop',
install_dir: get_option('prefix') / get_option('datadir') + '/wayland-sessions',
install_mode: 'rw-r--r--',
)
+
+install_data(
+ 'menu',
+ install_dir: get_option('prefix') / get_option('bindir'),
+ install_mode: 'rwxr-xr-x',
+ rename: ['waybox-menu']
+ )
diff --git a/data/rc.xml b/data/rc.xml
index d3bb8fb..818c8f7 100644
--- a/data/rc.xml
+++ b/data/rc.xml
@@ -9,15 +9,15 @@
0
-
+
-
+ -->
@@ -42,25 +42,16 @@
-
-
-
-
-
+
+
-
-
-
-
-
+
+
-
-
-
-
-
+
+
@@ -78,6 +69,12 @@
grim -g "$(slurp -d)" "$(xdg-user-dir PICTURES)/$(date +'%Y-%m-%d-%H%M%S_grim_sel.png')"
+
+
+
+ rofi -show waybox-menu
+
+
obrun l
@@ -115,4 +112,26 @@
+
+
+
+
+
diff --git a/data/waybox.desktop b/data/waybox.desktop
index 49355c4..33a1e02 100644
--- a/data/waybox.desktop
+++ b/data/waybox.desktop
@@ -4,4 +4,5 @@ Comment=An Openbox-like compositor for Wayland
Comment[de]=Ein Wayland-Compositor ähnlich zu Openbox
Comment[eo]=Komponilo de Wayland, kiu similas je Openbox
Exec=waybox
+Icon=waybox
Type=Application
diff --git a/data/waybox.sh b/data/waybox.sh
index cb87f34..98865e8 100644
--- a/data/waybox.sh
+++ b/data/waybox.sh
@@ -60,7 +60,12 @@ then
fi
# And the XDG autostart script
-if test -x $WB_USER_CONF_DIR/xdg-autostart;
+if command -v xdg-autostart >/dev/null 2>&1;
+then
+ # Probably what you want instead of my simple shell script.
+ # https://gitlab.com/somini/xdg-autostart/
+ WB_XDG_AUTOSTART="xdg-autostart"
+elif test -x $WB_USER_CONF_DIR/xdg-autostart;
then
WB_XDG_AUTOSTART=$WB_USER_CONF_DIR/xdg-autostart;
elif test -x $WB_SYS_CONF_DIR/xdg-autostart;
@@ -72,6 +77,26 @@ then
WB_XDG_AUTOSTART="@libexecdir@/openbox-autostart OPENBOX";
fi
+if test -f $WB_USER_CONF_DIR/menu.xml;
+then
+ WB_MENU_XML=$WB_USER_CONF_DIR/menu.xml
+elif test -f $WB_SYS_CONF_DIR/menu.xml;
+then
+ WB_MENU_XML=$WB_SYS_CONF_DIR/menu.xml
+elif test -f $OB_USER_CONF_DIR/menu.xml;
+then
+ _ "WARNING: Using files from Openbox. These may not work correctly."
+ WB_MENU_XML=$OB_USER_CONF_DIR/menu.xml
+elif test -f $OB_SYS_CONF_DIR/menu.xml;
+then
+ _ "WARNING: Using files from Openbox. These may not work correctly."
+ WB_MENU_XML=$OB_SYS_CONF_DIR/menu.xml;
+else
+ _ "ERROR: No menu file found." >&2
+ exit 1
+fi
+export WB_MENU_XML
+
if test -f $WB_USER_CONF_DIR/rc.xml;
then
WB_RC_XML=$WB_USER_CONF_DIR/rc.xml
@@ -94,9 +119,11 @@ export WB_RC_XML
if which dbus-launch >/dev/null 2>&1;
then
- DBUS_LANCH="dbus-launch --exit-with-session"
+ DBUS_LAUNCH="dbus-launch --exit-with-session"
fi
+export PATH=@bindir@:$PATH
+
# No need to export these to Waybox
unset TEXTDOMAIN TEXTDOMAINDIR
$DBUS_LAUNCH @libexecdir@/waybox --startup "${WB_AUTOSTART:-true}; ${WB_XDG_AUTOSTART:-true}" "$@"
diff --git a/data/waybox.svg b/data/waybox.svg
new file mode 100644
index 0000000..4146670
--- /dev/null
+++ b/data/waybox.svg
@@ -0,0 +1,14 @@
+
+
+
diff --git a/data/xdg-autostart b/data/xdg-autostart
index 5c11397..3ea0d55 100644
--- a/data/xdg-autostart
+++ b/data/xdg-autostart
@@ -66,6 +66,8 @@ do
then
show_in=0
break 2
+ else
+ show_in=1
fi
done
done
@@ -88,7 +90,7 @@ do
then
# Don't run the Exec key if a non-empty TryExec command can't be found
TRY_EXEC=$(cat "$f" | grep '^TryExec\s*=\s*\S' | sed -e 's/^TryExec\s*=\s*//g');
- if test -n "$TRY_EXEC" && ! which $TRY_EXEC;
+ if test -n "$TRY_EXEC" && ! command -v $TRY_EXEC >/dev/null 2>&1;
then
continue
fi
diff --git a/include/waybox/output.h b/include/waybox/output.h
index b11bcfd..b69e516 100644
--- a/include/waybox/output.h
+++ b/include/waybox/output.h
@@ -1,10 +1,10 @@
#ifndef _WB_OUTPUT_H
#define _WB_OUTPUT_H
-#include
#include
#include "waybox/server.h"
+#include
struct wb_output {
struct wlr_output *wlr_output;
@@ -18,42 +18,19 @@ struct wb_output {
struct wlr_scene_tree *shell_top;
} layers;
-#if ! WLR_CHECK_VERSION(0, 17, 0)
- /* DEPRECATED: Use a tool like swaybg instead */
- struct wlr_scene_rect *background;
-#endif
+ bool gamma_lut_changed;
struct wlr_box geometry;
struct wl_listener destroy;
struct wl_listener frame;
+ struct wl_listener request_state;
struct wl_list link;
};
-struct wb_view {
- struct wl_list link;
- struct wb_server *server;
- struct wlr_xdg_toplevel *xdg_toplevel;
- struct wlr_scene_tree *scene_tree;
-
- struct wlr_xdg_toplevel_decoration_v1 *decoration;
-
- struct wl_listener map;
- struct wl_listener unmap;
- struct wl_listener destroy;
- struct wl_listener new_popup;
- struct wl_listener request_fullscreen;
- struct wl_listener request_maximize;
- struct wl_listener request_minimize;
- struct wl_listener request_move;
- struct wl_listener request_resize;
-
- struct wlr_box geometry;
- struct wlr_box previous_geometry;
-};
-
-void output_frame_notify(struct wl_listener* listener, void *data);
-void output_destroy_notify(struct wl_listener* listener, void *data);
-void new_output_notify(struct wl_listener* listener, void *data);
+void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data);
+void output_frame_notify(struct wl_listener *listener, void *data);
+void output_destroy_notify(struct wl_listener *listener, void *data);
+void init_output(struct wb_server *server);
#endif /* output.h */
diff --git a/include/waybox/server.h b/include/waybox/server.h
index 9f68fc3..534ac20 100644
--- a/include/waybox/server.h
+++ b/include/waybox/server.h
@@ -12,6 +12,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -22,6 +23,8 @@
#include
#include
+#include
+
#ifdef USE_NLS
# include
# include
@@ -34,21 +37,27 @@
#include "waybox/cursor.h"
#include "decoration.h"
#include "layer_shell.h"
-#include "waybox/output.h"
+#include "waybox/xdg_shell.h"
#include "waybox/seat.h"
struct wb_server {
struct wl_display *wl_display;
+ struct wl_event_loop *wl_event_loop;
struct wlr_allocator *allocator;
struct wlr_backend *backend;
struct wlr_compositor *compositor;
+ struct wlr_gamma_control_manager_v1 *gamma_control_manager;
+ struct wlr_idle_inhibit_manager_v1 *idle_inhibit_manager;
struct wlr_idle_notifier_v1 *idle_notifier;
struct wlr_output_layout *output_layout;
struct wlr_xdg_output_manager_v1 *output_manager;
struct wlr_renderer *renderer;
struct wlr_scene *scene;
+ struct wlr_scene_output_layout *scene_layout;
+ struct wlr_session *session;
struct wlr_subcompositor *subcompositor;
+ struct wlr_output_manager_v1 *wlr_output_manager;
struct wb_config *config;
char *config_file;
@@ -56,21 +65,31 @@ struct wb_server {
struct wb_cursor *cursor;
struct wb_seat *seat;
- struct wb_view *grabbed_view;
+ struct wb_toplevel *grabbed_toplevel;
struct wlr_box grab_geo_box;
double grab_x, grab_y;
uint32_t resize_edges;
- struct wl_list views;
+ struct wlr_ext_foreign_toplevel_list_v1 *foreign_toplevel_list;
+ struct wl_list toplevels;
struct wlr_layer_shell_v1 *layer_shell;
struct wlr_xdg_shell *xdg_shell;
+ struct wl_listener gamma_control_set_gamma;
struct wl_listener new_layer_surface;
- struct wl_listener new_xdg_surface;
struct wl_listener new_xdg_decoration;
+ struct wl_listener new_xdg_popup;
+ struct wl_listener new_xdg_toplevel;
+
+ struct wl_listener destroy_inhibit_manager;
+ struct wl_listener destroy_inhibitor;
+ struct wl_listener new_inhibitor;
+ struct wl_list inhibitors;
struct wl_listener new_input;
struct wl_listener new_output;
+ struct wl_listener output_configuration_applied;
+ struct wl_listener output_configuration_tested;
struct wl_list outputs; /* wb_output::link */
};
diff --git a/include/waybox/xdg_shell.h b/include/waybox/xdg_shell.h
index aee65ef..6c3757b 100644
--- a/include/waybox/xdg_shell.h
+++ b/include/waybox/xdg_shell.h
@@ -1,12 +1,49 @@
#ifndef _WB_XDG_SHELL_H
#define _WB_XDG_SHELL_H
+#include
+
#include "waybox/server.h"
+struct wb_popup {
+ struct wlr_xdg_popup *xdg_popup;
+ struct wl_listener commit;
+ struct wl_listener destroy;
+};
+
+struct wb_toplevel {
+ struct wb_server *server;
+ struct wlr_xdg_toplevel *xdg_toplevel;
+ struct wlr_scene_tree *scene_tree;
+
+ struct wlr_xdg_toplevel_decoration_v1 *decoration;
+
+ struct wlr_ext_foreign_toplevel_handle_v1 *foreign_toplevel_handle;
+ struct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state;
+
+ struct wl_listener map;
+ struct wl_listener unmap;
+ struct wl_listener commit;
+ struct wl_listener destroy;
+ struct wl_listener new_popup;
+ struct wl_listener request_fullscreen;
+ struct wl_listener request_maximize;
+ struct wl_listener request_minimize;
+ struct wl_listener request_move;
+ struct wl_listener request_resize;
+ struct wl_listener set_app_id;
+ struct wl_listener set_title;
+
+ struct wlr_box geometry;
+ struct wlr_box previous_geometry;
+
+ struct wl_list link;
+};
+
void init_xdg_shell(struct wb_server *server);
-void focus_view(struct wb_view *view, struct wlr_surface *surface);
-struct wlr_output *get_active_output(struct wb_view *view);
-struct wb_view *get_view_at(
+void focus_toplevel(struct wb_toplevel *toplevel);
+struct wlr_output *get_active_output(struct wb_toplevel *toplevel);
+struct wb_toplevel *get_toplevel_at(
struct wb_server *server, double lx, double ly,
struct wlr_surface **surface, double *sx, double *sy);
#endif
diff --git a/meson.build b/meson.build
index 3cc5c5c..3626ba9 100644
--- a/meson.build
+++ b/meson.build
@@ -1,9 +1,9 @@
project(
'Waybox',
'c',
- version: '0.2.1',
+ version: '0.2.3',
license: 'MIT',
- meson_version: '>=0.52.0',
+ meson_version: '>=0.60.0',
default_options: [
'c_std=c11',
'warning_level=2',
@@ -15,6 +15,7 @@ add_project_arguments(
'-Wno-unused-parameter',
'-D_DEFAULT_SOURCE',
'-D_POSIX_C_SOURCE=200112L',
+ '-DWL_HIDE_DEPRECATED', # Hide the deprecated parts of the Wayland API
'-DWLR_USE_UNSTABLE',
'-DPACKAGE_NAME="' + meson.project_name() + '"',
'-DPACKAGE_VERSION="' + meson.project_version() + '"',
@@ -25,16 +26,27 @@ cc = meson.get_compiler('c')
# Adding include directory
inc_dir = include_directories('include')
+if get_option('wlroots-version') != ''
+ wlroots_version = get_option('wlroots-version')
+else
+ wlroots_version = ['wlroots-0.20', 'wlroots-0.19', 'wlroots-0.18']
+endif
+
+libevdev = dependency('libevdev')
+libinput = dependency('libinput', version: '>=1.21.0', required: false)
libxml2 = dependency('libxml-2.0')
-wlroots = dependency('wlroots', version: '>=0.16.0')
+wayland_protos = dependency('wayland-protocols', version: '>=1.37')
wayland_server = dependency('wayland-server', version: '>=1.15')
-wayland_protos = dependency('wayland-protocols', version: '>=1.27')
-xkbcommon = dependency('xkbcommon')
+wlroots = dependency(wlroots_version, version: '>=0.17.0')
+xkbcommon = dependency('xkbcommon')
+
+if libinput.found()
+ add_project_arguments('-DHAS_LIBINPUT', language: 'c')
+endif
msgfmt = find_program('msgfmt', required: false)
if msgfmt.found()
source_root = meson.current_source_dir()
- add_project_arguments('-DUSE_NLS=1', language: 'c')
subdir('po')
endif
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 0000000..f32c01d
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1 @@
+option('wlroots-version', type: 'string', value: '', description: 'The version of wlroots with which to compile the compositor')
diff --git a/po/de.po b/po/de.po
index 92723f2..28a64ec 100644
--- a/po/de.po
+++ b/po/de.po
@@ -11,8 +11,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Waybox 0.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/wizbright/waybox/issues\n"
-"POT-Creation-Date: 2022-12-06 22:27-0500\n"
-"PO-Revision-Date: 2022-12-06 22:30-0500\n"
+"POT-Creation-Date: 2024-01-25 21:21-0500\n"
+"PO-Revision-Date: 2024-01-25 21:34-0500\n"
"Last-Translator: Volker Ribbert \n"
"Language-Team: \n"
"Language: de\n"
@@ -22,10 +22,10 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "WARNING: Using files from Openbox. These may not work correctly."
-msgstr "WARNUNG: Benutzt Dateien aus Openbox. Das wird nicht richtig werken."
+msgstr "WARNUNG: Benutze Dateien von Openbox, was nicht richtig funktionieren wird."
msgid "ERROR: No configuration file found."
-msgstr "FEHLER: Keine Einstellungsdatei gefunden."
+msgstr "FEHLER: Keine Konfigurationsdatei gefunden."
msgid "Unable to evaluate expression"
msgstr "Konnte den Ausdruck nicht evaluieren"
@@ -39,18 +39,18 @@ msgstr "Kein nodeset"
msgid ""
"Unable to parse the configuration file. Consult stderr for more information."
msgstr ""
-"Kann nicht die Einstullungsdatei analisieren. Für mehr Informationen die "
-"Standardfehlerausgabe lesen."
+"Kann die Konfigurationsdatei nicht verarbeiten. Für weitere Informationen siehe "
+"die Standardfehlerausgabe."
msgid "Couldn't create new context!"
-msgstr "Konnte einen neuen Zusammenhang nicht erstellen"
+msgstr "Konnte keinen neuen Kontext erstellen!"
msgid "Couldn't register the namespace"
-msgstr "Konte dem Namensnraum nicht registrieren"
+msgstr "Konnte den Namensraum nicht registrieren"
#, c-format
msgid "Syntax: %s [options]\n"
-msgstr "Eingabe: %s [Optionen]\n"
+msgstr "Syntax: %s [Optionen]\n"
#, c-format
msgid ""
@@ -75,7 +75,7 @@ msgstr " --config-file DATEI Pfad zur Konfigurationsdatei\n"
#, c-format
msgid " --sm-disable Disable connection to the session manager\n"
-msgstr " --sm-disable Verbindung zum Sitzungsmanager trennen\n"
+msgstr " --sm-disable Deaktiviere Verbindung zum Sitzungsmanager\n"
#, c-format
msgid " --startup CMD Run CMD after starting\n"
@@ -83,7 +83,7 @@ msgstr " --startup CMD CMD nach Start ausführen\n"
#, c-format
msgid " --debug Display debugging output\n"
-msgstr " --debug Fehlersuche-Ergebnis anzeigen\n"
+msgstr " --debug Ausgabe von Debug-Informationen\n"
#, c-format
msgid ""
@@ -92,8 +92,8 @@ msgid ""
"on Wayland.\n"
msgstr ""
"\n"
-"Weitere Openbox-Optionen sind nicht angenommen, meistens wegen sie sind "
-"sinnlos auf Wayland.\n"
+"Weitere Openbox-Optionen werden nicht angenommen, da sie überwiegend "
+"sinnlos unter Wayland sind.\n"
#, c-format
msgid "%s requires an argument\n"
@@ -101,25 +101,25 @@ msgstr "%s erfordert einen Parameter\n"
#, c-format
msgid "%s hasn't been implemented yet.\n"
-msgstr "%s ist schon nicht implementiert.\n"
+msgstr "%s wurde bisher nicht implementiert.\n"
msgid "Successfully created backend"
-msgstr "Das Backend wurde erfolgreich hergestellt"
+msgstr "Das Backend wurde erfolgreich erstellt"
msgid "Failed to create backend"
-msgstr "Das Backend wurde nicht erfolgreich hergestellt"
+msgstr "Das Backend wurde nicht erstellt"
msgid "Successfully started server"
msgstr "Der Server wurde erfolgreich gestartet"
msgid "Failed to start server"
-msgstr "Der Server wurde nicht erfolgreich gestartet"
+msgstr "Der Server wurde nicht gestartet"
msgid "New output device detected"
-msgstr "Neue Ausgabegerät entdeckt"
+msgstr "Neues Ausgabegerät entdeckt"
-msgid "Couldn't commit pending frame to output"
-msgstr "Konnte sich nicht an den anstehenden Rahmen an die Ausgabe eintragen"
+msgid "Could not add an output layout."
+msgstr "Konnte keine neue Ausgabenanordnung hinzufügen."
msgid "New keyboard detected"
msgstr "Neue Tastatur entdeckt"
@@ -128,22 +128,25 @@ msgid "New pointer detected"
msgstr "Neuer Zeiger entdeckt"
msgid "Unsupported input device detected"
-msgstr "Unerkanntes Eingabegerät entdeckt"
+msgstr "Unbekanntes Eingabegerät entdeckt"
msgid "Failed to connect to a Wayland display"
-msgstr "Scheiterte an eines Wayland-Displays verbinden"
+msgstr "Scheiterte mit Verbindung an ein Wayland-Display"
+
+msgid "Failed to get an event loop"
+msgstr "Scheiterte eine Ereignisschleife zu bekommen"
msgid "Failed to create renderer"
-msgstr "Das Renderer wurde nicht erfolgreich hergestellt"
+msgstr "Der Renderer wurde nicht erstellt"
msgid "Failed to create allocator"
-msgstr "Das Allokator wurde nicht erfolgreich hergestellt"
+msgstr "Der Allokator wurde nicht erstellt"
msgid "Failed to start backend"
msgstr "Starten des Backend gescheitert"
msgid "Running Wayland compositor on Wayland display"
-msgstr "Ausführt Wayland-Compositor auf Wayland-Display"
+msgstr "Führe Wayland-Compositor auf Wayland-Display aus"
msgid "Display destroyed"
msgstr "Display zerstört"
@@ -151,28 +154,32 @@ msgstr "Display zerstört"
msgid "Keyboard focus is now on surface"
msgstr "Tastaturfokus ist jetzt auf Oberfläche"
-msgid "Focusing next view"
-msgstr "Fokussiert nächste Ansicht"
+msgid "Focusing next toplevel"
+msgstr "Fokussiere nächst höhere Ebene"
+
+#~ msgid "Couldn't commit pending frame to output"
+#~ msgstr ""
+#~ "Der anstehende Frame konnte nicht an die Ausgabe übergeben werden"
#, fuzzy
#~ msgid "Failed to create wlr_backend"
-#~ msgstr "Das Backend wurde nicht erfolgreich hergestellt"
+#~ msgstr "wlr_backend wurde nicht erstellt"
#~ msgid "Couldn't get a surface texture"
#~ msgstr "Konnte keine Oberflächentextur bekommen"
#~ msgid "Couldn't attach renderer to output"
-#~ msgstr "Konnte nicht einen Renderer an die Ausgabe nicht anbringen"
+#~ msgstr "Konnte Renderer nicht an die Ausgabe binden"
#~ msgid "Focusing current view"
#~ msgstr "Fokussiert jetzige Ansicht"
#~ msgid "Unable to parse XML file"
-#~ msgstr "Konnte die XML-Datei nicht analisieren"
+#~ msgstr "Konnte die XML-Datei nicht analysieren"
#, c-format
#~ msgid "Invalid action \"%s\" requested. No such action exists."
-#~ msgstr "Ungültige Aktion \"%s\" angefordert. Es gibt keine solche."
+#~ msgstr "Ungültige Aktion \"%s\" angefordert. Diese existiert nicht."
#~ msgid "No"
#~ msgstr "Nein"
@@ -255,7 +262,7 @@ msgstr "Fokussiert nächste Ansicht"
#~ msgstr "Immer im _Hintergrund"
#~ msgid "_Send to desktop"
-#~ msgstr "_Verschieben nach"
+#~ msgstr "_Sende an Desktop"
#~ msgid "Client menu"
#~ msgstr "Anwendungsmenü"
@@ -292,8 +299,8 @@ msgstr "Fokussiert nächste Ansicht"
#~ "Waybox was compiled without image loading support. Icons in menus will "
#~ "not be loaded."
#~ msgstr ""
-#~ "Waybox wurde ohne Bildladungsunterstützung kompiliert. Ikone in Menüs "
-#~ "werden nicht geladen werden."
+#~ "Waybox wurde ohne Unterstützung für Bilder kompiliert. Diese werden in Menüs "
+#~ "daher nicht geladen."
#~ msgid "Conflict with key binding in config file"
#~ msgstr "Störende Tastenkombination in Konfigurationsdatei"
@@ -312,18 +319,18 @@ msgstr "Fokussiert nächste Ansicht"
#, c-format
#~ msgid "Attempted to access menu \"%s\" but it does not exist"
-#~ msgstr "Versuchter Zugriff auf Menü \"%s\", doch es existiert nicht"
+#~ msgstr "Versuchter Zugriff auf Menü \"%s\", welches nicht existiert"
#~ msgid "More..."
#~ msgstr "Mehr..."
#, c-format
#~ msgid "Invalid button \"%s\" in mouse binding"
-#~ msgstr "Maus-Einbindung mit ungültiger Taste \"%s\""
+#~ msgstr "Maus-Kombination mit ungültiger Taste \"%s\""
#, c-format
#~ msgid "Invalid context \"%s\" in mouse binding"
-#~ msgstr "Maus-Einbindung mit ungültigem Kontext \"%s\""
+#~ msgstr "Maus-Kombination mit ungültigem Kontext \"%s\""
#, c-format
#~ msgid "Unable to change to home directory \"%s\": %s"
@@ -331,8 +338,8 @@ msgstr "Fokussiert nächste Ansicht"
#~ msgid "Unable to find a valid config file, using some simple defaults"
#~ msgstr ""
-#~ "Keine gültige Konfigurationsdatei vorhanden, benutze einfache "
-#~ "Standardwerte"
+#~ "Keine gültige Konfigurationsdatei vorhanden, daher werden einfache "
+#~ "Standardwerte benutzt"
#~ msgid "Unable to load a theme."
#~ msgstr "Kann kein Thema laden."
@@ -372,12 +379,12 @@ msgstr "Fokussiert nächste Ansicht"
#~ msgid " --debug-focus Display debugging output for focus handling\n"
#~ msgstr ""
-#~ " --debug-focus Fehlersuche-Ergebnis für Fokus-Handling anzeigen\n"
+#~ " --debug-focus Debug-Informationen für Fokus-Handling anzeigen\n"
#~ msgid ""
#~ " --debug-session Display debugging output for session management\n"
#~ msgstr ""
-#~ " --debug-session Fehler-Ergebnis für Sitzungsmanager anzeigen\n"
+#~ " --debug-session Debug-Informationen für Sitzungsmanager anzeigen\n"
#, c-format
#~ msgid ""
@@ -411,7 +418,7 @@ msgstr "Fokussiert nächste Ansicht"
#, c-format
#~ msgid "Unable to make directory \"%s\": %s"
-#~ msgstr "Kann nicht Verzeichnis \"%s\" machen: %s"
+#~ msgstr "Kann Verzeichnis \"%s\" nicht erstellen: %s"
#, c-format
#~ msgid "Unable to save the session to \"%s\": %s"
@@ -419,10 +426,10 @@ msgstr "Fokussiert nächste Ansicht"
#, c-format
#~ msgid "Error while saving the session to \"%s\": %s"
-#~ msgstr "Fehler während die Sitzung in \"%s\" speichern: %s"
+#~ msgstr "Fehler beim Speichern der Sitzung in \"%s\": %s"
#~ msgid "Not connected to a session manager"
-#~ msgstr "An einer Sitzungsmanager nicht verbindet"
+#~ msgstr "Nicht mit Sitzungsmanager verbunden"
#, c-format
#~ msgid "Running %s"
@@ -430,7 +437,7 @@ msgstr "Fokussiert nächste Ansicht"
#, c-format
#~ msgid "Invalid modifier key \"%s\" in key/mouse binding"
-#~ msgstr "Ungültige Modifier-Taste \"%s\" in Tasten/Maus-Einbindung"
+#~ msgstr "Ungültige Modifier-Taste \"%s\" in Tasten/Maus-Kombination"
#, c-format
#~ msgid "Invalid key code \"%s\" in key binding"
diff --git a/po/eo.po b/po/eo.po
index 0c80d20..f3c31d3 100644
--- a/po/eo.po
+++ b/po/eo.po
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Waybox 0.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/wizbright/waybox/issues\n"
-"POT-Creation-Date: 2022-12-06 22:27-0500\n"
-"PO-Revision-Date: 2022-12-06 22:33-0500\n"
+"POT-Creation-Date: 2024-01-25 21:21-0500\n"
+"PO-Revision-Date: 2024-01-25 21:35-0500\n"
"Last-Translator: Keith \n"
"Language-Team: Esperanto\n"
"Language: eo\n"
@@ -24,7 +24,7 @@ msgid "ERROR: No configuration file found."
msgstr "ERARO: Neniu agordo-dosiero trovita."
msgid "Unable to evaluate expression"
-msgstr "Ne povas taksi esprimon"
+msgstr "Ne eblis taksi esprimon"
msgid "No nodesetval"
msgstr "Neniu nodara valoro"
@@ -41,7 +41,7 @@ msgid "Couldn't create new context!"
msgstr "Ne povis krei novan kuntekston!"
msgid "Couldn't register the namespace"
-msgstr "Ne povas registri lo nomspacon"
+msgstr "Ne eblis registri la nomspacon"
#, c-format
msgid "Syntax: %s [options]\n"
@@ -98,13 +98,13 @@ msgstr "%s postulas argumenton\n"
#, c-format
msgid "%s hasn't been implemented yet.\n"
-msgstr "%s ankoraŭ ne estas efektigita.\n"
+msgstr "%s ankoraŭ ne estas efektivigita.\n"
msgid "Successfully created backend"
msgstr "Sukcese kreis servilan dorson"
msgid "Failed to create backend"
-msgstr "Malsukesis krei servilan dorson"
+msgstr "Malsukcesis krei servilan dorson"
msgid "Successfully started server"
msgstr "Sukcese startigis servilon"
@@ -113,10 +113,10 @@ msgid "Failed to start server"
msgstr "Malsukcesis startigi servilon"
msgid "New output device detected"
-msgstr "Nova enigilo malkovrita"
+msgstr "Nova eligilo malkovrita"
-msgid "Couldn't commit pending frame to output"
-msgstr "Ne povis surmeti atendantan framon sur eligon"
+msgid "Could not add an output layout."
+msgstr "Ne eblis aldoni eligan aranĝon."
msgid "New keyboard detected"
msgstr "Nova klavaro malkovrita"
@@ -130,14 +130,17 @@ msgstr "Nerekonata enigilo malkovrita"
msgid "Failed to connect to a Wayland display"
msgstr "Malsukcesis konektiĝi al Wayland-ekrano"
+msgid "Failed to get an event loop"
+msgstr "Malsukcesis atingi evento-iteracion"
+
msgid "Failed to create renderer"
-msgstr "Malsukesis krei servilan bildigilon"
+msgstr "Malsukcesis krei servilan bildigilon"
msgid "Failed to create allocator"
-msgstr "Malsukesis krei servilan asignilon"
+msgstr "Malsukcesis krei servilan asignilon"
msgid "Failed to start backend"
-msgstr "Malsukesis startigi servilan dorson"
+msgstr "Malsukcesis startigi servilan dorson"
msgid "Running Wayland compositor on Wayland display"
msgstr "Plenumas Wayland-komponilon sur Wayland-ekrano"
@@ -148,8 +151,11 @@ msgstr "Ekrano finigita"
msgid "Keyboard focus is now on surface"
msgstr "Klavara fokuso nun estas sur surfaco"
-msgid "Focusing next view"
-msgstr "Fokusas la sekvan vidon"
+msgid "Focusing next toplevel"
+msgstr "Fokusas la sekvan supran nivelon"
+
+#~ msgid "Couldn't commit pending frame to output"
+#~ msgstr "Ne eblis surmeti atendantan framon sur eligon"
#, fuzzy
#~ msgid "Failed to create wlr_backend"
diff --git a/po/meson.build b/po/meson.build
index 5466a32..022521d 100644
--- a/po/meson.build
+++ b/po/meson.build
@@ -1,11 +1,14 @@
-i18n = import('i18n')
add_project_arguments('-DGETTEXT_PACKAGE="' + meson.project_name().to_lower() + '"',
'-DLOCALEDIR="' + get_option('prefix') / get_option('localedir') + '"',
- language:'c')
-i18n.gettext(meson.project_name().to_lower(),
- args: ['--directory=' + source_root,
- '--add-comments=TRANSLATORS',
- '--no-location',
- '--keyword=_',
- '--msgid-bugs=https://github.com/wizbright/waybox/issues']
+ '-DUSE_NLS=1', language: 'c')
+
+i18n = import('i18n', required: false)
+if i18n.found()
+ i18n.gettext(meson.project_name().to_lower(),
+ args: ['--directory=' + source_root,
+ '--add-comments=TRANSLATORS',
+ '--no-location',
+ '--keyword=_',
+ '--msgid-bugs=https://github.com/wizbright/waybox/issues']
)
+endif
diff --git a/po/waybox.pot b/po/waybox.pot
index a71efa5..682fbdf 100644
--- a/po/waybox.pot
+++ b/po/waybox.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: waybox\n"
"Report-Msgid-Bugs-To: https://github.com/wizbright/waybox/issues\n"
-"POT-Creation-Date: 2022-12-06 22:27-0500\n"
+"POT-Creation-Date: 2024-01-25 21:21-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -107,7 +107,7 @@ msgstr ""
msgid "New output device detected"
msgstr ""
-msgid "Couldn't commit pending frame to output"
+msgid "Could not add an output layout."
msgstr ""
msgid "New keyboard detected"
@@ -122,6 +122,9 @@ msgstr ""
msgid "Failed to connect to a Wayland display"
msgstr ""
+msgid "Failed to get an event loop"
+msgstr ""
+
msgid "Failed to create renderer"
msgstr ""
@@ -140,5 +143,5 @@ msgstr ""
msgid "Keyboard focus is now on surface"
msgstr ""
-msgid "Focusing next view"
+msgid "Focusing next toplevel"
msgstr ""
diff --git a/protocol/meson.build b/protocol/meson.build
index e3b6fc0..33b8cc1 100644
--- a/protocol/meson.build
+++ b/protocol/meson.build
@@ -1,6 +1,7 @@
wl_protocol_dir = wayland_protos.get_variable(pkgconfig: 'pkgdatadir')
-wayland_scanner = find_program('wayland-scanner', version: '>= 1.15')
+wayland_scanner_dep = dependency('wayland-scanner', version: '>=1.15')
+wayland_scanner = find_program(wayland_scanner_dep.get_variable(pkgconfig: 'wayland_scanner'))
wayland_scanner_server = generator(
wayland_scanner,
@@ -24,19 +25,18 @@ protocols = [
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
[wl_protocol_dir, 'staging/ext-idle-notify/ext-idle-notify-v1.xml'],
[wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'],
- [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/primary-selection/primary-selection-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
'wlr-gamma-control-unstable-v1.xml',
- 'wlr-screencopy-unstable-v1.xml',
'wlr-layer-shell-unstable-v1.xml',
+ 'wlr-screencopy-unstable-v1.xml',
]
client_protocols = [
[wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'],
- 'wlr-screencopy-unstable-v1.xml',
'wlr-layer-shell-unstable-v1.xml',
+ 'wlr-screencopy-unstable-v1.xml',
]
wl_protos_src = []
diff --git a/screenshots/emptydesktop.png b/screenshots/emptydesktop.png
new file mode 100644
index 0000000..7f5bd73
Binary files /dev/null and b/screenshots/emptydesktop.png differ
diff --git a/screenshots/play.png b/screenshots/play.png
new file mode 100644
index 0000000..f9f3a0e
Binary files /dev/null and b/screenshots/play.png differ
diff --git a/screenshots/work.png b/screenshots/work.png
new file mode 100644
index 0000000..5f0de30
Binary files /dev/null and b/screenshots/work.png differ
diff --git a/waybox/config.c b/waybox/config.c
index 10452e7..fdd49d8 100644
--- a/waybox/config.c
+++ b/waybox/config.c
@@ -1,3 +1,4 @@
+#include
#include
#include
@@ -151,6 +152,7 @@ bool init_config(struct wb_server *server) {
doc = xmlReadFile(rc_file, NULL, XML_PARSE_RECOVER);
wlr_log(WLR_INFO, "Using config file %s", rc_file);
+ setenv("WAYBOX_CONFIG_FILE", rc_file, true);
free(rc_file);
if (doc == NULL) {
wlr_log(WLR_ERROR, "%s", _("Unable to parse the configuration file. Consult stderr for more information."));
@@ -166,15 +168,35 @@ bool init_config(struct wb_server *server) {
wlr_log(WLR_INFO, "%s", _("Couldn't register the namespace"));
}
- config->keyboard_layout.use_config = parse_xpath_expr("//ob:keyboard//ob:keyboardLayout", ctxt) != NULL;
-
+ config->keyboard_layout.use_config = parse_xpath_expr("//ob:keyboard//ob:xkb", ctxt) != NULL;
if (config->keyboard_layout.use_config) {
- config->keyboard_layout.layout = parse_xpath_expr("//ob:keyboard//ob:keyboardLayout//ob:layout", ctxt);
- config->keyboard_layout.model = parse_xpath_expr("//ob:keyboard//ob:keyboardLayout//ob:model", ctxt);
- config->keyboard_layout.options = parse_xpath_expr("//ob:keyboard//ob:keyboardLayout//ob:options", ctxt);
- config->keyboard_layout.rules = parse_xpath_expr("//ob:keyboard//ob:keyboardLayout//ob:rules", ctxt);
- config->keyboard_layout.variant = parse_xpath_expr("//ob:keyboard//ob:keyboardLayout//ob:variant", ctxt);
+ config->keyboard_layout.layout = parse_xpath_expr("//ob:keyboard//ob:xkb//ob:layout", ctxt);
+ config->keyboard_layout.model = parse_xpath_expr("//ob:keyboard//ob:xkb//ob:model", ctxt);
+ config->keyboard_layout.options = parse_xpath_expr("//ob:keyboard//ob:xkb//ob:options", ctxt);
+ config->keyboard_layout.rules = parse_xpath_expr("//ob:keyboard//ob:xkb//ob:rules", ctxt);
+ config->keyboard_layout.variant = parse_xpath_expr("//ob:keyboard//ob:xkb//ob:variant", ctxt);
}
+
+ config->libinput_config.use_config = parse_xpath_expr("//ob:mouse//ob:libinput", ctxt) != NULL;
+ if (config->libinput_config.use_config) {
+ config->libinput_config.accel_profile = parse_xpath_expr("//ob:mouse//ob:libinput/ob:accelProfile", ctxt);
+ config->libinput_config.accel_speed = parse_xpath_expr("//ob:mouse//ob:libinput/ob:accelSpeed", ctxt);
+ config->libinput_config.calibration_matrix = parse_xpath_expr("//ob:mouse//ob:libinput/ob:calibrationMatrix", ctxt);
+ config->libinput_config.click_method = parse_xpath_expr("//ob:mouse//ob:libinput/ob:clickMethod", ctxt);
+ config->libinput_config.dwt = parse_xpath_expr("//ob:mouse//ob:libinput/ob:disableWhileTyping", ctxt);
+ config->libinput_config.dwtp = parse_xpath_expr("//ob:mouse//ob:libinput/ob:disableWhileTrackpointing", ctxt);
+ config->libinput_config.left_handed = parse_xpath_expr("//ob:mouse//ob:libinput/ob:leftHanded", ctxt);
+ config->libinput_config.middle_emulation = parse_xpath_expr("//ob:mouse//ob:libinput/ob:middleEmulation", ctxt);
+ config->libinput_config.natural_scroll = parse_xpath_expr("//ob:mouse//ob:libinput/ob:naturalScroll", ctxt);
+ config->libinput_config.scroll_button = parse_xpath_expr("//ob:mouse//ob:libinput/ob:scrollButton", ctxt);
+ config->libinput_config.scroll_button_lock = parse_xpath_expr("//ob:mouse//ob:libinput/ob:scrollButtonLock", ctxt);
+ config->libinput_config.scroll_method = parse_xpath_expr("//ob:mouse//ob:libinput/ob:scrollMethod", ctxt);
+ config->libinput_config.tap = parse_xpath_expr("//ob:mouse//ob:libinput/ob:tap", ctxt);
+ config->libinput_config.tap_button_map = parse_xpath_expr("//ob:mouse//ob:libinput/ob:tapButtonMap", ctxt);
+ config->libinput_config.tap_drag = parse_xpath_expr("//ob:mouse//ob:libinput/ob:tapDrag", ctxt);
+ config->libinput_config.tap_drag = parse_xpath_expr("//ob:mouse//ob:libinput/ob:tapDragLock", ctxt);
+ }
+
if (!parse_key_bindings(config, ctxt)) {
xmlFreeDoc(doc);
return false;
diff --git a/waybox/config.h b/waybox/config.h
index 82cca4d..468e1b8 100644
--- a/waybox/config.h
+++ b/waybox/config.h
@@ -27,6 +27,26 @@ struct wb_config {
bool use_config;
} keyboard_layout;
+ struct {
+ char *accel_profile;
+ char *accel_speed;
+ char *calibration_matrix;
+ char *click_method;
+ char *dwt;
+ char *dwtp;
+ char *left_handed;
+ char *middle_emulation;
+ char *natural_scroll;
+ char *scroll_button;
+ char *scroll_button_lock;
+ char *scroll_method;
+ char *tap;
+ char *tap_button_map;
+ char *tap_drag;
+ char *tap_drag_lock;
+
+ bool use_config;
+ } libinput_config;
struct {
int bottom;
int left;
diff --git a/waybox/cursor.c b/waybox/cursor.c
index 4a1fff6..d4d5e75 100644
--- a/waybox/cursor.c
+++ b/waybox/cursor.c
@@ -4,22 +4,22 @@
void reset_cursor_mode(struct wb_server *server) {
/* Reset the cursor mode to passthrough */
server->cursor->cursor_mode = WB_CURSOR_PASSTHROUGH;
- server->grabbed_view = NULL;
+ server->grabbed_toplevel = NULL;
}
static void process_cursor_move(struct wb_server *server) {
- /* Move the grabbed view to the new position. */
- struct wb_view *view = server->grabbed_view;
- if (view->scene_tree->node.type == WLR_SCENE_NODE_TREE) {
- view->geometry.x = server->cursor->cursor->x - server->grab_x;
- view->geometry.y = server->cursor->cursor->y - server->grab_y;
- wlr_scene_node_set_position(&view->scene_tree->node,
- view->geometry.x, view->geometry.y);
+ /* Move the grabbed toplevel to the new position. */
+ struct wb_toplevel *toplevel = server->grabbed_toplevel;
+ if (toplevel->scene_tree->node.type == WLR_SCENE_NODE_TREE) {
+ toplevel->geometry.x = server->cursor->cursor->x - server->grab_x;
+ toplevel->geometry.y = server->cursor->cursor->y - server->grab_y;
+ wlr_scene_node_set_position(&toplevel->scene_tree->node,
+ toplevel->geometry.x, toplevel->geometry.y);
}
}
static void process_cursor_resize(struct wb_server *server) {
- struct wb_view *view = server->grabbed_view;
+ struct wb_toplevel *toplevel = server->grabbed_toplevel;
double border_x = server->cursor->cursor->x - server->grab_x;
double border_y = server->cursor->cursor->y - server->grab_y;
int new_left = server->grab_geo_box.x;
@@ -50,16 +50,20 @@ static void process_cursor_resize(struct wb_server *server) {
}
}
+#if WLR_CHECK_VERSION(0, 19, 0)
+ struct wlr_box geo_box = toplevel->xdg_toplevel->base->geometry;
+#else
struct wlr_box geo_box;
- wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo_box);
- view->geometry.x = new_left - geo_box.x;
- view->geometry.y = new_top - geo_box.y;
- wlr_scene_node_set_position(&view->scene_tree->node,
- view->geometry.x, view->geometry.y);
+ wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &geo_box);
+#endif
+ toplevel->geometry.x = new_left - geo_box.x;
+ toplevel->geometry.y = new_top - geo_box.y;
+ wlr_scene_node_set_position(&toplevel->scene_tree->node,
+ toplevel->geometry.x, toplevel->geometry.y);
int new_width = new_right - new_left;
int new_height = new_bottom - new_top;
- wlr_xdg_toplevel_set_size(view->xdg_toplevel, new_width, new_height);
+ wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel, new_width, new_height);
}
static void process_cursor_motion(struct wb_server *server, uint32_t time) {
@@ -72,18 +76,18 @@ static void process_cursor_motion(struct wb_server *server, uint32_t time) {
return;
}
- /* Otherwise, find the view under the pointer and send the event along. */
+ /* Otherwise, find the toplevel under the pointer and send the event along. */
double sx, sy;
struct wlr_seat *seat = server->seat->seat;
struct wlr_surface *surface = NULL;
- struct wb_view *view = get_view_at(server,
+ struct wb_toplevel *toplevel = get_toplevel_at(server,
server->cursor->cursor->x, server->cursor->cursor->y, &surface, &sx, &sy);
- if (!view) {
- /* If there's no view under the cursor, set the cursor image to a
+ if (!toplevel) {
+ /* If there's no toplevel under the cursor, set the cursor image to a
* default. This is what makes the cursor image appear when you move it
- * around the screen, not over any views. */
- wlr_xcursor_manager_set_cursor_image(
- server->cursor->xcursor_manager, "left_ptr", server->cursor->cursor);
+ * around the screen, not over any toplevels. */
+ wlr_cursor_set_xcursor(
+ server->cursor->cursor, server->cursor->xcursor_manager, "default");
}
if (surface) {
/*
@@ -134,14 +138,14 @@ static void handle_cursor_button(struct wl_listener *listener, void *data) {
event->time_msec, event->button, event->state);
double sx, sy;
struct wlr_surface *surface = NULL;
- struct wb_view *view = get_view_at(cursor->server,
+ struct wb_toplevel *toplevel = get_toplevel_at(cursor->server,
cursor->server->cursor->cursor->x, cursor->server->cursor->cursor->y, &surface, &sx, &sy);
- if (event->state == WLR_BUTTON_RELEASED) {
+ if (event->state == WL_POINTER_BUTTON_STATE_RELEASED) {
/* If you released any buttons, we exit interactive move/resize mode. */
reset_cursor_mode(cursor->server);
} else {
/* Focus that client if the button was _pressed_ */
- focus_view(view, surface);
+ focus_toplevel(toplevel);
}
wlr_idle_notifier_v1_notify_activity(cursor->server->idle_notifier, cursor->server->seat->seat);
@@ -156,7 +160,7 @@ static void handle_cursor_axis(struct wl_listener *listener, void *data) {
/* Notify the client with pointer focus of the axis event. */
wlr_seat_pointer_notify_axis(cursor->server->seat->seat,
event->time_msec, event->orientation, event->delta,
- event->delta_discrete, event->source);
+ event->delta_discrete, event->source, event->relative_direction);
}
static void handle_cursor_frame(struct wl_listener *listener, void *data) {
@@ -198,7 +202,6 @@ struct wb_cursor *wb_cursor_create(struct wb_server *server) {
const char *xcursor_size = getenv("XCURSOR_SIZE");
cursor->xcursor_manager = wlr_xcursor_manager_create(getenv("XCURSOR_THEME"),
xcursor_size ? strtoul(xcursor_size, (char **) NULL, 10) : 24);
- wlr_xcursor_manager_load(cursor->xcursor_manager, 1);
cursor->cursor_motion.notify = handle_cursor_motion;
wl_signal_add(&cursor->cursor->events.motion, &cursor->cursor_motion);
@@ -229,6 +232,14 @@ void wb_cursor_destroy(struct wb_cursor *cursor) {
return;
}
+ wl_list_remove(&cursor->request_cursor.link);
+ wl_list_remove(&cursor->cursor_motion.link);
+ wl_list_remove(&cursor->cursor_motion_absolute.link);
+
+ wl_list_remove(&cursor->cursor_button.link);
+ wl_list_remove(&cursor->cursor_axis.link);
+ wl_list_remove(&cursor->cursor_frame.link);
+
wlr_xcursor_manager_destroy(cursor->xcursor_manager);
wlr_cursor_destroy(cursor->cursor);
free(cursor);
diff --git a/waybox/decoration.c b/waybox/decoration.c
index fd3fe17..706dd8a 100644
--- a/waybox/decoration.c
+++ b/waybox/decoration.c
@@ -15,9 +15,10 @@ static void free_xdg_decoration_mode(struct wl_listener *listener, void *data) {
static void handle_xdg_decoration_mode(struct wl_listener *listener, void *data) {
struct wlr_xdg_toplevel_decoration_v1 *toplevel_decoration = data;
struct wb_decoration *decoration = wl_container_of(listener, decoration, request_mode);
- struct wb_view *view = wl_container_of(decoration->server->views.next, view, link);
- wlr_xdg_toplevel_decoration_v1_set_mode(toplevel_decoration, WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE);
- view->decoration = toplevel_decoration;
+ struct wb_toplevel *toplevel = wl_container_of(decoration->server->toplevels.next, toplevel, link);
+ if (toplevel->xdg_toplevel->base->initialized)
+ wlr_xdg_toplevel_decoration_v1_set_mode(toplevel_decoration, WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE);
+ toplevel->decoration = toplevel_decoration;
}
static void handle_new_xdg_toplevel_decoration(struct wl_listener *listener, void *data) {
@@ -31,8 +32,6 @@ static void handle_new_xdg_toplevel_decoration(struct wl_listener *listener, voi
wl_signal_add(&toplevel_decoration->events.request_mode, &decoration->request_mode);
decoration->mode_destroy.notify = free_xdg_decoration_mode;
wl_signal_add(&toplevel_decoration->events.destroy, &decoration->mode_destroy);
- /* For some reason, a lot of clients don't emit the request_mode signal. */
- handle_xdg_decoration_mode(&decoration->request_mode, toplevel_decoration);
}
void init_xdg_decoration(struct wb_server *server) {
diff --git a/waybox/idle.c b/waybox/idle.c
new file mode 100644
index 0000000..20b1b14
--- /dev/null
+++ b/waybox/idle.c
@@ -0,0 +1,41 @@
+#include
+
+#include "idle.h"
+
+static void idle_inhibit_manager_destroy(struct wl_listener *listener, void *data) {
+ struct wb_server *server = wl_container_of(listener, server, destroy_inhibit_manager);
+ wl_list_remove(&server->new_inhibitor.link);
+ wl_list_remove(&server->inhibitors);
+}
+
+static void idle_inhibitor_destroy(struct wl_listener *listener, void *data) {
+ struct wb_server *server = wl_container_of(listener, server, destroy_inhibitor);
+ /* wlroots will destroy the inhibitor after this callback, so this number will be 1 if the
+ * last inhibitor is being destroyed. */
+ wl_list_remove(&server->destroy_inhibitor.link);
+ wlr_idle_notifier_v1_set_inhibited(server->idle_notifier,
+ wl_list_length(&server->inhibitors) > 1);
+}
+
+static void idle_inhibitor_new(struct wl_listener *listener, void *data) {
+ struct wb_server *server = wl_container_of(listener, server, new_inhibitor);
+ struct wlr_idle_inhibitor_v1 *inhibitor = data;
+
+ server->destroy_inhibitor.notify = idle_inhibitor_destroy;
+ wl_signal_add(&inhibitor->events.destroy, &server->destroy_inhibitor);
+ wl_list_remove(&inhibitor->link);
+ wl_list_insert(&server->inhibitors, &inhibitor->link);
+ wlr_idle_notifier_v1_set_inhibited(server->idle_notifier, true);
+}
+
+bool create_idle_manager(struct wb_server *server) {
+ server->idle_notifier = wlr_idle_notifier_v1_create(server->wl_display);
+ server->idle_inhibit_manager = wlr_idle_inhibit_v1_create(server->wl_display);
+
+ wl_list_init(&server->inhibitors);
+ server->new_inhibitor.notify = idle_inhibitor_new;
+ wl_signal_add(&server->idle_inhibit_manager->events.new_inhibitor, &server->new_inhibitor);
+ server->destroy_inhibit_manager.notify = idle_inhibit_manager_destroy;
+ wl_signal_add(&server->idle_inhibit_manager->events.destroy, &server->destroy_inhibit_manager);
+ return true;
+}
diff --git a/waybox/idle.h b/waybox/idle.h
new file mode 100644
index 0000000..0adfa79
--- /dev/null
+++ b/waybox/idle.h
@@ -0,0 +1,3 @@
+#include "waybox/server.h"
+
+bool create_idle_manager(struct wb_server *server);
diff --git a/waybox/layer_shell.c b/waybox/layer_shell.c
index ece945c..fc734a2 100644
--- a/waybox/layer_shell.c
+++ b/waybox/layer_shell.c
@@ -53,6 +53,7 @@ static void arrange_surface(struct wb_output *output, struct wlr_box *full_area,
if (desc->type == WB_SCENE_DESC_LAYER_SHELL) {
struct wb_layer_surface *surface = desc->data;
+ surface->scene->layer_surface->initialized = true;
wlr_scene_layer_surface_v1_configure(surface->scene,
full_area, usable_area);
}
@@ -105,10 +106,13 @@ static struct wb_layer_surface *wb_layer_surface_create(
static void handle_surface_commit(struct wl_listener *listener, void *data) {
struct wb_layer_surface *surface =
wl_container_of(listener, surface, surface_commit);
+ struct wb_toplevel *current_toplevel =
+ wl_container_of(surface->server->toplevels.next, current_toplevel, link);
- if (!surface->output) {
+ if (!surface->output || current_toplevel->xdg_toplevel->current.fullscreen) {
return;
}
+ wlr_fractional_scale_v1_notify_scale(surface->scene->layer_surface->surface, surface->output->wlr_output->scale);
struct wlr_layer_surface_v1 *layer_surface = surface->scene->layer_surface;
uint32_t committed = layer_surface->current.committed;
@@ -121,8 +125,8 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) {
wlr_scene_node_reparent(&surface->scene->tree->node, output_layer);
}
- if (committed || layer_surface->mapped != surface->mapped) {
- surface->mapped = layer_surface->mapped;
+ if (committed || layer_surface->surface->mapped != surface->mapped) {
+ surface->mapped = layer_surface->surface->mapped;
arrange_layers(surface->output);
struct timespec now;
@@ -170,9 +174,12 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
seat_set_focus_layer(seat, NULL);
}
- struct wb_view *view = wl_container_of(surface->server->views.next, view, link);
- if (view && view->scene_tree && view->scene_tree->node.enabled) {
- focus_view(view, view->xdg_toplevel->base->surface);
+ if (!wl_list_empty(&surface->server->toplevels)) {
+ struct wb_toplevel *toplevel =
+ wl_container_of(surface->server->toplevels.next, toplevel, link);
+ if (toplevel && toplevel->scene_tree && toplevel->scene_tree->node.enabled) {
+ focus_toplevel(toplevel);
+ }
}
}
@@ -181,6 +188,10 @@ static void wb_layer_surface_destroy(struct wb_layer_surface *surface) {
return;
}
+ if (surface->scene->layer_surface->surface != NULL)
+ wlr_fractional_scale_v1_notify_scale(surface->scene->layer_surface->surface,
+ surface->output->wlr_output->scale);
+
wl_list_remove(&surface->map.link);
wl_list_remove(&surface->unmap.link);
wl_list_remove(&surface->surface_commit.link);
@@ -197,6 +208,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
arrange_layers(surface->output);
}
+ wl_list_remove(&surface->new_popup.link);
wb_layer_surface_destroy(surface);
}
@@ -305,9 +317,10 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
if (layer_surface->output == NULL) {
struct wb_server *server =
wl_container_of(listener, server, new_layer_surface);
- struct wb_view *view =
- wl_container_of(server->views.next, view, link);
- layer_surface->output = get_active_output(view);
+ if (wl_list_length(&server->toplevels) == 0) return;
+ struct wb_toplevel *toplevel =
+ wl_container_of(server->toplevels.next, toplevel, link);
+ layer_surface->output = get_active_output(toplevel);
}
struct wb_output *output = layer_surface->output->data;
@@ -343,9 +356,9 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
wl_signal_add(&layer_surface->surface->events.commit,
&surface->surface_commit);
surface->map.notify = handle_map;
- wl_signal_add(&layer_surface->events.map, &surface->map);
+ wl_signal_add(&layer_surface->surface->events.map, &surface->map);
surface->unmap.notify = handle_unmap;
- wl_signal_add(&layer_surface->events.unmap, &surface->unmap);
+ wl_signal_add(&layer_surface->surface->events.unmap, &surface->unmap);
surface->destroy.notify = handle_destroy;
wl_signal_add(&layer_surface->events.destroy, &surface->destroy);
surface->new_popup.notify = handle_new_popup;
@@ -360,7 +373,7 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
}
void init_layer_shell(struct wb_server *server) {
- server->layer_shell = wlr_layer_shell_v1_create(server->wl_display);
+ server->layer_shell = wlr_layer_shell_v1_create(server->wl_display, 4);
server->new_layer_surface.notify = handle_layer_shell_surface;
wl_signal_add(&server->layer_shell->events.new_surface,
&server->new_layer_surface);
diff --git a/waybox/layer_shell.h b/waybox/layer_shell.h
index 18b463d..4de9f96 100644
--- a/waybox/layer_shell.h
+++ b/waybox/layer_shell.h
@@ -2,7 +2,7 @@
#define _WB_LAYERS_H
#include
-struct wb_server;
+#include "waybox/output.h"
struct wb_layer_surface {
struct wb_output *output;
diff --git a/waybox/main.c b/waybox/main.c
index 3db1d9f..0bb304b 100644
--- a/waybox/main.c
+++ b/waybox/main.c
@@ -28,8 +28,8 @@ void signal_handler(int sig) {
wl_display_terminate(server.wl_display);
break;
case SIGUSR1:
- /* Openbox uses SIGUSR1 to restart. I'm not sure of the
- * difference between restarting and reconfiguring.
+ /* Openbox uses SIGUSR1 to restart and SIGUSR2 to reconfigure.
+ * What's the difference?
*/
case SIGUSR2:
deinit_config(server.config);
diff --git a/waybox/meson.build b/waybox/meson.build
index ff91dd8..30cfe75 100644
--- a/waybox/meson.build
+++ b/waybox/meson.build
@@ -2,6 +2,7 @@ wb_src = files(
'config.c',
'cursor.c',
'decoration.c',
+ 'idle.c',
'layer_shell.c',
'main.c',
'output.c',
@@ -11,6 +12,8 @@ wb_src = files(
)
wb_dep = [
+ libevdev,
+ libinput,
libxml2,
wayland_server,
wlroots,
@@ -24,4 +27,5 @@ executable(
dependencies: [wb_dep, wlr_protos],
install: true,
install_dir: get_option('prefix') / get_option('libexecdir'),
+ link_args: ['-Wl,-lm'],
)
diff --git a/waybox/output.c b/waybox/output.c
index 3bd99bb..dbffc51 100644
--- a/waybox/output.c
+++ b/waybox/output.c
@@ -10,14 +10,32 @@ void output_frame_notify(struct wl_listener *listener, void *data) {
wlr_output_layout_get_box(output->server->output_layout,
output->wlr_output, &output->geometry);
-#if ! WLR_CHECK_VERSION(0, 17, 0)
- /* Update the background for the current output size. */
- wlr_scene_rect_set_size(output->background,
- output->geometry.width, output->geometry.height);
-#endif
+
+ if (output->gamma_lut_changed) {
+ output->gamma_lut_changed = false;
+ struct wlr_gamma_control_v1 *gamma_control =
+ wlr_gamma_control_manager_v1_get_control(output->server->gamma_control_manager,
+ output->wlr_output);
+ struct wlr_output_state pending;
+ if (!wlr_scene_output_build_state(scene_output, &pending, NULL))
+ return;
+
+ if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) {
+ wlr_output_state_finish(&pending);
+ return;
+ }
+
+ if (!wlr_output_test_state(output->wlr_output, &pending)) {
+ wlr_gamma_control_v1_send_failed_and_destroy(gamma_control);
+ wlr_output_state_finish(&pending);
+ return;
+ }
+
+ wlr_output_state_finish(&pending);
+ }
/* Render the scene if needed and commit the output */
- wlr_scene_output_commit(scene_output);
+ wlr_scene_output_commit(scene_output, NULL);
/* This lets the client know that we've displayed that frame and it can
* prepare another one now if it likes. */
@@ -26,11 +44,39 @@ void output_frame_notify(struct wl_listener *listener, void *data) {
wlr_scene_output_send_frame_done(scene_output, &now);
}
+void output_configuration_applied(struct wl_listener *listener, void *data) {
+ struct wb_server *server = wl_container_of(listener, server, wlr_output_manager);
+ struct wlr_output_configuration_v1 *configuration = data;
+ wlr_output_configuration_v1_send_succeeded(configuration);
+}
+
+void output_configuration_tested(struct wl_listener *listener, void *data) {
+ output_configuration_applied(listener, data);
+}
+
+void output_request_state_notify(struct wl_listener *listener, void *data) {
+ struct wb_output *output = wl_container_of(listener, output, request_state);
+ const struct wlr_output_event_request_state *event = data;
+
+ struct wlr_output_configuration_v1 *configuration = wlr_output_configuration_v1_create();
+ wlr_output_manager_v1_set_configuration(output->server->wlr_output_manager, configuration);
+
+ wlr_output_commit_state(output->wlr_output, event->state);
+}
+
+void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data) {
+ const struct wlr_gamma_control_manager_v1_set_gamma_event *event = data;
+ struct wb_output *output = event->output->data;
+ output->gamma_lut_changed = true;
+ wlr_output_schedule_frame(output->wlr_output);
+}
+
void output_destroy_notify(struct wl_listener *listener, void *data) {
struct wb_output *output = wl_container_of(listener, output, destroy);
wl_list_remove(&output->destroy.link);
wl_list_remove(&output->frame.link);
+ wl_list_remove(&output->request_state.link);
/* Frees the layers */
size_t num_layers = sizeof(output->layers) / sizeof(struct wlr_scene_node *);
@@ -55,29 +101,23 @@ void new_output_notify(struct wl_listener *listener, void *data) {
* and our renderer */
wlr_output_init_render(wlr_output, server->allocator, server->renderer);
- if (!wl_list_empty(&wlr_output->modes)) {
- struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
- wlr_output_set_mode(wlr_output, mode);
- wlr_output_enable(wlr_output, true);
+ struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
+ struct wlr_output_state state;
+ wlr_output_state_init(&state);
+ wlr_output_state_set_enabled(&state, true);
- if (!wlr_output_commit(wlr_output)) {
- wlr_log_errno(WLR_ERROR, "%s", _("Couldn't commit pending frame to output"));
- return;
- }
+ if (mode != NULL) {
+ wlr_output_state_set_mode(&state, mode);
}
+ wlr_output_commit_state(wlr_output, &state);
+ wlr_output_state_finish(&state);
+
struct wb_output *output = calloc(1, sizeof(struct wb_output));
output->server = server;
output->wlr_output = wlr_output;
wlr_output->data = output;
-#if ! WLR_CHECK_VERSION(0, 17, 0)
- /* Set the background color */
- float color[4] = {0.1875, 0.1875, 0.1875, 1.0};
- output->background = wlr_scene_rect_create(&server->scene->tree, 0, 0, color);
- wlr_scene_node_lower_to_bottom(&output->background->node);
-#endif
-
/* Initializes the layers */
size_t num_layers = sizeof(output->layers) / sizeof(struct wlr_scene_node *);
for (size_t i = 0; i < num_layers; i++) {
@@ -91,6 +131,8 @@ void new_output_notify(struct wl_listener *listener, void *data) {
wl_signal_add(&wlr_output->events.destroy, &output->destroy);
output->frame.notify = output_frame_notify;
wl_signal_add(&wlr_output->events.frame, &output->frame);
+ output->request_state.notify = output_request_state_notify;
+ wl_signal_add(&wlr_output->events.request_state, &output->request_state);
/* Adds this to the output layout. The add_auto function arranges outputs
* from left-to-right in the order they appear. A more sophisticated
@@ -101,5 +143,29 @@ void new_output_notify(struct wl_listener *listener, void *data) {
* display, which Wayland clients can see to find out information about the
* output (such as DPI, scale factor, manufacturer, etc).
*/
- wlr_output_layout_add_auto(server->output_layout, wlr_output);
+ struct wlr_output_layout_output *l_output =
+ wlr_output_layout_add_auto(server->output_layout, wlr_output);
+ if (!l_output) {
+ wlr_log(WLR_ERROR, "%s", _("Could not add an output layout."));
+ return;
+ }
+
+ struct wlr_output_configuration_v1 *configuration = wlr_output_configuration_v1_create();
+ wlr_output_configuration_head_v1_create(configuration, wlr_output);
+ wlr_output_manager_v1_set_configuration(server->wlr_output_manager, configuration);
+
+ struct wlr_scene_output *scene_output = wlr_scene_output_create(server->scene, wlr_output);
+ wlr_scene_output_layout_add_output(server->scene_layout, l_output, scene_output);
+}
+
+void init_output(struct wb_server *server) {
+ wl_list_init(&server->outputs);
+
+ server->new_output.notify = new_output_notify;
+ wl_signal_add(&server->backend->events.new_output, &server->new_output);
+
+ server->wlr_output_manager = wlr_output_manager_v1_create(server->wl_display);
+ server->output_configuration_applied.notify = output_configuration_applied;
+ wl_signal_add(&server->wlr_output_manager->events.apply, &server->output_configuration_applied);
+ wl_signal_add(&server->wlr_output_manager->events.test, &server->output_configuration_tested);
}
diff --git a/waybox/seat.c b/waybox/seat.c
index 03c3d0e..129d545 100644
--- a/waybox/seat.c
+++ b/waybox/seat.c
@@ -1,50 +1,58 @@
+#include
#include
+#include
+#if WLR_HAS_LIBINPUT_BACKEND && defined(HAS_LIBINPUT)
+#include
+#else
+#undef HAS_LIBINPUT
+#endif
+#include
#include
#include
#include "waybox/seat.h"
#include "waybox/xdg_shell.h"
-static void deiconify_view(struct wb_view *view) {
- if (view->xdg_toplevel->requested.minimized) {
- view->xdg_toplevel->requested.minimized = false;
- wl_signal_emit(&view->xdg_toplevel->events.request_minimize, NULL);
+static void deiconify_toplevel(struct wb_toplevel *toplevel) {
+ if (toplevel->xdg_toplevel->requested.minimized) {
+ toplevel->xdg_toplevel->requested.minimized = false;
+ wl_signal_emit(&toplevel->xdg_toplevel->events.request_minimize, NULL);
}
}
-static void cycle_views(struct wb_server *server) {
- /* Cycle to the next view */
- if (wl_list_length(&server->views) < 1) {
+static void cycle_toplevels(struct wb_server *server) {
+ /* Cycle to the next toplevel */
+ if (wl_list_length(&server->toplevels) < 1) {
return;
}
- struct wb_view *current_view = wl_container_of(
- server->views.prev, current_view, link);
- deiconify_view(current_view);
- focus_view(current_view, current_view->xdg_toplevel->base->surface);
+ struct wb_toplevel *current_toplevel = wl_container_of(
+ server->toplevels.prev, current_toplevel, link);
+ deiconify_toplevel(current_toplevel);
+ focus_toplevel(current_toplevel);
- /* Move the current view to the beginning of the list */
- wl_list_remove(¤t_view->link);
- wl_list_insert(&server->views, ¤t_view->link);
+ /* Move the current toplevel to the beginning of the list */
+ wl_list_remove(¤t_toplevel->link);
+ wl_list_insert(&server->toplevels, ¤t_toplevel->link);
}
-static void cycle_views_reverse(struct wb_server *server) {
- /* Cycle to the previous view */
- if (wl_list_length(&server->views) < 1) {
+static void cycle_toplevels_reverse(struct wb_server *server) {
+ /* Cycle to the previous toplevel */
+ if (wl_list_length(&server->toplevels) < 1) {
return;
}
- struct wb_view *current_view = wl_container_of(
- server->views.next, current_view, link);
- struct wb_view *next_view = wl_container_of(
- current_view->link.next, next_view, link);
- deiconify_view(next_view);
- focus_view(next_view, next_view->xdg_toplevel->base->surface);
+ struct wb_toplevel *current_toplevel = wl_container_of(
+ server->toplevels.next, current_toplevel, link);
+ struct wb_toplevel *next_toplevel = wl_container_of(
+ current_toplevel->link.next, next_toplevel, link);
+ deiconify_toplevel(next_toplevel);
+ focus_toplevel(next_toplevel);
- /* Move the current view to after the previous view in the list */
- wl_list_remove(¤t_view->link);
- wl_list_insert(server->views.prev, ¤t_view->link);
+ /* Move the current toplevel to after the previous toplevel in the list */
+ wl_list_remove(¤t_toplevel->link);
+ wl_list_insert(server->toplevels.prev, ¤t_toplevel->link);
}
static bool handle_keybinding(struct wb_server *server, xkb_keysym_t sym, uint32_t modifiers) {
@@ -56,14 +64,25 @@ static bool handle_keybinding(struct wb_server *server, xkb_keysym_t sym, uint32
* Returns true if the keybinding is handled, false to send it to the
* client.
*/
+
+ /* TODO: Make these configurable through rc.xml */
+ if (modifiers & (WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT) &&
+ sym >= XKB_KEY_XF86Switch_VT_1 &&
+ sym <= XKB_KEY_XF86Switch_VT_12) {
+ unsigned int vt = sym - XKB_KEY_XF86Switch_VT_1 + 1;
+ wlr_session_change_vt (server->session, vt);
+
+ return true;
+ }
+
if (!server->config) {
/* Some default key bindings, when the rc.xml file can't be
* parsed. */
if (modifiers & WLR_MODIFIER_ALT && sym == XKB_KEY_Tab)
- cycle_views(server);
+ cycle_toplevels(server);
else if (modifiers & (WLR_MODIFIER_ALT|WLR_MODIFIER_SHIFT) &&
sym == XKB_KEY_Tab)
- cycle_views_reverse(server);
+ cycle_toplevels_reverse(server);
else if (sym == XKB_KEY_Escape && modifiers & WLR_MODIFIER_CTRL)
wl_display_terminate(server->wl_display);
else
@@ -75,14 +94,14 @@ static bool handle_keybinding(struct wb_server *server, xkb_keysym_t sym, uint32
wl_list_for_each(key_binding, &server->config->key_bindings, link) {
if (sym == key_binding->sym && modifiers == key_binding->modifiers) {
if (key_binding->action & ACTION_NEXT_WINDOW)
- cycle_views(server);
+ cycle_toplevels(server);
if (key_binding->action & ACTION_PREVIOUS_WINDOW)
- cycle_views_reverse(server);
+ cycle_toplevels_reverse(server);
if (key_binding->action & ACTION_CLOSE) {
- struct wb_view *current_view = wl_container_of(
- server->views.next, current_view, link);
- if (current_view->scene_tree->node.enabled)
- wlr_xdg_toplevel_send_close(current_view->xdg_toplevel);
+ struct wb_toplevel *current_toplevel = wl_container_of(
+ server->toplevels.next, current_toplevel, link);
+ if (current_toplevel->scene_tree->node.enabled)
+ wlr_xdg_toplevel_send_close(current_toplevel->xdg_toplevel);
}
if (key_binding->action & ACTION_EXECUTE) {
if (fork() == 0) {
@@ -90,34 +109,38 @@ static bool handle_keybinding(struct wb_server *server, xkb_keysym_t sym, uint32
}
}
if (key_binding->action & ACTION_TOGGLE_MAXIMIZE) {
- struct wb_view *view = wl_container_of(server->views.next, view, link);
- if (view->scene_tree->node.enabled)
- wl_signal_emit(&view->xdg_toplevel->events.request_maximize, NULL);
+ struct wb_toplevel *toplevel = wl_container_of(server->toplevels.next, toplevel, link);
+ if (toplevel->scene_tree->node.enabled)
+ wl_signal_emit(&toplevel->xdg_toplevel->events.request_maximize, NULL);
}
if (key_binding->action & ACTION_ICONIFY) {
- struct wb_view *view = wl_container_of(server->views.next, view, link);
- if (view->scene_tree->node.enabled) {
- view->xdg_toplevel->requested.minimized = true;
- wl_signal_emit(&view->xdg_toplevel->events.request_minimize, NULL);
+ struct wb_toplevel *toplevel = wl_container_of(server->toplevels.next, toplevel, link);
+ if (toplevel->scene_tree->node.enabled) {
+ toplevel->xdg_toplevel->requested.minimized = true;
+ wl_signal_emit(&toplevel->xdg_toplevel->events.request_minimize, NULL);
}
}
if (key_binding->action & ACTION_SHADE) {
- struct wb_view *view = wl_container_of(server->views.next, view, link);
- if (view->scene_tree->node.enabled) {
+ struct wb_toplevel *toplevel = wl_container_of(server->toplevels.next, toplevel, link);
+ if (toplevel->scene_tree->node.enabled) {
+#if WLR_CHECK_VERSION(0, 19, 0)
+ struct wlr_box geo_box = toplevel->xdg_toplevel->base->geometry;
+#else
struct wlr_box geo_box;
- wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo_box);
- int decoration_height = MAX(geo_box.y - view->geometry.y, TITLEBAR_HEIGHT);
+ wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &geo_box);
+#endif
+ int decoration_height = MAX(geo_box.y - toplevel->geometry.y, TITLEBAR_HEIGHT);
- view->previous_geometry = view->geometry;
- wlr_xdg_toplevel_set_size(view->xdg_toplevel,
- view->geometry.width, decoration_height);
+ toplevel->previous_geometry = toplevel->geometry;
+ wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel,
+ toplevel->geometry.width, decoration_height);
}
}
if (key_binding->action & ACTION_UNSHADE) {
- struct wb_view *view = wl_container_of(server->views.next, view, link);
- if (view->scene_tree->node.enabled) {
- wlr_xdg_toplevel_set_size(view->xdg_toplevel,
- view->previous_geometry.width, view->previous_geometry.height);
+ struct wb_toplevel *toplevel = wl_container_of(server->toplevels.next, toplevel, link);
+ if (toplevel->previous_geometry.height > 0 && toplevel->previous_geometry.width > 0 && toplevel->scene_tree->node.enabled) {
+ wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel,
+ toplevel->previous_geometry.width, toplevel->previous_geometry.height);
}
}
if (key_binding->action & ACTION_RECONFIGURE) {
@@ -250,6 +273,113 @@ static void handle_new_keyboard(struct wb_server *server,
wl_list_insert(&server->seat->keyboards, &keyboard->link);
}
+#ifdef HAS_LIBINPUT
+static bool libinput_config_get_enabled(char *config) {
+ return strcmp(config, "disabled") != 0;
+}
+#endif
+
+static void handle_new_pointer(struct wb_server *server, struct wlr_input_device *device) {
+#ifdef HAS_LIBINPUT
+ struct wb_config *config = server->config;
+ if (wlr_input_device_is_libinput(device) && config->libinput_config.use_config) {
+ struct libinput_device *libinput_handle =
+ wlr_libinput_get_device_handle(device);
+
+ if (config->libinput_config.accel_profile) {
+ enum libinput_config_accel_profile accel_profile =
+ LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
+ if (strcmp(config->libinput_config.accel_profile, "flat") == 0)
+ accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT;
+ else if (strcmp(config->libinput_config.accel_profile, "none") == 0)
+ accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_NONE;
+ libinput_device_config_accel_set_profile(libinput_handle, accel_profile);
+ }
+ if (config->libinput_config.accel_speed) {
+ double accel_speed = strtod(config->libinput_config.accel_speed, NULL);
+ libinput_device_config_accel_set_speed(libinput_handle, accel_speed);
+ }
+ if (config->libinput_config.calibration_matrix) {
+ float matrix[6];
+ unsigned short i = 0;
+ while ((matrix[i] = strtod(strtok(config->libinput_config.calibration_matrix, " "), NULL) && i < 6)) {
+ config->libinput_config.calibration_matrix = NULL;
+ i++;
+ }
+ libinput_device_config_calibration_set_matrix(libinput_handle, matrix);
+ }
+ if (config->libinput_config.click_method) {
+ enum libinput_config_click_method click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
+ if (strcmp(config->libinput_config.click_method, "clickfinger") == 0)
+ click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
+ else if (strcmp(config->libinput_config.click_method, "none") == 0)
+ click_method = LIBINPUT_CONFIG_CLICK_METHOD_NONE;
+ libinput_device_config_click_set_method(libinput_handle, click_method);
+ }
+ if (config->libinput_config.dwt) {
+ libinput_device_config_dwt_set_enabled(libinput_handle,
+ libinput_config_get_enabled(config->libinput_config.dwt));
+ }
+ if (config->libinput_config.dwtp) {
+ libinput_device_config_dwtp_set_enabled(libinput_handle,
+ libinput_config_get_enabled(config->libinput_config.dwtp));
+ }
+ if (config->libinput_config.left_handed) {
+ libinput_device_config_left_handed_set(libinput_handle,
+ libinput_config_get_enabled(config->libinput_config.left_handed));
+ }
+ if (config->libinput_config.middle_emulation) {
+ libinput_device_config_middle_emulation_set_enabled(libinput_handle,
+ libinput_config_get_enabled(config->libinput_config.middle_emulation));
+ }
+ if (config->libinput_config.natural_scroll) {
+ libinput_device_config_scroll_set_natural_scroll_enabled(libinput_handle,
+ libinput_config_get_enabled(config->libinput_config.natural_scroll));
+ }
+ if (config->libinput_config.scroll_button) {
+ int button = libevdev_event_code_from_name(EV_KEY, config->libinput_config.scroll_button);
+ if (button != -1) {
+ libinput_device_config_scroll_set_button(libinput_handle, button);
+ }
+ }
+ if (config->libinput_config.scroll_button_lock) {
+ libinput_device_config_scroll_set_button_lock(libinput_handle,
+ libinput_config_get_enabled(config->libinput_config.scroll_button_lock));
+ }
+ if (config->libinput_config.scroll_method) {
+ enum libinput_config_scroll_method scroll_method = LIBINPUT_CONFIG_SCROLL_2FG;
+ if (strcmp(config->libinput_config.scroll_method, "edge") == 0)
+ scroll_method = LIBINPUT_CONFIG_SCROLL_EDGE;
+ else if (strcmp(config->libinput_config.scroll_method, "none") == 0)
+ scroll_method = LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
+ else if (strcmp(config->libinput_config.scroll_method, "button") == 0)
+ scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
+ libinput_device_config_scroll_set_method(libinput_handle, scroll_method);
+ }
+ if (config->libinput_config.tap) {
+ libinput_device_config_tap_set_enabled(libinput_handle,
+ libinput_config_get_enabled(config->libinput_config.tap));
+ }
+ if (config->libinput_config.tap_button_map) {
+ enum libinput_config_tap_button_map map = LIBINPUT_CONFIG_TAP_MAP_LRM;
+ if (strcmp(config->libinput_config.tap_button_map, "lmr") == 0)
+ map = LIBINPUT_CONFIG_TAP_MAP_LMR;
+ libinput_device_config_tap_set_button_map(libinput_handle, map);
+ }
+ if (config->libinput_config.tap_drag) {
+ libinput_device_config_tap_set_drag_enabled(libinput_handle,
+ libinput_config_get_enabled(config->libinput_config.tap_drag));
+ };
+ if (config->libinput_config.tap_drag_lock) {
+ libinput_device_config_tap_set_drag_lock_enabled(libinput_handle,
+ libinput_config_get_enabled(config->libinput_config.tap_drag_lock));
+ };
+ }
+#endif
+
+ wlr_cursor_attach_input_device(server->cursor->cursor, device);
+}
+
static void new_input_notify(struct wl_listener *listener, void *data) {
struct wlr_input_device *device = data;
struct wb_server *server = wl_container_of(listener, server, new_input);
@@ -260,7 +390,7 @@ static void new_input_notify(struct wl_listener *listener, void *data) {
break;
case WLR_INPUT_DEVICE_POINTER:
wlr_log(WLR_INFO, "%s: %s", _("New pointer detected"), device->name);
- wlr_cursor_attach_input_device(server->cursor->cursor, device);
+ handle_new_pointer(server, device);
break;
default:
wlr_log(WLR_INFO, "%s: %s", _("Unsupported input device detected"), device->name);
@@ -293,7 +423,7 @@ void seat_set_focus_layer(struct wb_seat *seat, struct wlr_layer_surface_v1 *lay
return;
}
seat_focus_surface(seat, layer->surface);
- if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) {
+ if (layer->current.layer > ZWLR_LAYER_SHELL_V1_LAYER_TOP) {
seat->focused_layer = layer;
}
}
diff --git a/waybox/server.c b/waybox/server.c
index d103aad..ece36c4 100644
--- a/waybox/server.c
+++ b/waybox/server.c
@@ -1,5 +1,13 @@
+#include
+#include
+#include
+
+#include "idle.h"
#include "waybox/server.h"
#include "waybox/xdg_shell.h"
+#if WLR_CHECK_VERSION(0, 19, 0)
+# include
+#endif
bool wb_create_backend(struct wb_server* server) {
/* The Wayland display is managed by libwayland. It handles accepting
@@ -10,15 +18,17 @@ bool wb_create_backend(struct wb_server* server) {
return false;
}
+ server->wl_event_loop = wl_display_get_event_loop(server->wl_display);
+ if (server->wl_event_loop == NULL) {
+ wlr_log(WLR_ERROR, "%s", _("Failed to get an event loop"));
+ return false;
+ }
+
/* The backend is a wlroots feature which abstracts the underlying input and
* output hardware. The autocreate option will choose the most suitable
* backend based on the current environment, such as opening an X11 window
* if an X11 server is running. */
-#if ! WLR_CHECK_VERSION(0, 13, 0) || WLR_CHECK_VERSION(0, 17, 0)
- server->backend = wlr_backend_autocreate(server->wl_display, NULL);
-#else
- server->backend = wlr_backend_autocreate(server->wl_display);
-#endif
+ server->backend = wlr_backend_autocreate(server->wl_event_loop, &server->session);
if (server->backend == NULL) {
wlr_log(WLR_ERROR, "%s", _("Failed to create backend"));
return false;
@@ -46,10 +56,10 @@ bool wb_create_backend(struct wb_server* server) {
return false;
}
- server->compositor = wlr_compositor_create(server->wl_display,
- server->renderer);
+ server->compositor =
+ wlr_compositor_create(server->wl_display, 5, server->renderer);
server->subcompositor = wlr_subcompositor_create(server->wl_display);
- server->output_layout = wlr_output_layout_create();
+ server->output_layout = wlr_output_layout_create(server->wl_display);
server->seat = wb_seat_create(server);
server->cursor = wb_cursor_create(server);
@@ -61,10 +71,7 @@ bool wb_create_backend(struct wb_server* server) {
bool wb_start_server(struct wb_server* server) {
init_config(server);
- wl_list_init(&server->outputs);
-
- server->new_output.notify = new_output_notify;
- wl_signal_add(&server->backend->events.new_output, &server->new_output);
+ init_output(server);
/* Create a scene graph. This is a wlroots abstraction that handles all
* rendering and damage tracking. All the compositor author needs to do
@@ -73,7 +80,8 @@ bool wb_start_server(struct wb_server* server) {
* necessary.
*/
server->scene = wlr_scene_create();
- wlr_scene_attach_output_layout(server->scene, server->output_layout);
+ server->scene_layout =
+ wlr_scene_attach_output_layout(server->scene, server->output_layout);
const char *socket = wl_display_add_socket_auto(server->wl_display);
if (!socket) {
@@ -91,23 +99,38 @@ bool wb_start_server(struct wb_server* server) {
wlr_log(WLR_INFO, "%s: WAYLAND_DISPLAY=%s", _("Running Wayland compositor on Wayland display"), socket);
setenv("WAYLAND_DISPLAY", socket, true);
- wlr_gamma_control_manager_v1_create(server->wl_display);
- wlr_screencopy_manager_v1_create(server->wl_display);
- server->idle_notifier = wlr_idle_notifier_v1_create(server->wl_display);
-
+ wlr_data_control_manager_v1_create(server->wl_display);
wlr_data_device_manager_create(server->wl_display);
- wl_list_init(&server->views);
+
+ server->foreign_toplevel_list =
+ wlr_ext_foreign_toplevel_list_v1_create(server->wl_display, 1);
+
+ server->gamma_control_manager =
+ wlr_gamma_control_manager_v1_create(server->wl_display);
+ server->gamma_control_set_gamma.notify = handle_gamma_control_set_gamma;
+ wl_signal_add(&server->gamma_control_manager->events.set_gamma, &server->gamma_control_set_gamma);
+
+ wlr_screencopy_manager_v1_create(server->wl_display);
+ create_idle_manager(server);
+
+ wl_list_init(&server->toplevels);
init_xdg_decoration(server);
init_layer_shell(server);
/* Set up the xdg-shell. The xdg-shell is a Wayland protocol which is used
- * for application windows. For more detail on shells, refer to Drew
- * DeVault's article:
- *
+ * for application windows. For more detail on shells, refer to
* https://drewdevault.com/2018/07/29/Wayland-shells.html
*/
init_xdg_shell(server);
+ wlr_fractional_scale_manager_v1_create(server->wl_display, 1);
+ wlr_viewporter_create(server->wl_display);
+#if WLR_CHECK_VERSION(0, 19, 0)
+ struct wlr_xdg_toplevel_icon_manager_v1 * icon_manager = wlr_xdg_toplevel_icon_manager_v1_create(server->wl_display, 1);
+ int sizes[] = {16, 24, 32, 48, 64};
+ wlr_xdg_toplevel_icon_manager_v1_set_sizes(icon_manager, (int *) sizes, 5);
+#endif
+
return true;
}
@@ -116,9 +139,27 @@ bool wb_terminate(struct wb_server* server) {
wl_list_remove(&server->new_xdg_decoration.link); /* wb_decoration_destroy */
deinit_config(server->config);
wl_display_destroy_clients(server->wl_display);
- wl_display_destroy(server->wl_display);
- wb_seat_destroy(server->seat);
wlr_output_layout_destroy(server->output_layout);
+ wlr_allocator_destroy(server->allocator);
+ wlr_renderer_destroy(server->renderer);
+
+ wl_list_remove(&server->new_input.link);
+ wl_list_remove(&server->new_output.link);
+ wl_list_remove(&server->output_configuration_applied.link);
+ wl_list_remove(&server->output_configuration_tested.link);
+ wl_list_remove(&server->new_inhibitor.link);
+ wl_list_remove(&server->inhibitors);
+ wl_list_remove(&server->destroy_inhibit_manager.link);
+ wl_list_remove(&server->gamma_control_set_gamma.link);
+ wl_list_remove(&server->new_layer_surface.link);
+
+ wl_list_remove(&server->new_xdg_toplevel.link);
+ wl_list_remove(&server->new_xdg_popup.link);
+
+ wlr_backend_destroy(server->backend);
+ wb_seat_destroy(server->seat);
+ wl_display_destroy(server->wl_display);
+ wlr_scene_node_destroy(&server->scene->tree.node);
wlr_log(WLR_INFO, "%s", _("Display destroyed"));
diff --git a/waybox/xdg_shell.c b/waybox/xdg_shell.c
index 1cf6402..97bf259 100644
--- a/waybox/xdg_shell.c
+++ b/waybox/xdg_shell.c
@@ -1,11 +1,12 @@
+#include "idle.h"
#include "waybox/xdg_shell.h"
-struct wb_view *get_view_at(
+struct wb_toplevel *get_toplevel_at(
struct wb_server *server, double lx, double ly,
struct wlr_surface **surface, double *sx, double *sy) {
/* This returns the topmost node in the scene at the given layout coords.
* we only care about surface nodes as we are specifically looking for a
- * surface in the surface tree of a wb_view. */
+ * surface in the surface tree of a wb_toplevel. */
struct wlr_scene_node *node =
wlr_scene_node_at(&server->scene->tree.node, lx, ly, sx, sy);
if (node == NULL || node->type != WLR_SCENE_NODE_BUFFER) {
@@ -13,13 +14,13 @@ struct wb_view *get_view_at(
}
struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
struct wlr_scene_surface *scene_surface =
- wlr_scene_surface_from_buffer(scene_buffer);
+ wlr_scene_surface_try_from_buffer(scene_buffer);
if (!scene_surface) {
return NULL;
}
*surface = scene_surface->surface;
- /* Find the node corresponding to the wb_view at the root of this
+ /* Find the node corresponding to the wb_toplevel at the root of this
* surface tree, it is the only one for which we set the data field. */
struct wlr_scene_tree *tree = node->parent;
while (tree != NULL && tree->node.data == NULL) {
@@ -28,62 +29,67 @@ struct wb_view *get_view_at(
return tree->node.data;
}
-void focus_view(struct wb_view *view, struct wlr_surface *surface) {
+void focus_toplevel(struct wb_toplevel *toplevel) {
/* Note: this function only deals with keyboard focus. */
- if (view == NULL || surface == NULL || !wlr_surface_is_xdg_surface(surface)) {
+ if (toplevel == NULL || toplevel->xdg_toplevel->base->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
return;
}
- struct wlr_xdg_surface *xdg_surface = wlr_xdg_surface_from_wlr_surface(surface);
- if (xdg_surface)
+ struct wlr_surface *surface = toplevel->xdg_toplevel->base->surface;
+ struct wlr_xdg_surface *xdg_surface = wlr_xdg_surface_try_from_wlr_surface(surface);
+ if (xdg_surface != NULL)
wlr_log(WLR_INFO, "%s: %s", _("Keyboard focus is now on surface"),
xdg_surface->toplevel->app_id);
- struct wb_server *server = view->server;
+ struct wb_server *server = toplevel->server;
+ if (server->seat->focused_layer != NULL) {
+ /* If a layer is focused, don't focus a toplevel. */
+ return;
+ }
struct wlr_seat *seat = server->seat->seat;
struct wlr_surface *prev_surface = seat->keyboard_state.focused_surface;
if (prev_surface == surface) {
- /* Don't re-focus an already focused surface. */
+ /* Don't focus a surface that's already focused. */
return;
}
- if (prev_surface && wlr_surface_is_xdg_surface(prev_surface)) {
+ if (prev_surface != NULL) {
/*
* Deactivate the previously focused surface. This lets the client know
* it no longer has focus and the client will repaint accordingly, e.g.
* stop displaying a caret.
*/
- struct wlr_xdg_surface *previous =
- wlr_xdg_surface_from_wlr_surface(prev_surface);
- wlr_xdg_toplevel_set_activated(previous->toplevel, false);
+ struct wlr_xdg_toplevel *prev_toplevel =
+ wlr_xdg_toplevel_try_from_wlr_surface(prev_surface);
+ if (prev_toplevel != NULL) {
+ wlr_xdg_toplevel_set_activated(prev_toplevel, false);
+ }
}
- /* Move the view to the front */
- if (!server->seat->focused_layer) {
- wlr_scene_node_raise_to_top(&view->scene_tree->node);
- }
- wl_list_remove(&view->link);
- wl_list_insert(&server->views, &view->link);
+ /* Move the toplevel to the front */
+ wlr_scene_node_raise_to_top(&toplevel->scene_tree->node);
+ wl_list_remove(&toplevel->link);
+ wl_list_insert(&server->toplevels, &toplevel->link);
/* Activate the new surface */
- wlr_xdg_toplevel_set_activated(view->xdg_toplevel, true);
+ wlr_xdg_toplevel_set_activated(toplevel->xdg_toplevel, true);
/*
* Tell the seat to have the keyboard enter this surface. wlroots will keep
* track of this and automatically send key events to the appropriate
* clients without additional work on your part.
*/
- seat_focus_surface(server->seat, view->xdg_toplevel->base->surface);
+ seat_focus_surface(server->seat, surface);
}
-struct wlr_output *get_active_output(struct wb_view *view) {
+struct wlr_output *get_active_output(struct wb_toplevel *toplevel) {
double closest_x, closest_y;
struct wlr_output *output = NULL;
- wlr_output_layout_closest_point(view->server->output_layout, output,
- view->geometry.x + view->geometry.width / 2,
- view->geometry.y + view->geometry.height / 2,
+ wlr_output_layout_closest_point(toplevel->server->output_layout, output,
+ toplevel->geometry.x + toplevel->geometry.width / 2,
+ toplevel->geometry.y + toplevel->geometry.height / 2,
&closest_x, &closest_y);
- return wlr_output_layout_output_at(view->server->output_layout, closest_x, closest_y);
+ return wlr_output_layout_output_at(toplevel->server->output_layout, closest_x, closest_y);
}
-static struct wlr_box get_usable_area(struct wb_view *view) {
- struct wlr_output *output = get_active_output(view);
+static struct wlr_box get_usable_area(struct wb_toplevel *toplevel) {
+ struct wlr_output *output = get_active_output(toplevel);
struct wlr_box usable_area = {0};
wlr_output_effective_resolution(output, &usable_area.width, &usable_area.height);
return usable_area;
@@ -91,89 +97,155 @@ static struct wlr_box get_usable_area(struct wb_view *view) {
static void xdg_toplevel_map(struct wl_listener *listener, void *data) {
/* Called when the surface is mapped, or ready to display on-screen. */
- struct wb_view *view = wl_container_of(listener, view, map);
- if (view->xdg_toplevel->base->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL)
+ struct wb_toplevel *toplevel = wl_container_of(listener, toplevel, map);
+ if (toplevel->xdg_toplevel->base->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL)
return;
- struct wb_config *config = view->server->config;
+ struct wb_config *config = toplevel->server->config;
+ struct wlr_box usable_area = get_usable_area(toplevel);
+#if WLR_CHECK_VERSION(0, 19, 0)
+ struct wlr_box geo_box = toplevel->xdg_toplevel->base->geometry;
+#else
struct wlr_box geo_box = {0};
- struct wlr_box usable_area = get_usable_area(view);
- wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo_box);
+ wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &geo_box);
+#endif
if (config) {
- view->geometry.height = MIN(geo_box.height,
+ toplevel->geometry.height = MIN(geo_box.height,
usable_area.height - config->margins.top - config->margins.bottom);
- view->geometry.width = MIN(geo_box.width,
+ toplevel->geometry.width = MIN(geo_box.width,
usable_area.width - config->margins.left - config->margins.right);
- view->geometry.x = config->margins.left;
- view->geometry.y = config->margins.top;
+ toplevel->geometry.x = config->margins.left;
+ toplevel->geometry.y = config->margins.top;
} else {
- view->geometry.height = MIN(geo_box.height, usable_area.height);
- view->geometry.width = MIN(geo_box.width, usable_area.width);
- view->geometry.x = 0;
- view->geometry.y = 0;
+ toplevel->geometry.height = MIN(geo_box.height, usable_area.height);
+ toplevel->geometry.width = MIN(geo_box.width, usable_area.width);
+ toplevel->geometry.x = 0;
+ toplevel->geometry.y = 0;
}
- /* A view no larger than a title bar shouldn't be sized or focused */
- if (view->geometry.height > TITLEBAR_HEIGHT &&
- view->geometry.height > TITLEBAR_HEIGHT *
- (usable_area.width / usable_area.height)) {
- wlr_xdg_toplevel_set_size(view->xdg_toplevel,
- view->geometry.width, view->geometry.height);
- focus_view(view, view->xdg_toplevel->base->surface);
- }
+ wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel,
+ toplevel->geometry.width, toplevel->geometry.height);
+ focus_toplevel(toplevel);
- wlr_scene_node_set_position(&view->scene_tree->node,
- view->geometry.x, view->geometry.y);
+ wlr_scene_node_set_position(&toplevel->scene_tree->node,
+ toplevel->geometry.x, toplevel->geometry.y);
}
static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) {
/* Called when the surface is unmapped, and should no longer be shown. */
- struct wb_view *view = wl_container_of(listener, view, unmap);
- if (view->xdg_toplevel->base->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL)
+ struct wb_toplevel *toplevel = wl_container_of(listener, toplevel, unmap);
+ if (toplevel->xdg_toplevel->base->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL)
return;
- reset_cursor_mode(view->server);
+ reset_cursor_mode(toplevel->server);
- /* Focus the next view, if any. */
- struct wb_view *next_view = wl_container_of(view->link.next, next_view, link);
- if (next_view && next_view->scene_tree && next_view->scene_tree->node.enabled) {
- wlr_log(WLR_INFO, "%s: %s", _("Focusing next view"),
- next_view->xdg_toplevel->app_id);
- focus_view(next_view, next_view->xdg_toplevel->base->surface);
+ /* Focus the next toplevel, if any. */
+ if (wl_list_length(&toplevel->link) > 1) {
+ struct wb_toplevel *next_toplevel = wl_container_of(toplevel->link.next, next_toplevel, link);
+ if (next_toplevel && next_toplevel->xdg_toplevel && next_toplevel->scene_tree && next_toplevel->scene_tree->node.enabled) {
+ wlr_log(WLR_INFO, "%s: %s", _("Focusing next toplevel"),
+ next_toplevel->xdg_toplevel->app_id);
+ focus_toplevel(next_toplevel);
+ }
+ }
+}
+
+static void update_fractional_scale(struct wlr_surface *surface) {
+ float scale = 1;
+ struct wlr_surface_output *surface_output;
+ wl_list_for_each(surface_output, &surface->current_outputs, link) {
+ if (surface_output->output->scale > scale) {
+ scale = surface_output->output->scale;
+ }
+ }
+ wlr_fractional_scale_v1_notify_scale(surface, scale);
+ wlr_surface_set_preferred_buffer_scale(surface, ceil(scale));
+}
+
+static void xdg_toplevel_commit(struct wl_listener *listener, void *data) {
+ struct wb_toplevel *toplevel = wl_container_of(listener, toplevel, commit);
+ struct wlr_xdg_surface *base = toplevel->xdg_toplevel->base;
+
+ struct wlr_output *output = get_active_output(toplevel);
+ wlr_surface_send_enter(base->surface, output);
+ update_fractional_scale(base->surface);
+
+ if (base->initial_commit) {
+ wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel, 0, 0);
+ if (toplevel->decoration != NULL)
+ wl_signal_emit(&toplevel->decoration->events.request_mode, toplevel->decoration);
}
}
static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) {
- /* Called when the surface is destroyed and should never be shown again. */
- struct wb_view *view = wl_container_of(listener, view, destroy);
+ /* Called when the xdg_toplevel is destroyed and should never be shown again. */
+ struct wb_toplevel *toplevel = wl_container_of(listener, toplevel, destroy);
- wl_list_remove(&view->map.link);
- wl_list_remove(&view->unmap.link);
- wl_list_remove(&view->destroy.link);
- wl_list_remove(&view->new_popup.link);
+ struct wlr_output *output = get_active_output(toplevel);
+ struct wlr_xdg_surface *base = toplevel->xdg_toplevel->base;
+ wlr_surface_send_leave(base->surface, output);
+ update_fractional_scale(base->surface);
+ wlr_ext_foreign_toplevel_handle_v1_destroy(toplevel->foreign_toplevel_handle);
- if (view->xdg_toplevel->base->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
- wl_list_remove(&view->request_fullscreen.link);
- wl_list_remove(&view->request_minimize.link);
- wl_list_remove(&view->request_maximize.link);
- wl_list_remove(&view->request_move.link);
- wl_list_remove(&view->request_resize.link);
- wl_list_remove(&view->link);
- }
+ wl_list_remove(&toplevel->map.link);
+ wl_list_remove(&toplevel->unmap.link);
+ wl_list_remove(&toplevel->commit.link);
+ wl_list_remove(&toplevel->destroy.link);
+ wl_list_remove(&toplevel->new_popup.link);
- free(view);
+ wl_list_remove(&toplevel->request_fullscreen.link);
+ wl_list_remove(&toplevel->request_minimize.link);
+ wl_list_remove(&toplevel->request_maximize.link);
+ wl_list_remove(&toplevel->request_move.link);
+ wl_list_remove(&toplevel->request_resize.link);
+ wl_list_remove(&toplevel->set_app_id.link);
+ wl_list_remove(&toplevel->set_title.link);
+
+ wl_list_remove(&toplevel->link);
+ free(toplevel);
+}
+
+static void xdg_toplevel_set_app_id(
+ struct wl_listener *listener, void *data) {
+ struct wb_toplevel *toplevel =
+ wl_container_of(listener, toplevel, set_app_id);
+ toplevel->foreign_toplevel_state.app_id = toplevel->xdg_toplevel->app_id;
+ wlr_ext_foreign_toplevel_handle_v1_update_state(
+ toplevel->foreign_toplevel_handle, &toplevel->foreign_toplevel_state);
+}
+
+static void xdg_toplevel_set_title(
+ struct wl_listener *listener, void *data) {
+ struct wb_toplevel *toplevel =
+ wl_container_of(listener, toplevel, set_title);
+ toplevel->foreign_toplevel_state.title = toplevel->xdg_toplevel->title;
+ wlr_ext_foreign_toplevel_handle_v1_update_state(
+ toplevel->foreign_toplevel_handle, &toplevel->foreign_toplevel_state);
}
static void xdg_toplevel_request_fullscreen(
struct wl_listener *listener, void *data) {
/* This event is raised when a client would like to set itself to
- * fullscreen. waybox currently doesn't support fullscreen, but to
- * conform to xdg-shell protocol we still must send a configure.
- * wlr_xdg_surface_schedule_configure() is used to send an empty reply.
- */
- struct wb_view *view =
- wl_container_of(listener, view, request_fullscreen);
- wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base);
+ * fullscreen. */
+ struct wb_toplevel *toplevel =
+ wl_container_of(listener, toplevel, request_fullscreen);
+ bool is_fullscreen = toplevel->xdg_toplevel->current.fullscreen;
+ if (!is_fullscreen) {
+ struct wlr_output *wlr_output = get_active_output(toplevel);
+ struct wb_output *output = wlr_output->data;
+ toplevel->previous_geometry = toplevel->geometry;
+ toplevel->geometry.x = 0;
+ toplevel->geometry.y = 0;
+ toplevel->geometry.height = output->geometry.height;
+ toplevel->geometry.width = output->geometry.width;
+ wlr_scene_node_raise_to_top(&toplevel->scene_tree->node);
+ } else {
+ toplevel->geometry = toplevel->previous_geometry;
+ }
+
+ wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel, toplevel->geometry.width, toplevel->geometry.height);
+ wlr_xdg_toplevel_set_fullscreen(toplevel->xdg_toplevel, !is_fullscreen);
+ wlr_scene_node_set_position(&toplevel->scene_tree->node, toplevel->geometry.x, toplevel->geometry.y);
}
static void xdg_toplevel_request_maximize(struct wl_listener *listener, void *data) {
@@ -181,85 +253,83 @@ static void xdg_toplevel_request_maximize(struct wl_listener *listener, void *da
* typically because the user clicked on the maximize button on
* client-side decorations.
*/
- struct wb_view *view = wl_container_of(listener, view, request_maximize);
- struct wlr_box usable_area = get_usable_area(view);
+ struct wb_toplevel *toplevel = wl_container_of(listener, toplevel, request_maximize);
+ struct wlr_box usable_area = get_usable_area(toplevel);
- bool is_maximized = view->xdg_toplevel->current.maximized;
+ bool is_maximized = toplevel->xdg_toplevel->current.maximized;
if (!is_maximized) {
- struct wb_config *config = view->server->config;
- view->previous_geometry = view->geometry;
+ struct wb_config *config = toplevel->server->config;
+ toplevel->previous_geometry = toplevel->geometry;
if (config) {
- view->geometry.x = config->margins.left;
- view->geometry.y = config->margins.top;
+ toplevel->geometry.x = config->margins.left;
+ toplevel->geometry.y = config->margins.top;
usable_area.height -= config->margins.top + config->margins.bottom;
usable_area.width -= config->margins.left + config->margins.right;
} else {
- view->geometry.x = 0;
- view->geometry.y = 0;
+ toplevel->geometry.x = 0;
+ toplevel->geometry.y = 0;
}
} else {
- usable_area = view->previous_geometry;
- view->geometry.x = view->previous_geometry.x;
- view->geometry.y = view->previous_geometry.y;
+ usable_area = toplevel->previous_geometry;
+ toplevel->geometry.x = toplevel->previous_geometry.x;
+ toplevel->geometry.y = toplevel->previous_geometry.y;
}
- wlr_xdg_toplevel_set_size(view->xdg_toplevel, usable_area.width, usable_area.height);
- wlr_xdg_toplevel_set_maximized(view->xdg_toplevel, !is_maximized);
- wlr_scene_node_set_position(&view->scene_tree->node,
- view->geometry.x, view->geometry.y);
+ wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel, usable_area.width, usable_area.height);
+ wlr_xdg_toplevel_set_maximized(toplevel->xdg_toplevel, !is_maximized);
+ wlr_scene_node_set_position(&toplevel->scene_tree->node,
+ toplevel->geometry.x, toplevel->geometry.y);
}
static void xdg_toplevel_request_minimize(struct wl_listener *listener, void *data) {
- struct wb_view *view = wl_container_of(listener, view, request_minimize);
- bool minimize_requested = view->xdg_toplevel->requested.minimized;
+ struct wb_toplevel *toplevel = wl_container_of(listener, toplevel, request_minimize);
+ bool minimize_requested = toplevel->xdg_toplevel->requested.minimized;
if (minimize_requested) {
- view->previous_geometry = view->geometry;
- view->geometry.y = -view->geometry.height;
+ toplevel->previous_geometry = toplevel->geometry;
+ toplevel->geometry.y = -toplevel->geometry.height;
- struct wb_view *next_view = wl_container_of(view->link.next, next_view, link);
- if (wl_list_length(&view->link) > 1)
- focus_view(next_view, next_view->xdg_toplevel->base->surface);
+ struct wb_toplevel *next_toplevel = wl_container_of(toplevel->link.next, next_toplevel, link);
+ if (wl_list_length(&toplevel->link) > 1)
+ focus_toplevel(next_toplevel);
else
- focus_view(view, view->xdg_toplevel->base->surface);
+ focus_toplevel(toplevel);
} else {
- view->geometry = view->previous_geometry;
+ toplevel->geometry = toplevel->previous_geometry;
}
- wlr_scene_node_set_position(&view->scene_tree->node,
- view->geometry.x, view->geometry.y);
+ wlr_scene_node_set_position(&toplevel->scene_tree->node,
+ toplevel->geometry.x, toplevel->geometry.y);
}
-static void begin_interactive(struct wb_view *view,
+static void begin_interactive(struct wb_toplevel *toplevel,
enum wb_cursor_mode mode, uint32_t edges) {
/* This function sets up an interactive move or resize operation, where the
* compositor stops propagating pointer events to clients and instead
* consumes them itself, to move or resize windows. */
- struct wb_server *server = view->server;
- struct wlr_surface *focused_surface =
- server->seat->seat->pointer_state.focused_surface;
- if (view->xdg_toplevel->base->surface != wlr_surface_get_root_surface(focused_surface)) {
- /* Deny move/resize requests from unfocused clients. */
- return;
- }
- server->grabbed_view = view;
+ struct wb_server *server = toplevel->server;
+ server->grabbed_toplevel = toplevel;
server->cursor->cursor_mode = mode;
if (mode == WB_CURSOR_MOVE) {
- server->grab_x = server->cursor->cursor->x - view->geometry.x;
- server->grab_y = server->cursor->cursor->y - view->geometry.y;
+ server->grab_x = server->cursor->cursor->x - toplevel->geometry.x;
+ server->grab_y = server->cursor->cursor->y - toplevel->geometry.y;
} else if (mode == WB_CURSOR_RESIZE) {
+#if WLR_CHECK_VERSION(0, 19, 0)
+ struct wlr_box geo_box = toplevel->xdg_toplevel->base->geometry;
+#else
struct wlr_box geo_box;
- wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo_box);
+ wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &geo_box);
+#endif
- double border_x = (view->geometry.x + geo_box.x) +
+ double border_x = (toplevel->geometry.x + geo_box.x) +
((edges & WLR_EDGE_RIGHT) ? geo_box.width : 0);
- double border_y = (view->geometry.y + geo_box.y) +
+ double border_y = (toplevel->geometry.y + geo_box.y) +
((edges & WLR_EDGE_BOTTOM) ? geo_box.height : 0);
server->grab_x = server->cursor->cursor->x - border_x;
server->grab_y = server->cursor->cursor->y - border_y;
server->grab_geo_box = geo_box;
- server->grab_geo_box.x += view->geometry.x;
- server->grab_geo_box.y += view->geometry.y;
+ server->grab_geo_box.x += toplevel->geometry.x;
+ server->grab_geo_box.y += toplevel->geometry.y;
server->resize_edges = edges;
}
@@ -270,8 +340,8 @@ static void xdg_toplevel_request_move(
/* This event is raised when a client would like to begin an interactive
* move, typically because the user clicked on their client-side
* decorations. */
- struct wb_view *view = wl_container_of(listener, view, request_move);
- begin_interactive(view, WB_CURSOR_MOVE, 0);
+ struct wb_toplevel *toplevel = wl_container_of(listener, toplevel, request_move);
+ begin_interactive(toplevel, WB_CURSOR_MOVE, 0);
}
static void xdg_toplevel_request_resize(
@@ -280,100 +350,135 @@ static void xdg_toplevel_request_resize(
* resize, typically because the user clicked on their client-side
* decorations. */
struct wlr_xdg_toplevel_resize_event *event = data;
- struct wb_view *view = wl_container_of(listener, view, request_resize);
- begin_interactive(view, WB_CURSOR_RESIZE, event->edges);
+ struct wb_toplevel *toplevel = wl_container_of(listener, toplevel, request_resize);
+ begin_interactive(toplevel, WB_CURSOR_RESIZE, event->edges);
+}
+
+static void xdg_popup_commit(struct wl_listener *listener, void *data) {
+ struct wb_popup *popup = wl_container_of(listener, popup, commit);
+ if (!popup->xdg_popup) return;
+ struct wlr_xdg_surface *base = popup->xdg_popup->base;
+
+ if (base && base->initial_commit) {
+ update_fractional_scale(base->surface);
+ wlr_xdg_surface_schedule_configure(base);
+ }
+}
+
+static void xdg_popup_destroy(struct wl_listener *listener, void *data) {
+ struct wb_popup *popup = wl_container_of(listener, popup, destroy);
+ if (popup->xdg_popup)
+ update_fractional_scale(popup->xdg_popup->base->surface);
+
+ wl_list_remove(&popup->commit.link);
+ wl_list_remove(&popup->destroy.link);
+ free(popup);
}
static void handle_new_popup(struct wl_listener *listener, void *data) {
struct wlr_xdg_popup *popup = data;
- struct wb_view *view = wl_container_of(listener, view, new_popup);
+ struct wb_toplevel *toplevel = wl_container_of(listener, toplevel, new_popup);
struct wlr_output *wlr_output = wlr_output_layout_output_at(
- view->server->output_layout,
- view->geometry.x + popup->current.geometry.x,
- view->geometry.y + popup->current.geometry.y);
+ toplevel->server->output_layout,
+ toplevel->geometry.x + popup->current.geometry.x,
+ toplevel->geometry.y + popup->current.geometry.y);
- if (!wlr_output) return;
+ if (!wlr_output) {
+ return;
+ }
struct wb_output *output = wlr_output->data;
- int top_margin = (view->server->config) ?
- view->server->config->margins.top : 0;
+ int top_margin = (toplevel->server->config) ?
+ toplevel->server->config->margins.top : 0;
struct wlr_box output_toplevel_box = {
- .x = output->geometry.x - view->geometry.x,
- .y = output->geometry.y - view->geometry.y,
+ .x = output->geometry.x - toplevel->geometry.x,
+ .y = output->geometry.y - toplevel->geometry.y,
.width = output->geometry.width,
.height = output->geometry.height - top_margin,
};
wlr_xdg_popup_unconstrain_from_box(popup, &output_toplevel_box);
}
-static void handle_new_xdg_surface(struct wl_listener *listener, void *data) {
- /* This event is raised when wlr_xdg_shell receives a new xdg surface from a
- * client, either a toplevel (application window) or popup. */
- struct wb_server *server =
- wl_container_of(listener, server, new_xdg_surface);
- struct wlr_xdg_surface *xdg_surface = data;
-
+static void handle_new_xdg_popup(struct wl_listener *listener, void *data) {
/* We must add xdg popups to the scene graph so they get rendered. The
* wlroots scene graph provides a helper for this, but to use it we must
* provide the proper parent scene node of the xdg popup. To enable this,
* we always set the user data field of xdg_surfaces to the corresponding
* scene node. */
- if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
- if (wlr_surface_is_xdg_surface(xdg_surface->popup->parent)) {
- struct wlr_xdg_surface *parent = wlr_xdg_surface_from_wlr_surface(
- xdg_surface->popup->parent);
+ struct wlr_xdg_popup *xdg_popup = data;
+ if (xdg_popup->parent) {
+ struct wlr_xdg_surface *parent = wlr_xdg_surface_try_from_wlr_surface(
+ xdg_popup->parent);
+ if (parent != NULL) {
struct wlr_scene_tree *parent_tree = parent->data;
- xdg_surface->data = wlr_scene_xdg_surface_create(
- parent_tree, xdg_surface);
+ xdg_popup->base->data = wlr_scene_xdg_surface_create(
+ parent_tree, xdg_popup->base);
}
- /* The scene graph doesn't currently unconstrain popups, so keep going */
- /* return; */
}
- if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_NONE)
- return;
- /* Allocate a wb_view for this surface */
- struct wb_view *view =
- calloc(1, sizeof(struct wb_view));
- view->server = server;
- view->xdg_toplevel = xdg_surface->toplevel;
+ struct wb_popup *popup = calloc(1, sizeof(struct wb_popup));
+ popup->commit.notify = xdg_popup_commit;
+ wl_signal_add(&xdg_popup->base->surface->events.commit, &popup->commit);
+
+ popup->destroy.notify = xdg_popup_destroy;
+ wl_signal_add(&xdg_popup->events.destroy, &popup->destroy);
+}
+
+static void handle_new_xdg_toplevel(struct wl_listener *listener, void *data) {
+ struct wb_server *server =
+ wl_container_of(listener, server, new_xdg_toplevel);
+ struct wlr_xdg_toplevel *xdg_toplevel = data;
+
+ /* Allocate a wb_toplevel for this toplevel */
+ struct wb_toplevel *toplevel =
+ calloc(1, sizeof(struct wb_toplevel));
+ toplevel->server = server;
+ toplevel->xdg_toplevel = xdg_toplevel;
+
+ toplevel->foreign_toplevel_handle = wlr_ext_foreign_toplevel_handle_v1_create(
+ server->foreign_toplevel_list, &toplevel->foreign_toplevel_state);
/* Listen to the various events it can emit */
- view->map.notify = xdg_toplevel_map;
- wl_signal_add(&xdg_surface->events.map, &view->map);
- view->unmap.notify = xdg_toplevel_unmap;
- wl_signal_add(&xdg_surface->events.unmap, &view->unmap);
- view->destroy.notify = xdg_toplevel_destroy;
- wl_signal_add(&xdg_surface->events.destroy, &view->destroy);
- view->new_popup.notify = handle_new_popup;
- wl_signal_add(&xdg_surface->events.new_popup, &view->new_popup);
+ toplevel->map.notify = xdg_toplevel_map;
+ wl_signal_add(&xdg_toplevel->base->surface->events.map, &toplevel->map);
+ toplevel->unmap.notify = xdg_toplevel_unmap;
+ wl_signal_add(&xdg_toplevel->base->surface->events.unmap, &toplevel->unmap);
+ toplevel->commit.notify = xdg_toplevel_commit;
+ wl_signal_add(&xdg_toplevel->base->surface->events.commit, &toplevel->commit);
+ toplevel->destroy.notify = xdg_toplevel_destroy;
+ wl_signal_add(&xdg_toplevel->events.destroy, &toplevel->destroy);
+ toplevel->new_popup.notify = handle_new_popup;
+ wl_signal_add(&xdg_toplevel->base->events.new_popup, &toplevel->new_popup);
- if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
- view->scene_tree = wlr_scene_xdg_surface_create(
- &view->server->scene->tree, view->xdg_toplevel->base);
- view->scene_tree->node.data = view;
- xdg_surface->data = view->scene_tree;
+ toplevel->scene_tree = wlr_scene_xdg_surface_create(
+ &toplevel->server->scene->tree, xdg_toplevel->base);
+ toplevel->scene_tree->node.data = toplevel;
+ xdg_toplevel->base->data = toplevel->scene_tree;
- struct wlr_xdg_toplevel *toplevel = view->xdg_toplevel;
- view->request_fullscreen.notify = xdg_toplevel_request_fullscreen;
- wl_signal_add(&toplevel->events.request_fullscreen, &view->request_fullscreen);
- view->request_maximize.notify = xdg_toplevel_request_maximize;
- wl_signal_add(&toplevel->events.request_maximize, &view->request_maximize);
- view->request_minimize.notify = xdg_toplevel_request_minimize;
- wl_signal_add(&toplevel->events.request_minimize, &view->request_minimize);
- view->request_move.notify = xdg_toplevel_request_move;
- wl_signal_add(&toplevel->events.request_move, &view->request_move);
- view->request_resize.notify = xdg_toplevel_request_resize;
- wl_signal_add(&toplevel->events.request_resize, &view->request_resize);
+ toplevel->request_fullscreen.notify = xdg_toplevel_request_fullscreen;
+ wl_signal_add(&xdg_toplevel->events.request_fullscreen, &toplevel->request_fullscreen);
+ toplevel->request_maximize.notify = xdg_toplevel_request_maximize;
+ wl_signal_add(&xdg_toplevel->events.request_maximize, &toplevel->request_maximize);
+ toplevel->request_minimize.notify = xdg_toplevel_request_minimize;
+ wl_signal_add(&xdg_toplevel->events.request_minimize, &toplevel->request_minimize);
+ toplevel->request_move.notify = xdg_toplevel_request_move;
+ wl_signal_add(&xdg_toplevel->events.request_move, &toplevel->request_move);
+ toplevel->request_resize.notify = xdg_toplevel_request_resize;
+ wl_signal_add(&xdg_toplevel->events.request_resize, &toplevel->request_resize);
+ toplevel->set_app_id.notify = xdg_toplevel_set_app_id;
+ wl_signal_add(&xdg_toplevel->events.set_app_id, &toplevel->set_app_id);
+ toplevel->set_title.notify = xdg_toplevel_set_title;
+ wl_signal_add(&xdg_toplevel->events.set_title, &toplevel->set_title);
- wl_list_insert(&view->server->views, &view->link);
- }
+ wl_list_insert(&toplevel->server->toplevels, &toplevel->link);
}
void init_xdg_shell(struct wb_server *server) {
/* xdg-shell version 3 */
server->xdg_shell = wlr_xdg_shell_create(server->wl_display, 3);
- server->new_xdg_surface.notify = handle_new_xdg_surface;
- wl_signal_add(&server->xdg_shell->events.new_surface, &server->new_xdg_surface);
+ server->new_xdg_popup.notify = handle_new_xdg_popup;
+ wl_signal_add(&server->xdg_shell->events.new_popup, &server->new_xdg_popup);
+ server->new_xdg_toplevel.notify = handle_new_xdg_toplevel;
+ wl_signal_add(&server->xdg_shell->events.new_toplevel, &server->new_xdg_toplevel);
}