├── .clang-format ├── .github └── workflows │ └── mypy.yml ├── .gitignore ├── .gitmodules ├── README.md ├── dev ├── DEV.org └── plot_DEBUGPERFORMANCE.py ├── flake.lock ├── flake.nix ├── include ├── py │ ├── _pywm_callbacks.h │ ├── _pywm_view.h │ └── _pywm_widget.h └── wm │ ├── shaders │ └── wm_shaders.h │ ├── wm.h │ ├── wm_composite.h │ ├── wm_config.h │ ├── wm_content.h │ ├── wm_cursor.h │ ├── wm_drag.h │ ├── wm_idle_inhibit.h │ ├── wm_keyboard.h │ ├── wm_layout.h │ ├── wm_output.h │ ├── wm_pointer.h │ ├── wm_renderer.h │ ├── wm_seat.h │ ├── wm_server.h │ ├── wm_util.h │ ├── wm_view.h │ ├── wm_view_layer.h │ ├── wm_view_xdg.h │ ├── wm_view_xwayland.h │ └── wm_widget.h ├── meson.build ├── meson_options.txt ├── mypy.ini ├── protocols ├── idle.xml ├── meson.build ├── wlr-input-inhibitor-unstable-v1.xml └── wlr-layer-shell-unstable-v1.xml ├── pywm ├── __init__.py ├── _pywm.pyi ├── damage_tracked.py ├── py.typed ├── pywm.py ├── pywm_background_widget.py ├── pywm_blur_widget.py ├── pywm_cairo_widget.py ├── pywm_view.py └── pywm_widget.py ├── requirements.txt ├── setup.py └── src ├── main.c ├── py ├── _pywm_callbacks.c ├── _pywm_view.c ├── _pywm_widget.c └── _pywmmodule.c └── wm ├── shaders ├── .gitignore ├── generate_primitive_shaders.py ├── generate_quad_shaders.py ├── generate_texture_shaders.py ├── meson.build ├── primitive │ ├── corner │ │ ├── fragment.glsl │ │ └── vertex.glsl │ ├── rect │ │ ├── fragment.glsl │ │ └── vertex.glsl │ ├── rounded_corners_border │ │ ├── fragment.glsl │ │ └── vertex.glsl │ └── rounded_corners_rect │ │ ├── fragment.glsl │ │ └── vertex.glsl ├── quad │ ├── fragment.glsl │ ├── fragment_downsample.glsl │ ├── fragment_upsample.glsl │ └── vertex.glsl └── texture │ ├── basic │ ├── fragment_ext.glsl │ ├── fragment_rgba.glsl │ ├── fragment_rgbx.glsl │ └── vertex.glsl │ ├── fancy │ ├── fragment_ext.glsl │ ├── fragment_rgba.glsl │ ├── fragment_rgbx.glsl │ └── vertex.glsl │ └── noeffect │ ├── fragment_ext.glsl │ ├── fragment_rgba.glsl │ ├── fragment_rgbx.glsl │ └── vertex.glsl ├── wm.c ├── wm_composite.c ├── wm_config.c ├── wm_content.c ├── wm_cursor.c ├── wm_drag.c ├── wm_idle_inhibit.c ├── wm_keyboard.c ├── wm_layout.c ├── wm_output.c ├── wm_pointer.c ├── wm_renderer.c ├── wm_seat.c ├── wm_server.c ├── wm_view.c ├── wm_view_layer.c ├── wm_view_xdg.c ├── wm_view_xwayland.c └── wm_widget.c /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 4 3 | -------------------------------------------------------------------------------- /.github/workflows/mypy.yml: -------------------------------------------------------------------------------- 1 | name: Mypy 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | name: Mypy 9 | steps: 10 | - uses: actions/checkout@v1 11 | - name: Set up Python 3.9 12 | uses: actions/setup-python@v1 13 | with: 14 | python-version: 3.9 15 | - name: Install Dependencies 16 | run: | 17 | pip install mypy 18 | pip install -r requirements.txt 19 | - name: mypy 20 | run: | 21 | mypy pywm 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .projectile 2 | .ccls-cache 3 | result 4 | build/* 5 | compile_commands.json 6 | **/__pycache__/* 7 | pywm/*.so 8 | pywm/touchpad/tmp.png 9 | subprojects 10 | .mypy_cache 11 | pywm.egg-info 12 | _pywm.so 13 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "subprojects/wlroots"] 2 | path = subprojects/wlroots 3 | url = https://gitlab.freedesktop.org/wlroots/wlroots 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pywm - backend for [newm](https://github.com/jbuchermn/newm) 2 | 3 | Unfortunately, newm as well as pywm are currently unmaintained. 4 | 5 | pywm is an abstraction layer for [newm](https://github.com/jbuchermn/newm) encapsulating all c code. 6 | 7 | Basically this is a very tiny compositor built on top of [wlroots](https://github.com/swaywm/wlroots), making all the assumptions that wlroots does not. On the Python side pywm exposes Wayland clients (XDG and XWayland) as so-called views and passes along all input. This way, handling the positioning of views, animating their movement, ... based on keystrokes or touchpad inputs (i.e. the logical, not performance-critical part of any compositor) is possible Python-side, whereas rendering and all other performance-critical aspects are handled by c code. 8 | 9 | Check the Python class `PyWM` and c struct `wm_server` for a start, as well as newms `Layout`. 10 | 11 | See also [pywm-fullscreen](https://github.com/jbuchermn/pywm-fullscreen) for a trivial implementation opening one application in fullscreen. 12 | 13 | ## Attention 14 | 15 | v0.3 with a better renderer implementation supporting blur has been merged into master. There are still some bugs around but I consider it an alpha stage. 16 | 17 | ## Installing 18 | 19 | If you install [newm](https://github.com/jbuchermn/newm) via the AUR, pywm is installed automatically. 20 | 21 | ### Prerequisites 22 | 23 | Prerequisites for PyWM, apart from Python, are given by [wlroots](https://github.com/swaywm/wlroots): 24 | 25 | * python and pip 26 | * gcc, meson and ninja 27 | * pkg-config 28 | * wayland 29 | * wayland-protocols 30 | * xorg-xwayland 31 | * EGL 32 | * GLESv2 33 | * libdrm 34 | * GBM 35 | * libinput 36 | * xkbcommon 37 | * udev 38 | * pixman 39 | * libseat 40 | 41 | ### Install 42 | 43 | Compilation is handled by meson and started automatically via pip (you need to install prerequisites first): 44 | 45 | ``` 46 | pip3 install git+https://github.com/jbuchermn/pywm 47 | ``` 48 | 49 | In case of issues, clone the repo and execute `meson build && ninja -C build` in order to debug. 50 | 51 | ### Configuration 52 | 53 | Configuration is handled via key-value pairs given to the `PyWM` contructor: 54 | 55 | | Key | Default | Description | 56 | |---------------------------------|------------|---------------------------------------------------------------------------------------------------------| 57 | | `enable_xwayland` | `False` | Boolean: Start `XWayland` | 58 | | `xkb_model` | | String: Keyboard model (`xkb`) | 59 | | `xkb_layout` | | String: Keyboard layout (`xkb`) | 60 | | `xkb_variant` | | String: Keyboard variant (`xkb`) | 61 | | `xkb_options` | | String: Keyboard options (`xkb`) | 62 | | `outputs` | | List of dicts: Output configuration (see next lines) | 63 | | `output.name` | `""` | String: Name of output to attach config to actual output | 64 | | `output.scale` | `1.0` | Number: HiDPI scale of output | 65 | | `output.width` | `0` | Integer: Output width (or zero to use preferred) | 66 | | `output.height` | `0` | Integer: Output height (or zero to use preferred) | 67 | | `output.mHz` | `0` | Integer: Output refresh rate in milli Hertz (or zero to use preferred) | 68 | | `output.pos_x` | `None` | Integer: Output position x in layout (or None to be placed automatically) | 69 | | `output.pos_y` | `None` | Integer: Output position y in layout (or None to be placed automatically) | 70 | | `xcursor_theme` | | String: `XCursor` theme (if not set, read from; if set, exported to `XCURSOR_THEME`) | 71 | | `xcursor_size` | `24` | Integer: `XCursor` size (if not set, read from; if set, exported to `XCURSOR_SIZE`) | 72 | | `tap_to_click` | `True` | Boolean: On tocuhpads use tap for click enter | 73 | | `natural_scroll` | `True` | Boolean: On touchpads use natural scrolling enter | 74 | | `focus_follows_mouse` | `True` | Boolean: `Focus` window upon mouse enter | 75 | | `contstrain_popups_to_toplevel` | `False` | Boolean: Try to keep popups contrained within their window | 76 | | `encourage_csd` | `True` | Boolean: Encourage clients to show client-side-decorations (see `wlr_server_decoration_manager`) | 77 | | `debug` | `False` | Boolean: Loglevel debug plus output debug information to stdout on every F1 press | 78 | | `texture_shaders` | `basic` | String: Shaders to use for texture rendering (see `src/wm/shaders/texture`) | 79 | | `renderer_mode` | `pywm` | String: Renderer mode, `pywm` (enable pywm renderer, and therefore blur), `wlr` (disable pywm renderer) | 80 | 81 | 82 | ### Troubleshooting 83 | 84 | #### seatd 85 | 86 | Be aware that current wlroots requires `seatd` under certain circumstances. Example systemd service (be sure to place your user in group `_seatd`): 87 | 88 | ``` 89 | [Unit] 90 | Description=Seat management daemon 91 | Documentation=man:seatd(1) 92 | 93 | [Service] 94 | Type=simple 95 | ExecStart=seatd -g _seatd 96 | Restart=always 97 | RestartSec=1 98 | 99 | [Install] 100 | WantedBy=multi-user.target 101 | ``` 102 | -------------------------------------------------------------------------------- /dev/DEV.org: -------------------------------------------------------------------------------- 1 | #+TITLE: pywm developer information 2 | 3 | * Development 4 | ** Current (v0.3) 5 | *** Bugs 6 | **** WAIT Horizontal damage artifacts after closing views 7 | **** WAIT Lag in fullscreen video on chrome 8 | 9 | *** Improvements 10 | **** TODO implement ext-session-lock-v1 11 | 12 | *** TODO clang-format 13 | 14 | ** Backlog / Ideas 15 | *** wlr-output-management-unstable-v1 (in order to use wdisplays) 16 | *** If performance-critical: Store wm_composite as texture, better damage handling 17 | *** If performance-critical: Further optimize (simplification algorithm, incorporate all wm_contents) wm_compose_tree 18 | *** If necessary: Secondary buffer for blurring should extend beyond primary buffer (however this is very complicated, intervenes with workspace logic, for little reward) 19 | *** Enable keyboard-exclusive client (e.g. layer shell keyboard_interactivity / use in lock screen) 20 | *** Complete libinput device config / support for external mouse 21 | *** Use libseat from python / patched python evdev 22 | *** Support newm-sidecar (or similar, via ws://) as touchpad 23 | *** Touch input from yoga or similar 24 | 25 | 26 | * Notes 27 | ** Screen record: wf-recorder 28 | ** Screen shot: grim -g "$(slurp)" 29 | ** Firefox: MOZ_ENABLE_WAYLAND=1 30 | ** Chromium: --enable-features=UseOzonePlatform --ozone-platform=wayland 31 | ** Matplotlib / Qt5 on Wayland requires DISPLAY=":0" to be set 32 | ** Apple Trackpad 33 | - See https://medium.com/macoclock/how-to-pair-apple-magic-keyboard-a1314-on-ubuntu-18-04-and-act-as-numpad-42fe4402454c 34 | - See https://wiki.archlinux.org/index.php/Bluetooth 35 | ** Screensharing using xdg-desktop-portal-wlr -r 36 | -------------------------------------------------------------------------------- /dev/plot_DEBUGPERFORMANCE.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt # type: ignore 2 | import sys 3 | import re 4 | 5 | 6 | print("Loading data...") 7 | pattern = re.compile(r".*DEBUGPERFORMANCE\[(.*)\(([0-9]*)\)\]: ([0-9]*.[0-9]*)") 8 | 9 | output = int(sys.argv[2]) 10 | 11 | data: list[tuple[str, float, float, float, int]] = [] 12 | with open(sys.argv[1], 'r') as inp: 13 | last_render = -1. 14 | idx_render = 0 15 | for l in inp: 16 | res = pattern.match(l) 17 | if res is not None: 18 | kind, o, ts = str(res.group(1)), int(res.group(2)), float(res.group(3)) 19 | if o != 0 and o != output and kind != "py_start" and kind != "py_finish": 20 | continue 21 | if last_render > 0: 22 | data += [(kind, idx_render, ts - last_render, ts, idx_render)] 23 | 24 | if kind == "render": 25 | last_render = ts 26 | idx_render += 1 27 | data += [("render2", idx_render, 0, ts, idx_render)] 28 | 29 | 30 | zero_ts = data[0][3] 31 | cur_idx = -1. 32 | cur_ts = 0. 33 | for i in range(len(data)): 34 | if data[i][1] != cur_idx: 35 | cur_idx, cur_ts = data[i][1], data[i][3] - zero_ts 36 | data[i] = data[i][0], cur_ts, data[i][2], data[i][3] - zero_ts, data[i][4] 37 | 38 | 39 | 40 | if sys.argv[3] == "plot": 41 | visual = { 42 | 'render': 'g.-', 43 | 'render2': 'g.-', 44 | 'damage': 'b.', 45 | 'schedule_frame': 'b+', 46 | 'skip_frame': 'gx', 47 | 'present_frame': 'ro', 48 | 'py_start': 'r+', 49 | 'py_finish': 'r.', 50 | 'callback_start': 'y+', 51 | 'callback_finish': 'y.', 52 | } 53 | 54 | print("Plotting...") 55 | plt.figure() 56 | 57 | for d in data: 58 | if d[0] == 'enter_constant_damage': 59 | plt.axvline(d[1], color='gray') 60 | elif d[0] == 'exit_constant_damage': 61 | plt.axvline(d[1], color='gray', linestyle='--') 62 | 63 | plt.plot([t[1] for t in data], [t[4] for t in data], 'k.') 64 | for kind in visual: 65 | plt.plot([t[1] for t in data if t[0] == kind], [1000.*t[2] for t in data if t[0] == kind], visual[kind], alpha=.5) 66 | 67 | plt.show() 68 | elif sys.argv[3] == "show": 69 | idx = int(sys.argv[4]) 70 | for d in data: 71 | if d[4] == idx: 72 | print(d) 73 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "locked": { 5 | "lastModified": 1659877975, 6 | "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", 7 | "owner": "numtide", 8 | "repo": "flake-utils", 9 | "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "numtide", 14 | "repo": "flake-utils", 15 | "type": "github" 16 | } 17 | }, 18 | "nixpkgs": { 19 | "locked": { 20 | "lastModified": 1665643254, 21 | "narHash": "sha256-IBVWNJxGCsshwh62eRfR6+ry3bSXmulB3VQRzLQo3hk=", 22 | "owner": "NixOS", 23 | "repo": "nixpkgs", 24 | "rev": "ba187fbdc5e35322c7dff556ef2c47bddfd6e8d7", 25 | "type": "github" 26 | }, 27 | "original": { 28 | "owner": "nixos", 29 | "ref": "nixos-unstable", 30 | "repo": "nixpkgs", 31 | "type": "github" 32 | } 33 | }, 34 | "root": { 35 | "inputs": { 36 | "flake-utils": "flake-utils", 37 | "nixpkgs": "nixpkgs" 38 | } 39 | } 40 | }, 41 | "root": "root", 42 | "version": 7 43 | } 44 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "pywm - Wayland compositor core"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; 6 | flake-utils.url = "github:numtide/flake-utils"; 7 | }; 8 | 9 | outputs = { self, nixpkgs, flake-utils }: 10 | flake-utils.lib.eachDefaultSystem ( 11 | system: 12 | let 13 | pkgs = import nixpkgs { 14 | inherit system; 15 | }; 16 | has_xwayland = true; 17 | inherit (pkgs.lib) optionals; 18 | in 19 | { 20 | packages.pywm = ( 21 | pkgs.python3.pkgs.buildPythonPackage rec { 22 | pname = "pywm"; 23 | version = "0.3alpha"; 24 | 25 | # BEGIN Fucking subprojects bug workaround for 'src = ./.' 26 | srcs = [ 27 | ./. 28 | (builtins.fetchGit { 29 | url = "https://gitlab.freedesktop.org/wlroots/wlroots"; 30 | rev = "e279266f714c7122e9ad97d56d047313f78cfdbe"; 31 | submodules = true; 32 | }) 33 | ]; 34 | 35 | setSourceRoot = '' 36 | for i in ./*; do 37 | if [ -f "$i/wlroots.syms" ]; then 38 | wlrootsDir=$i 39 | fi 40 | if [ -f "$i/pywm/pywm.py" ]; then 41 | sourceRoot=$i 42 | fi 43 | done 44 | ''; 45 | 46 | preConfigure = '' 47 | echo "--- Pre-configure --------------" 48 | echo " wlrootsDir=$wlrootsDir" 49 | echo " sourceRoot=$sourceRoot" 50 | echo "--- ls -------------------------" 51 | ls -al 52 | echo "--- ls ../wlrootsDir -----------" 53 | ls -al ../$wlrootsDir 54 | 55 | rm -rf ./subprojects/wlroots 2> /dev/null 56 | cp -r ../$wlrootsDir ./subprojects/wlroots 57 | rm -rf ./build 58 | 59 | echo "--- ls ./subprojects/wlroots ---" 60 | ls -al ./subprojects/wlroots/ 61 | echo "--------------------------------" 62 | ''; 63 | # END Fucking subprojects bug workaround 64 | 65 | mesonFlags = if has_xwayland then [ "-Dxwayland=enabled" ] else []; 66 | 67 | nativeBuildInputs = with pkgs; [ 68 | meson 69 | ninja 70 | pkg-config 71 | wayland-scanner 72 | glslang 73 | ]; 74 | 75 | preBuild = "cd .."; 76 | 77 | buildInputs = with pkgs; [ 78 | libGL 79 | wayland 80 | wayland-protocols 81 | libinput 82 | libxkbcommon 83 | pixman 84 | vulkan-loader 85 | mesa 86 | seatd 87 | 88 | libpng 89 | ffmpeg 90 | libcap 91 | 92 | xorg.xcbutilwm 93 | xorg.xcbutilrenderutil 94 | xorg.xcbutilerrors 95 | xorg.xcbutilimage 96 | xorg.libX11 97 | ] ++ optionals has_xwayland [ 98 | xwayland 99 | ]; 100 | 101 | propagatedBuildInputs = with pkgs.python3Packages; [ 102 | imageio 103 | numpy 104 | pycairo 105 | evdev 106 | ]; 107 | } 108 | ); 109 | 110 | devShell = let 111 | my-python = pkgs.python3; 112 | python-with-my-packages = my-python.withPackages (ps: with ps; [ 113 | imageio 114 | numpy 115 | pycairo 116 | evdev 117 | matplotlib 118 | 119 | python-lsp-server 120 | (pylsp-mypy.overrideAttrs (old: { pytestCheckPhase = "true"; })) 121 | mypy 122 | ]); 123 | in 124 | pkgs.mkShell { 125 | nativeBuildInputs = with pkgs; [ 126 | meson 127 | ninja 128 | pkg-config 129 | wayland-scanner 130 | glslang 131 | ]; 132 | 133 | buildInputs = with pkgs; [ 134 | libGL 135 | wayland 136 | wayland-protocols 137 | libinput 138 | libxkbcommon 139 | pixman 140 | seatd 141 | vulkan-loader 142 | mesa 143 | 144 | libpng 145 | ffmpeg 146 | libcap 147 | python-with-my-packages 148 | 149 | xorg.xcbutilwm 150 | xorg.xcbutilrenderutil 151 | xorg.xcbutilerrors 152 | xorg.xcbutilimage 153 | xorg.libX11 154 | xwayland 155 | ]; 156 | 157 | }; 158 | } 159 | ); 160 | } 161 | -------------------------------------------------------------------------------- /include/py/_pywm_callbacks.h: -------------------------------------------------------------------------------- 1 | #ifndef _PYWM_CALLBACKS_H 2 | #define _PYWM_CALLBACKS_H 3 | 4 | #include 5 | 6 | struct _pywm_callbacks { 7 | PyObject* ready; 8 | PyObject* layout_change; 9 | PyObject* motion; 10 | PyObject* button; 11 | PyObject* axis; 12 | PyObject* key; 13 | PyObject* modifiers; 14 | PyObject* gesture; 15 | 16 | PyObject* update_view; 17 | PyObject* destroy_view; 18 | PyObject* view_event; 19 | 20 | PyObject* query_new_widget; 21 | PyObject* update_widget; 22 | PyObject* query_destroy_widget; 23 | 24 | PyObject* update; 25 | }; 26 | 27 | void _pywm_callbacks_init(); 28 | PyObject** _pywm_callbacks_get(const char* name); 29 | 30 | struct _pywm_callbacks* _pywm_callbacks_get_all(); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /include/py/_pywm_view.h: -------------------------------------------------------------------------------- 1 | #ifndef _PYWM_VIEW_H 2 | #define _PYWM_VIEW_H 3 | 4 | struct wm_view; 5 | 6 | struct _pywm_view { 7 | long handle; 8 | struct wm_view* view; 9 | 10 | int update_cnt; 11 | 12 | struct _pywm_view* next_view; 13 | }; 14 | 15 | void _pywm_view_init(struct _pywm_view* _view, struct wm_view* view); 16 | 17 | void _pywm_view_update(struct _pywm_view* view); 18 | 19 | struct _pywm_views { 20 | struct _pywm_view* first_view; 21 | }; 22 | 23 | void _pywm_views_init(); 24 | long _pywm_views_add(struct wm_view* view); 25 | long _pywm_views_get_handle(struct wm_view* view); 26 | long _pywm_views_remove(struct wm_view* view); 27 | void _pywm_views_update(); 28 | void _pywm_views_update_single(struct wm_view* view); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /include/py/_pywm_widget.h: -------------------------------------------------------------------------------- 1 | #ifndef _PYWM_WIDGET_H 2 | #define _PYWM_WIDGET_H 3 | 4 | struct wm_widget; 5 | struct wm_composite; 6 | struct wm_content; 7 | 8 | struct _pywm_widget { 9 | long handle; 10 | struct _pywm_widget* next_widget; 11 | 12 | struct wm_widget* widget; 13 | struct wm_composite* composite; 14 | struct wm_content* super; 15 | }; 16 | 17 | void _pywm_widget_init(struct _pywm_widget* _widget, struct wm_widget* widget, struct wm_composite* composite); 18 | 19 | void _pywm_widget_update(struct _pywm_widget* widget); 20 | 21 | struct _pywm_widgets { 22 | struct _pywm_widget* first_widget; 23 | }; 24 | 25 | void _pywm_widgets_init(); 26 | long _pywm_widgets_add(struct wm_widget* widget, struct wm_composite* composite); 27 | long _pywm_widgets_get_handle(struct wm_content* content); 28 | long _pywm_widgets_remove(struct wm_content* content); 29 | void _pywm_widgets_update(); 30 | 31 | struct _pywm_widget* _pywm_widgets_container_from_handle(long handle); 32 | struct wm_content* _pywm_widgets_from_handle(long handle); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /include/wm/shaders/wm_shaders.h: -------------------------------------------------------------------------------- 1 | #ifndef WM_SHADERS_H 2 | #define WM_SAHDERS_H 3 | 4 | void wm_texture_shaders_init(struct wm_renderer* renderer); 5 | void wm_primitive_shaders_init(struct wm_renderer* renderer); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /include/wm/wm.h: -------------------------------------------------------------------------------- 1 | #ifndef WM_H 2 | #define WM_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | struct wm_view; 11 | struct wm_server; 12 | struct wm_layout; 13 | struct wm_widget; 14 | 15 | #define WM_CURSOR_MIN -1000000 16 | 17 | struct wm { 18 | struct wm_server* server; 19 | 20 | void (*callback_layout_change)(struct wm_layout*); 21 | bool (*callback_key)(struct wlr_event_keyboard_key*, const char* keysyms); 22 | bool (*callback_modifiers)(struct wlr_keyboard_modifiers*); 23 | bool (*callback_motion)(double, double, double, double, uint32_t); 24 | bool (*callback_button)(struct wlr_event_pointer_button*); 25 | bool (*callback_axis)(struct wlr_event_pointer_axis*); 26 | 27 | bool (*callback_gesture_swipe_begin)(struct wlr_event_pointer_swipe_begin* event); 28 | bool (*callback_gesture_swipe_update)(struct wlr_event_pointer_swipe_update* event); 29 | bool (*callback_gesture_swipe_end)(struct wlr_event_pointer_swipe_end* event); 30 | bool (*callback_gesture_pinch_begin)(struct wlr_event_pointer_pinch_begin* event); 31 | bool (*callback_gesture_pinch_update)(struct wlr_event_pointer_pinch_update* event); 32 | bool (*callback_gesture_pinch_end)(struct wlr_event_pointer_pinch_end* event); 33 | bool (*callback_gesture_hold_begin)(struct wlr_event_pointer_hold_begin* event); 34 | bool (*callback_gesture_hold_end)(struct wlr_event_pointer_hold_end* event); 35 | 36 | void (*callback_init_view)(struct wm_view*); 37 | void (*callback_destroy_view)(struct wm_view*); 38 | void (*callback_view_event)(struct wm_view*, const char* event); 39 | 40 | /* Once the server is ready, and we can create new threads */ 41 | void (*callback_ready)(void); 42 | 43 | void (*callback_update_view)(struct wm_view*); 44 | 45 | /* Synchronous update once per frame */ 46 | void (*callback_update)(void); 47 | }; 48 | 49 | void wm_init(); 50 | void wm_destroy(); 51 | int wm_run(); 52 | void wm_terminate(); 53 | 54 | void wm_focus_view(struct wm_view* view); 55 | void wm_update_cursor(int cursor_visible, int pos_x, int pos_y); 56 | 57 | void wm_set_locked(double locked); 58 | 59 | void wm_open_virtual_output(const char* name); 60 | void wm_close_virtual_output(const char* name); 61 | 62 | /* 63 | * Instead of writing setters for every single callback, 64 | * just put them in this object 65 | */ 66 | struct wm* get_wm(); 67 | 68 | void wm_callback_layout_change(struct wm_layout* layout); 69 | 70 | /* 71 | * Return false if event should be dispatched to clients 72 | */ 73 | bool wm_callback_key(struct wlr_event_keyboard_key* event, const char* keysyms); 74 | bool wm_callback_modifiers(struct wlr_keyboard_modifiers* modifiers); 75 | bool wm_callback_motion(double delta_x, double delta_y, double abs_x, double abs_y, uint32_t time_msec); 76 | bool wm_callback_button(struct wlr_event_pointer_button* event); 77 | bool wm_callback_axis(struct wlr_event_pointer_axis* event); 78 | 79 | bool wm_callback_gesture_swipe_begin(struct wlr_event_pointer_swipe_begin* event); 80 | bool wm_callback_gesture_swipe_update(struct wlr_event_pointer_swipe_update* event); 81 | bool wm_callback_gesture_swipe_end(struct wlr_event_pointer_swipe_end* event); 82 | bool wm_callback_gesture_pinch_begin(struct wlr_event_pointer_pinch_begin* event); 83 | bool wm_callback_gesture_pinch_update(struct wlr_event_pointer_pinch_update* event); 84 | bool wm_callback_gesture_pinch_end(struct wlr_event_pointer_pinch_end* event); 85 | bool wm_callback_gesture_hold_begin(struct wlr_event_pointer_hold_begin* event); 86 | bool wm_callback_gesture_hold_end(struct wlr_event_pointer_hold_end* event); 87 | 88 | /* 89 | * Should set display_x, display_y, display_height, display_width 90 | * Can also call set_size 91 | */ 92 | void wm_callback_init_view(struct wm_view* view); 93 | void wm_callback_destroy_view(struct wm_view* view); 94 | void wm_callback_view_event(struct wm_view* view, const char* event); 95 | 96 | void wm_callback_update_view(struct wm_view* view); 97 | void wm_callback_update(); 98 | void wm_callback_ready(); 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /include/wm/wm_composite.h: -------------------------------------------------------------------------------- 1 | #ifndef WM_COMPOSITE_H 2 | #define WM_COMPOSITE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "wm_content.h" 9 | 10 | struct wm_server; 11 | 12 | enum wm_composite_type { 13 | WM_COMPOSITE_BLUR 14 | }; 15 | 16 | struct wm_composite { 17 | struct wm_content super; 18 | enum wm_composite_type type; 19 | 20 | struct { 21 | int n_params_int; 22 | int* params_int; 23 | int n_params_float; 24 | float* params_float; 25 | } params; 26 | 27 | }; 28 | 29 | void wm_composite_init(struct wm_composite* comp, struct wm_server* server); 30 | void wm_composite_set_type(struct wm_composite* comp, const char* type, int n_params_int, int* params_int, int n_params_float, float* params_float); 31 | 32 | void wm_composite_on_damage_below(struct wm_composite* comp, struct wm_output* output, struct wm_content* from, pixman_region32_t* damage); 33 | bool wm_content_is_composite(struct wm_content* content); 34 | void wm_composite_apply(struct wm_composite* composite, struct wm_output* output, pixman_region32_t* damage, struct timespec now); 35 | 36 | struct wm_compose_chain { 37 | struct wm_compose_chain* lower; 38 | struct wm_compose_chain* higher; 39 | struct wm_composite* composite; 40 | double z_index; 41 | pixman_region32_t damage; 42 | pixman_region32_t composite_output; 43 | }; 44 | 45 | struct wm_compose_chain* wm_compose_chain_from_damage(struct wm_server* server, struct wm_output* output, pixman_region32_t* damage); 46 | void wm_compose_chain_free(struct wm_compose_chain* chain); 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /include/wm/wm_config.h: -------------------------------------------------------------------------------- 1 | #ifndef WM_CONFIG_H 2 | #define WM_CONFIG_H 3 | 4 | #include 5 | #include 6 | 7 | #include "wm/wm_renderer.h" 8 | 9 | #define WM_CONFIG_POS_MIN -1000000 10 | #define WM_CONFIG_STRLEN 100 11 | 12 | struct wm_server; 13 | 14 | struct wm_config_output { 15 | struct wl_list link; // wm_config::outputs 16 | 17 | char name[WM_CONFIG_STRLEN]; 18 | double scale; 19 | 20 | /* Unit: pixels */ 21 | int width; 22 | int height; 23 | 24 | /* Refresh rate in mHz */ 25 | int mHz; 26 | 27 | /* Unit: Logical pixels, > WM_CONFIG_POS_MIN */ 28 | int pos_x; 29 | int pos_y; 30 | 31 | enum wl_output_transform transform; 32 | }; 33 | 34 | struct wm_config { 35 | /* Excluded from runtime update */ 36 | bool enable_xwayland; 37 | int callback_frequency; 38 | 39 | char xkb_model[WM_CONFIG_STRLEN]; 40 | char xkb_layout[WM_CONFIG_STRLEN]; 41 | char xkb_variant[WM_CONFIG_STRLEN]; 42 | char xkb_options[WM_CONFIG_STRLEN]; 43 | 44 | char texture_shaders[WM_CONFIG_STRLEN]; 45 | char renderer_mode[WM_CONFIG_STRLEN]; 46 | 47 | struct wl_list outputs; 48 | 49 | const char *xcursor_theme; 50 | int xcursor_size; 51 | 52 | int focus_follows_mouse; 53 | 54 | int constrain_popups_to_toplevel; 55 | 56 | int encourage_csd; 57 | 58 | bool tap_to_click; 59 | bool natural_scroll; 60 | 61 | bool debug; 62 | }; 63 | 64 | void wm_config_init_default(struct wm_config *config); 65 | void wm_config_reset_default(struct wm_config* config); 66 | void wm_config_reconfigure(struct wm_config* config, struct wm_server* server); 67 | void wm_config_set_xcursor_theme(struct wm_config* config, const char* xcursor_theme); 68 | void wm_config_set_xcursor_size(struct wm_config* config, int xcursor_size); 69 | void wm_config_add_output(struct wm_config *config, const char *name, 70 | double scale, int width, int height, int mHz, 71 | int pos_x, int pos_y, enum wl_output_transform transform); 72 | struct wm_config_output *wm_config_find_output(struct wm_config *config, 73 | const char *name); 74 | void wm_config_destroy(struct wm_config *config); 75 | 76 | enum wm_renderer_mode wm_config_get_renderer_mode(struct wm_config* config); 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /include/wm/wm_content.h: -------------------------------------------------------------------------------- 1 | #ifndef WM_CONTENT_H 2 | #define WM_CONTENT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | struct wm_output; 11 | 12 | struct wm_content_vtable; 13 | 14 | struct wm_content { 15 | struct wl_list link; // wm_server::wm_views 16 | struct wm_server* wm_server; 17 | 18 | struct wm_content_vtable* vtable; 19 | 20 | double display_x; 21 | double display_y; 22 | double display_width; 23 | double display_height; 24 | 25 | /* Fix to one output - only for comparison; don't dereference */ 26 | struct wm_output* fixed_output; 27 | 28 | /* Only consider the parts of content inside this box valid (disabled if width or height < 0) */ 29 | double workspace_x; 30 | double workspace_y; 31 | double workspace_width; 32 | double workspace_height; 33 | 34 | /* Only applied to root surface(s) */ 35 | double mask_x; 36 | double mask_y; 37 | double mask_w; 38 | double mask_h; 39 | double corner_radius; 40 | 41 | double z_index; 42 | double opacity; 43 | 44 | /* Accepts input and is displayed clearly during lock - careful */ 45 | bool lock_enabled; 46 | }; 47 | 48 | void wm_content_init(struct wm_content* content, struct wm_server* server); 49 | void wm_content_base_destroy(struct wm_content* content); 50 | 51 | void wm_content_set_output(struct wm_content* content, int key, struct wlr_output* output); 52 | struct wm_output* wm_content_get_output(struct wm_content* content); 53 | void wm_content_set_workspace(struct wm_content* content, double x, double y, double width, double height); 54 | void wm_content_get_workspace(struct wm_content* content, double* workspace_x, double* workspace_y, double* workspace_width, double* workspace_height); 55 | bool wm_content_has_workspace(struct wm_content* content); 56 | bool wm_content_is_on_output(struct wm_content* content, struct wm_output* output); 57 | 58 | void wm_content_set_box(struct wm_content* content, double x, double y, double width, double height); 59 | void wm_content_get_box(struct wm_content* content, double* display_x, double* display_y, double* display_width, double* display_height); 60 | 61 | void wm_content_set_mask(struct wm_content* content, double mask_x, double mask_y, double mask_w, double mask_h); 62 | void wm_content_get_mask(struct wm_content* content, double* mask_x, double* mask_y, double* mask_w, double* mask_h); 63 | void wm_content_set_corner_radius(struct wm_content* content, double corner_radius); 64 | double wm_content_get_corner_radius(struct wm_content* content); 65 | 66 | void wm_content_set_z_index(struct wm_content* content, double z_index); 67 | double wm_content_get_z_index(struct wm_content* content); 68 | 69 | void wm_content_set_opacity(struct wm_content* content, double opacity); 70 | double wm_content_get_opacity(struct wm_content* content); 71 | 72 | void wm_content_set_lock_enabled(struct wm_content* content, bool lock_enabled); 73 | 74 | struct wm_content_vtable { 75 | void (*destroy)(struct wm_content* content); 76 | void (*render)(struct wm_content* content, struct wm_output* output, pixman_region32_t* output_damage, struct timespec now); 77 | 78 | /* origin == NULL means damage whole */ 79 | void (*damage_output)(struct wm_content* content, struct wm_output* output, struct wlr_surface* origin); 80 | 81 | void (*printf)(FILE* file, struct wm_content* content); 82 | }; 83 | 84 | void wm_content_destroy(struct wm_content* content); 85 | 86 | void wm_content_render(struct wm_content* content, struct wm_output* output, pixman_region32_t* output_damage, struct timespec now); 87 | 88 | void wm_content_damage_output_base(struct wm_content* content, struct wm_output* output, struct wlr_surface* origin); 89 | 90 | static inline void wm_content_damage_output(struct wm_content* content, struct wm_output* output, struct wlr_surface* origin){ 91 | if(content->vtable->damage_output){ 92 | (*content->vtable->damage_output)(content, output, origin); 93 | }else{ 94 | wm_content_damage_output_base(content, output, origin); 95 | } 96 | } 97 | 98 | static inline void wm_content_printf(FILE* file, struct wm_content* content){ 99 | (*content->vtable->printf)(file, content); 100 | } 101 | 102 | #endif 103 | -------------------------------------------------------------------------------- /include/wm/wm_cursor.h: -------------------------------------------------------------------------------- 1 | #ifndef WM_CURSOR_H 2 | #define WM_CURSOR_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct wm_cursor; 10 | struct wm_seat; 11 | struct wm_layout; 12 | struct wm_output; 13 | struct wm_pointer; 14 | 15 | struct wm_cursor { 16 | struct wm_seat* wm_seat; 17 | 18 | struct wlr_cursor* wlr_cursor; 19 | struct wlr_xcursor_manager* wlr_xcursor_manager; 20 | 21 | struct wl_listener motion; 22 | struct wl_listener motion_absolute; 23 | struct wl_listener button; 24 | struct wl_listener axis; 25 | struct wl_listener frame; 26 | struct wl_listener surface_destroy; 27 | 28 | struct wlr_pointer_gestures_v1 *pointer_gestures; 29 | struct wl_listener pinch_begin; 30 | struct wl_listener pinch_update; 31 | struct wl_listener pinch_end; 32 | struct wl_listener swipe_begin; 33 | struct wl_listener swipe_update; 34 | struct wl_listener swipe_end; 35 | 36 | bool swipe_started; 37 | bool pinch_started; 38 | 39 | 40 | uint32_t msec_delta; 41 | 42 | /* Set from python - final say about whether a cursor is displayed */ 43 | int cursor_visible; 44 | 45 | struct { 46 | struct wlr_surface* surface; 47 | int32_t hotspot_x; 48 | int32_t hotspot_y; 49 | } client_image; 50 | }; 51 | 52 | void wm_cursor_init(struct wm_cursor* cursor, struct wm_seat* seat, struct wm_layout* layout); 53 | void wm_cursor_ensure_loaded_for_scale(struct wm_cursor* cursor, double scale); 54 | 55 | void wm_cursor_set_visible(struct wm_cursor* cursor, int visible); 56 | void wm_cursor_set_image(struct wm_cursor* cursor, const char* image); 57 | void wm_cursor_set_image_surface(struct wm_cursor* cursor, struct wlr_surface* surface, int32_t hotspot_x, int32_t hotspot_y); 58 | void wm_cursor_set_position(struct wm_cursor* cursor, int pos_x, int pos_y); 59 | 60 | void wm_cursor_destroy(struct wm_cursor* cursor); 61 | void wm_cursor_add_pointer(struct wm_cursor* cursor, struct wm_pointer* pointer); 62 | void wm_cursor_update(struct wm_cursor* cursor); 63 | 64 | void wm_cursor_reconfigure(struct wm_cursor* cursor); 65 | 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /include/wm/wm_drag.h: -------------------------------------------------------------------------------- 1 | #ifndef WM_DRAG_H 2 | #define WM_DRAG_H 3 | 4 | #include 5 | 6 | #include "wm_content.h" 7 | 8 | #define WM_DRAG_Z_INDEX 50. 9 | 10 | struct wm_drag { 11 | struct wm_content super; 12 | 13 | struct wm_seat* wm_seat; 14 | struct wlr_drag* wlr_drag; 15 | 16 | struct wl_listener destroy; 17 | 18 | struct wlr_drag_icon* wlr_drag_icon; 19 | struct wl_listener icon_surface_commit; 20 | struct wl_listener icon_map; 21 | struct wl_listener icon_unmap; 22 | struct wl_listener icon_destroy; 23 | }; 24 | 25 | void wm_drag_init(struct wm_drag* drag, struct wm_seat* seat, struct wlr_drag* wlr_drag); 26 | void wm_drag_update_position(struct wm_drag* drag); 27 | bool wm_content_is_drag(struct wm_content* content); 28 | 29 | 30 | #endif // WM_DRAG_H 31 | -------------------------------------------------------------------------------- /include/wm/wm_idle_inhibit.h: -------------------------------------------------------------------------------- 1 | #ifndef WM_IDLE_INHIBIT_H 2 | #define WM_IDLE_INHIBIT_H 3 | 4 | #include 5 | 6 | struct wm_server; 7 | struct wm_idle_inhibit; 8 | 9 | struct wm_idle_inhibitor { 10 | struct wlr_idle_inhibitor_v1* wlr_inhibitor; 11 | struct wm_idle_inhibit* parent; 12 | struct wm_view* view; 13 | 14 | struct wl_listener destroy; 15 | }; 16 | 17 | void wm_idle_inhibitor_init(struct wm_idle_inhibitor* inhibitor, struct wm_idle_inhibit* parent, struct wlr_idle_inhibitor_v1* wlr_inhibitor); 18 | void wm_idle_inhibitor_destroy(struct wm_idle_inhibitor* inhibitor); 19 | 20 | struct wm_idle_inhibit { 21 | struct wm_server* wm_server; 22 | 23 | struct wlr_idle_inhibit_manager_v1* wlr_idle_inhibit_manager; 24 | struct wl_listener new_idle_inhibitor; 25 | }; 26 | 27 | void wm_idle_inhibit_init(struct wm_idle_inhibit* inhibit, struct wm_server* server); 28 | void wm_idle_inhibit_destroy(struct wm_idle_inhibit* inhibit); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /include/wm/wm_keyboard.h: -------------------------------------------------------------------------------- 1 | #ifndef WM_KEYBOARD_H 2 | #define WM_KEYBOARD_H 3 | 4 | #include 5 | #include 6 | 7 | struct wm_seat; 8 | 9 | struct wm_keyboard { 10 | struct wl_list link; // wm_seat::wm_keyboards 11 | struct wm_seat* wm_seat; 12 | 13 | struct wlr_input_device* wlr_input_device; 14 | 15 | struct wl_listener destroy; 16 | struct wl_listener key; 17 | struct wl_listener modifiers; 18 | }; 19 | 20 | void wm_keyboard_init(struct wm_keyboard* keyboard, struct wm_seat* seat, struct wlr_input_device* input_device); 21 | void wm_keyboard_destroy(struct wm_keyboard* keyboard); 22 | void wm_keyboard_reconfigure(struct wm_keyboard* keyboard); 23 | 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /include/wm/wm_layout.h: -------------------------------------------------------------------------------- 1 | #ifndef WM_LAYOUT_H 2 | #define WM_LAYOUT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct wm_server; 10 | struct wm_view; 11 | struct wm_content; 12 | struct wm_output; 13 | 14 | struct wm_layout { 15 | struct wm_server* wm_server; 16 | 17 | struct wlr_output_layout* wlr_output_layout; 18 | struct wl_list wm_outputs; // wm_output::link 19 | 20 | struct wl_listener change; 21 | 22 | int refresh_master_output; 23 | int refresh_scheduled; 24 | }; 25 | 26 | void wm_layout_init(struct wm_layout* layout, struct wm_server* server); 27 | void wm_layout_destroy(struct wm_layout* layout); 28 | 29 | void wm_layout_add_output(struct wm_layout* layout, struct wlr_output* output); 30 | void wm_layout_remove_output(struct wm_layout* layout, struct wm_output* output); 31 | 32 | void wm_layout_reconfigure(struct wm_layout* layout); 33 | 34 | /* Damage whole output layout */ 35 | void wm_layout_damage_whole(struct wm_layout* layout); 36 | 37 | /* Calls wm_content_damage_output, expects calls to wm_layout_damage_output */ 38 | void wm_layout_damage_from(struct wm_layout* layout, struct wm_content* content, struct wlr_surface* origin); 39 | void wm_layout_damage_output(struct wm_layout* layout, struct wm_output* output, pixman_region32_t* damage, struct wm_content* from); 40 | 41 | void wm_layout_start_update(struct wm_layout* layout); 42 | int wm_layout_get_refresh_output(struct wm_layout* layout); 43 | 44 | void wm_layout_update_content_outputs(struct wm_layout* layout, struct wm_content* content); 45 | 46 | void wm_layout_printf(FILE* file, struct wm_layout* layout); 47 | 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /include/wm/wm_output.h: -------------------------------------------------------------------------------- 1 | #ifndef WM_OUTPUT_H 2 | #define WM_OUTPUT_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | struct wm_layout; 9 | struct wm_renderer_buffers; 10 | 11 | struct wm_output { 12 | struct wm_server* wm_server; 13 | struct wm_layout* wm_layout; 14 | struct wl_list link; // wm_layout::wm_outputs 15 | 16 | int layout_x; // Duplicated from wlr_output_layout 17 | int layout_y; // Duplicated from wlr_output_layout 18 | 19 | int key; // Unique key in a layout - update on layout change 20 | 21 | struct wlr_output* wlr_output; 22 | struct wlr_output_damage* wlr_output_damage; 23 | 24 | struct wl_listener destroy; 25 | struct wl_listener commit; 26 | struct wl_listener mode; 27 | struct wl_listener present; 28 | struct wl_listener damage_frame; 29 | struct wl_listener damage_destroy; 30 | 31 | bool expecting_frame; 32 | struct timespec last_frame; 33 | 34 | #if WM_CUSTOM_RENDERER 35 | struct wm_renderer_buffers* renderer_buffers; 36 | #endif 37 | }; 38 | 39 | void wm_output_init(struct wm_output* output, struct wm_server* server, struct wm_layout* layout, struct wlr_output* out); 40 | void wm_output_destroy(struct wm_output* output); 41 | 42 | void wm_output_reconfigure(struct wm_output* output); 43 | 44 | 45 | /* 46 | * Override name of next output to be initialised 47 | * Necessary, as wlroots provides no way of naming 48 | * headless outputs 49 | */ 50 | void wm_output_override_name(const char* name); 51 | 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /include/wm/wm_pointer.h: -------------------------------------------------------------------------------- 1 | #ifndef WM_POINTER_H 2 | #define WM_POINTER_H 3 | 4 | #include 5 | #include 6 | 7 | struct wm_seat; 8 | 9 | struct wm_pointer { 10 | struct wl_list link; // wm_seat::wm_pointers 11 | struct wm_seat* wm_seat; 12 | 13 | struct wlr_input_device* wlr_input_device; 14 | 15 | struct wl_listener destroy; 16 | }; 17 | 18 | void wm_pointer_init(struct wm_pointer* pointer, struct wm_seat* seat, struct wlr_input_device* input_device); 19 | void wm_pointer_destroy(struct wm_pointer* pointer); 20 | void wm_pointer_reconfigure(struct wm_pointer* pointer); 21 | 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /include/wm/wm_renderer.h: -------------------------------------------------------------------------------- 1 | #ifndef WM_RENDERER_H 2 | #define WM_RENDERER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | #ifdef WM_CUSTOM_RENDERER 11 | 12 | struct wm_output; 13 | struct wm_renderer; 14 | 15 | #include 16 | struct wm_renderer_texture_shader { 17 | GLuint shader; 18 | 19 | /* Basic parameters */ 20 | GLint proj; 21 | GLint tex; 22 | GLint alpha; 23 | GLint pos_attrib; 24 | GLint tex_attrib; 25 | 26 | GLint offset_x; 27 | GLint offset_y; 28 | GLint scale_x; 29 | GLint scale_y; 30 | GLint width; 31 | GLint height; 32 | 33 | /* pywm-defined additional parameters */ 34 | GLint padding_l; 35 | GLint padding_t; 36 | GLint padding_r; 37 | GLint padding_b; 38 | GLint cornerradius; 39 | GLint lock_perc; 40 | }; 41 | 42 | struct wm_renderer_texture_shaders { 43 | const char* name; 44 | 45 | struct wm_renderer_texture_shader rgba; 46 | struct wm_renderer_texture_shader rgbx; 47 | struct wm_renderer_texture_shader ext; 48 | }; 49 | 50 | struct wm_renderer_primitive_shader { 51 | GLuint shader; 52 | const char* name; 53 | 54 | /* Basic parameters */ 55 | GLint proj; 56 | GLint alpha; 57 | GLint pos_attrib; 58 | GLint tex_attrib; 59 | 60 | GLint width; 61 | GLint height; 62 | 63 | /* Additional parameters */ 64 | int n_params_int; 65 | int n_params_float; 66 | GLint params_float; 67 | GLint params_int; 68 | }; 69 | 70 | #define WM_RENDERER_DOWNSAMPLE_BUFFERS 4 71 | 72 | struct wm_renderer_buffers { 73 | int width; 74 | int height; 75 | struct wm_renderer* parent; 76 | 77 | GLuint frame_buffer; 78 | GLuint frame_buffer_rbo; 79 | GLuint frame_buffer_tex; 80 | 81 | GLuint downsample_buffers[WM_RENDERER_DOWNSAMPLE_BUFFERS]; 82 | GLuint downsample_buffers_rbo[WM_RENDERER_DOWNSAMPLE_BUFFERS]; 83 | GLuint downsample_buffers_tex[WM_RENDERER_DOWNSAMPLE_BUFFERS]; 84 | 85 | int downsample_buffers_width[WM_RENDERER_DOWNSAMPLE_BUFFERS]; 86 | int downsample_buffers_height[WM_RENDERER_DOWNSAMPLE_BUFFERS]; 87 | }; 88 | 89 | void wm_renderer_buffers_init(struct wm_renderer_buffers* buffers, struct wm_renderer* renderer, int width, int height); 90 | void wm_renderer_buffers_destroy(struct wm_renderer_buffers* buffers); 91 | void wm_renderer_buffers_ensure(struct wm_renderer* renderer, struct wm_output* output); 92 | 93 | #endif 94 | 95 | enum wm_renderer_mode { 96 | /* Pass all methods to wlr_renderer */ 97 | WM_RENDERER_WLR, 98 | 99 | /* Use own shaders */ 100 | WM_RENDERER_PYWM, 101 | }; 102 | 103 | struct wm_renderer { 104 | struct wm_server* wm_server; 105 | struct wlr_renderer* wlr_renderer; 106 | 107 | struct wm_output* current; 108 | 109 | enum wm_renderer_mode mode; 110 | 111 | #ifdef WM_CUSTOM_RENDERER 112 | int n_texture_shaders; 113 | struct wm_renderer_texture_shaders* texture_shaders; 114 | 115 | struct { 116 | GLuint shader; 117 | GLint tex; 118 | GLint pos_attrib; 119 | GLint tex_attrib; 120 | } quad_shader; 121 | struct { 122 | GLuint shader; 123 | GLint tex; 124 | GLint pos_attrib; 125 | GLint tex_attrib; 126 | 127 | GLint halfpixel; 128 | GLint offset; 129 | } downsample_shader; 130 | struct { 131 | GLuint shader; 132 | GLint tex; 133 | GLint pos_attrib; 134 | GLint tex_attrib; 135 | 136 | GLint halfpixel; 137 | GLint offset; 138 | 139 | GLint width; 140 | GLint height; 141 | GLint padding_l; 142 | GLint padding_t; 143 | GLint padding_r; 144 | GLint padding_b; 145 | GLint cornerradius; 146 | } upsample_shader; 147 | 148 | int n_primitive_shaders; 149 | struct wm_renderer_primitive_shader* primitive_shaders; 150 | 151 | struct wm_renderer_texture_shaders* texture_shaders_selected; 152 | struct wm_renderer_primitive_shader* primitive_shader_selected; 153 | 154 | unsigned int selected_buffer; 155 | #endif 156 | }; 157 | 158 | void wm_renderer_init(struct wm_renderer *renderer, struct wm_server *server); 159 | void wm_renderer_destroy(struct wm_renderer *renderer); 160 | int wm_renderer_init_output(struct wm_renderer* renderer, struct wm_output* output); 161 | 162 | #ifdef WM_CUSTOM_RENDERER 163 | 164 | void wm_renderer_init_texture_shaders(struct wm_renderer* renderer, int n_shaders); 165 | void wm_renderer_add_texture_shaders(struct wm_renderer* renderer, const char* name, 166 | const GLchar* vert_src, 167 | const GLchar* frag_src_rgba, 168 | const GLchar* frag_src_rgbx, 169 | const GLchar* frag_src_ext); 170 | 171 | void wm_renderer_init_primitive_shaders(struct wm_renderer* renderer, int n_shaders); 172 | void wm_renderer_add_primitive_shader(struct wm_renderer* renderer, const char* name, 173 | const GLchar* vert_src, const GLchar* frag_src, int n_params_int, int n_params_float); 174 | 175 | #endif 176 | 177 | void wm_renderer_select_texture_shaders(struct wm_renderer* renderer, const char* name); 178 | void wm_renderer_select_primitive_shader(struct wm_renderer* renderer, const char* name); 179 | bool wm_renderer_check_primitive_params(struct wm_renderer* renderer, int n_int, int n_float); 180 | 181 | void wm_renderer_to_buffer(struct wm_renderer* renderer, unsigned int buffer); 182 | 183 | void wm_renderer_begin(struct wm_renderer *renderer, struct wm_output *output); 184 | void wm_renderer_end(struct wm_renderer *renderer, pixman_region32_t *damage, 185 | struct wm_output *output); 186 | void wm_renderer_render_texture_at(struct wm_renderer *renderer, 187 | pixman_region32_t *damage, 188 | struct wlr_surface* surface, 189 | struct wlr_texture *texture, 190 | struct wlr_box *box, double opacity, 191 | struct wlr_box *mask, 192 | double corner_radius, double lock_perc); 193 | 194 | void wm_renderer_render_primitive(struct wm_renderer* renderer, 195 | pixman_region32_t* damage, 196 | struct wlr_box* box, 197 | double opacity, int* params_int, float* params_float); 198 | 199 | void wm_renderer_apply_blur(struct wm_renderer* renderer, 200 | pixman_region32_t* damage, 201 | int extend_damage, 202 | struct wlr_box* box, 203 | int radius, 204 | int passes, 205 | double cornerradius); 206 | 207 | void wm_renderer_clear(struct wm_renderer* renderer, 208 | pixman_region32_t* damage, 209 | float* color); 210 | 211 | void wm_renderer_ensure_mode(struct wm_renderer* renderer, enum wm_renderer_mode mode); 212 | 213 | #endif 214 | -------------------------------------------------------------------------------- /include/wm/wm_seat.h: -------------------------------------------------------------------------------- 1 | #ifndef WM_SEAT_H 2 | #define WM_SEAT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct wm_server; 10 | struct wm_cursor; 11 | struct wm_layout; 12 | 13 | struct wm_seat{ 14 | struct wm_server* wm_server; 15 | 16 | struct wm_cursor* wm_cursor; 17 | 18 | struct wlr_seat* wlr_seat; 19 | struct wl_list wm_keyboards; 20 | struct wl_list wm_pointers; 21 | 22 | struct wl_listener request_start_drag; 23 | struct wl_listener start_drag; 24 | struct wl_listener request_set_selection; 25 | struct wl_listener request_set_primary_selection; 26 | struct wl_listener request_set_cursor; 27 | struct wl_listener destroy; 28 | 29 | struct { 30 | double x0, xm; 31 | double y0, ym; 32 | bool active; 33 | } seatop_down; 34 | }; 35 | 36 | void wm_seat_init(struct wm_seat* seat, struct wm_server* server, struct wm_layout* layout); 37 | void wm_seat_destroy(struct wm_seat* seat); 38 | 39 | void wm_seat_add_input_device(struct wm_seat* seat, struct wlr_input_device* input_device); 40 | 41 | void wm_seat_clear_focus(struct wm_seat* seat); 42 | void wm_seat_focus_surface(struct wm_seat* seat, struct wlr_surface* surface); 43 | 44 | /* Pass input on to client */ 45 | void wm_seat_dispatch_key(struct wm_seat* seat, struct wlr_input_device* input_device, struct wlr_event_keyboard_key* event); 46 | void wm_seat_dispatch_modifiers(struct wm_seat* seat, struct wlr_input_device* input_device); 47 | 48 | /* true means event has been dispatched */ 49 | bool wm_seat_dispatch_motion(struct wm_seat* seat, double x, double y, uint32_t time_msec); 50 | void wm_seat_dispatch_button(struct wm_seat* seat, struct wlr_event_pointer_button* event); 51 | void wm_seat_dispatch_axis(struct wm_seat* seat, struct wlr_event_pointer_axis* event); 52 | void wm_seat_kill_seatop(struct wm_seat* seat); 53 | 54 | void wm_seat_reconfigure(struct wm_seat* seat); 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /include/wm/wm_server.h: -------------------------------------------------------------------------------- 1 | #ifndef WM_SERVER_H 2 | #define WM_SERVER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | struct wm_config; 16 | struct wm_seat; 17 | struct wm_layout; 18 | struct wm_renderer; 19 | struct wm_output; 20 | struct wm_idle_inhibit; 21 | 22 | struct wm_server{ 23 | struct wm_config* wm_config; 24 | 25 | struct wl_display* wl_display; 26 | struct wl_event_loop* wl_event_loop; 27 | 28 | struct wlr_backend* wlr_backend; 29 | struct wlr_backend* wlr_headless_backend; 30 | 31 | struct wlr_allocator* wlr_allocator; 32 | 33 | struct wlr_compositor* wlr_compositor; 34 | struct wlr_subcompositor* wlr_subcompositor; 35 | struct wlr_data_device_manager* wlr_data_device_manager; 36 | struct wlr_xdg_shell* wlr_xdg_shell; 37 | struct wlr_server_decoration_manager* wlr_server_decoration_manager; 38 | struct wlr_xdg_decoration_manager_v1* wlr_xdg_decoration_manager; 39 | #ifdef WM_HAS_XWAYLAND 40 | struct wlr_xwayland* wlr_xwayland; 41 | #endif 42 | struct wlr_xcursor_manager* wlr_xcursor_manager; 43 | struct wlr_virtual_keyboard_manager_v1* wlr_virtual_keyboard_manager; 44 | struct wlr_virtual_pointer_manager_v1* wlr_virtual_pointer_manager; 45 | struct wlr_layer_shell_v1* wlr_layer_shell; 46 | 47 | struct wm_renderer* wm_renderer; 48 | struct wm_seat* wm_seat; 49 | struct wm_layout* wm_layout; 50 | struct wm_idle_inhibit* wm_idle_inhibit; 51 | 52 | /* Sorted by z-index (highest first) */ 53 | struct wl_list wm_contents; // wm_content::link 54 | 55 | struct wl_listener new_input; 56 | struct wl_listener new_virtual_pointer; 57 | struct wl_listener new_virtual_keyboard; 58 | struct wl_listener new_output; 59 | struct wl_listener new_xdg_surface; 60 | struct wl_listener new_layer_surface; 61 | struct wl_listener new_server_decoration; 62 | struct wl_listener new_xdg_decoration; 63 | struct wl_listener xwayland_ready; 64 | struct wl_listener new_xwayland_surface; 65 | 66 | double lock_perc; 67 | 68 | int constant_damage_mode; 69 | struct wl_event_source* callback_timer; 70 | }; 71 | 72 | void wm_server_init(struct wm_server* server, struct wm_config* config); 73 | void wm_server_destroy(struct wm_server* server); 74 | 75 | void wm_server_surface_at(struct wm_server* server, double at_x, double at_y, 76 | struct wlr_surface** result, double* result_sx, double* result_sy, double* result_scale_x, double* result_scale_y); 77 | struct wm_view* wm_server_view_for_surface(struct wm_server* server, struct wlr_surface* surface); 78 | 79 | void wm_server_update_contents(struct wm_server* server); 80 | 81 | void wm_server_open_virtual_output(struct wm_server* server, const char* name); 82 | void wm_server_close_virtual_output(struct wm_server* server, const char* name); 83 | 84 | void wm_server_set_constant_damage_mode(struct wm_server* server, int mode); 85 | /* 86 | * Schedule wm_callback_update() after frame present 87 | */ 88 | void wm_server_schedule_update(struct wm_server* server, struct wm_output* from_output); 89 | 90 | void wm_server_set_locked(struct wm_server* server, double lock_perc); 91 | bool wm_server_is_locked(struct wm_server* server); 92 | 93 | void wm_server_printf(FILE* file, struct wm_server* server); 94 | 95 | /* Update after new wm_config key-vals where suitable */ 96 | void wm_server_reconfigure(struct wm_server* server); 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /include/wm/wm_util.h: -------------------------------------------------------------------------------- 1 | #ifndef WM_UTIL_H 2 | #define WM_UTIL_H 3 | 4 | #include 5 | #include 6 | 7 | /* Warning - very chatty */ 8 | // #define DEBUG_PERFORMANCE_ENABLED 9 | 10 | #define wm_offset_of(_struct_, _member_) (size_t)&(((struct _struct_ *)0)->_member_) 11 | 12 | #define wm_cast(_subclass_, _superclass_pt_) \ 13 | \ 14 | ((struct _subclass_ *)( \ 15 | (unsigned char *)(_superclass_pt_) \ 16 | - wm_offset_of(_subclass_, super) \ 17 | ) \ 18 | ) 19 | 20 | 21 | static inline long msec_diff(struct timespec t1, struct timespec t2){ 22 | return (t1.tv_sec - t2.tv_sec) * 1000L + (t1.tv_nsec - t2.tv_nsec) / 1000000L; 23 | } 24 | 25 | static inline double secs_now(){ 26 | struct timespec now; 27 | clock_gettime(CLOCK_REALTIME, &now); 28 | return ((double)now.tv_sec) + (double)now.tv_nsec / 1000000000.; 29 | } 30 | 31 | #define TIMER_DEFINE(TNAME) \ 32 | static struct timespec TIMER_ ## TNAME ## _start; \ 33 | static struct timespec TIMER_ ## TNAME ## _end; \ 34 | static long long int TIMER_ ## TNAME ## _tmp; \ 35 | static long long int TIMER_ ## TNAME ## _max = 0; \ 36 | static struct timespec TIMER_ ## TNAME ## _last_print = { 0 }; \ 37 | static struct timespec TIMER_ ## TNAME ## _print; \ 38 | static int TIMER_ ## TNAME ## _n_agg = 0; \ 39 | static long long int TIMER_ ## TNAME ## _nsec_agg = 0; 40 | 41 | #define TIMER_STARTONLY(TNAME) \ 42 | clock_gettime(CLOCK_REALTIME, &TIMER_ ## TNAME ## _start); 43 | 44 | #define TIMER_START(TNAME) \ 45 | TIMER_DEFINE(TNAME); \ 46 | clock_gettime(CLOCK_REALTIME, &TIMER_ ## TNAME ## _start); 47 | 48 | #define TIMER_STOP(TNAME) \ 49 | clock_gettime(CLOCK_REALTIME, &TIMER_ ## TNAME ## _end); \ 50 | TIMER_ ## TNAME ## _n_agg++; \ 51 | TIMER_ ## TNAME ## _tmp = TIMER_ ## TNAME ## _end.tv_nsec - TIMER_ ## TNAME ## _start.tv_nsec; \ 52 | TIMER_ ## TNAME ## _tmp += 1000000000 * (TIMER_ ## TNAME ## _end.tv_sec - TIMER_ ## TNAME ## _start.tv_sec);\ 53 | TIMER_ ## TNAME ## _nsec_agg += TIMER_ ## TNAME ## _tmp; \ 54 | TIMER_ ## TNAME ## _max = TIMER_ ## TNAME ## _tmp > TIMER_ ## TNAME ## _max ? TIMER_ ## TNAME ## _tmp : TIMER_ ## TNAME ## _max; 55 | 56 | 57 | #define TIMER_PRINT(TNAME) \ 58 | clock_gettime(CLOCK_REALTIME, &TIMER_ ## TNAME ## _print); \ 59 | if(msec_diff(TIMER_ ## TNAME ## _print, TIMER_ ## TNAME ## _last_print) > 1000. / 0.1){ \ 60 | wlr_log(WLR_DEBUG, "\nTIMER[%-30s] %s: %7.2fms (%7.2fms max), %5.2fHz", #TNAME, \ 61 | ((double)TIMER_ ## TNAME ## _max / 1000000.) > 10. ? "X" : \ 62 | ((double)TIMER_ ## TNAME ## _max / 1000000.) > 5. ? "o" : \ 63 | ((double)TIMER_ ## TNAME ## _max / 1000000.) > 1. ? "." : " ", \ 64 | (double)TIMER_ ## TNAME ## _nsec_agg / TIMER_ ## TNAME ## _n_agg / 1000000., \ 65 | (double)TIMER_ ## TNAME ## _max / 1000000., \ 66 | (double)TIMER_ ## TNAME ## _n_agg * 0.1); \ 67 | TIMER_ ## TNAME ## _n_agg = 0; \ 68 | TIMER_ ## TNAME ## _max = 0; \ 69 | TIMER_ ## TNAME ## _nsec_agg = 0; \ 70 | TIMER_ ## TNAME ## _last_print = TIMER_ ## TNAME ## _print; \ 71 | } 72 | 73 | #ifdef DEBUG_PERFORMANCE_ENABLED 74 | #define DEBUG_PERFORMANCE(name, output) wlr_log(WLR_DEBUG, "DEBUGPERFORMANCE[%s(%d)]: %.6f", #name, output, secs_now()); 75 | #define DEBUG_PERFORMANCE_PTR(name, output) wlr_log(WLR_DEBUG, "DEBUGPERFORMANCE[%s(%d)]: %.6f", name, output, secs_now()); 76 | #else 77 | #define DEBUG_PERFORMANCE(name, output); 78 | #define DEBUG_PERFORMANCE_PTR(name, output); 79 | #endif 80 | 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /include/wm/wm_view.h: -------------------------------------------------------------------------------- 1 | #ifndef WM_VIEW_H 2 | #define WM_VIEW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "wm_content.h" 11 | 12 | struct wm_seat; 13 | struct wm_view_vtable; 14 | 15 | typedef void (*wm_surface_iterator_func_t)(struct wlr_surface *surface, 16 | int sx, int sy, bool constrained, void *data); 17 | 18 | struct wm_view { 19 | struct wm_content super; 20 | 21 | struct wm_view_vtable* vtable; 22 | bool mapped; 23 | bool inhibiting_idle; 24 | 25 | bool accepts_input; 26 | 27 | /* defaults to false; if by means of wlr_server_decoration or wlr_toplevel_decoration we know the view is decorated: true */ 28 | bool shows_csd; 29 | 30 | /* Server-side determined states - stored from setter */ 31 | bool floating; 32 | bool focused; 33 | bool fullscreen; 34 | bool maximized; 35 | bool resizing; 36 | }; 37 | 38 | void wm_view_base_init(struct wm_view* view, struct wm_server* server); 39 | 40 | void wm_view_set_inhibiting_idle(struct wm_view* view, bool inhibiting_idle); 41 | bool wm_view_is_inhibiting_idle(struct wm_view* view); 42 | 43 | bool wm_content_is_view(struct wm_content* content); 44 | bool wm_view_shows_csd(struct wm_view* view); 45 | 46 | struct wm_view_vtable { 47 | void (*destroy)(struct wm_view* view); 48 | 49 | struct wlr_surface* (*surface_at)(struct wm_view* view, double at_x, double at_y, double* sx, double* sy); 50 | void (*for_each_surface)(struct wm_view* view, wm_surface_iterator_func_t iterator, void* user_data); 51 | void (*set_activated)(struct wm_view* view, bool activated); 52 | 53 | void (*get_credentials)(struct wm_view* view, pid_t* pid, uid_t* uid, gid_t* gid); 54 | void (*get_info)(struct wm_view* view, const char** title, const char** app_id, const char** role); 55 | void (*get_size)(struct wm_view* view, int* width, int* height); 56 | void (*get_offset)(struct wm_view* view, int* offset_x, int* offset_y); 57 | void (*get_size_constraints)(struct wm_view* view, int** constraints, int* n_constraints); 58 | struct wm_view* (*get_parent)(struct wm_view* view); 59 | 60 | void (*request_size)(struct wm_view* view, int width, int height); 61 | void (*set_floating)(struct wm_view* view, bool floating); 62 | void (*set_resizing)(struct wm_view* view, bool resizing); 63 | void (*set_fullscreen)(struct wm_view* view, bool fullscreen); 64 | void (*set_maximized)(struct wm_view* view, bool maximized); 65 | void (*focus)(struct wm_view* view, struct wm_seat* seat); 66 | void (*request_close)(struct wm_view* view); 67 | 68 | void (*structure_printf)(FILE* file, struct wm_view* view); 69 | }; 70 | 71 | static inline void wm_view_get_credentials(struct wm_view* view, pid_t* pid, uid_t* uid, gid_t* gid){ 72 | (*view->vtable->get_credentials)(view, pid, uid, gid); 73 | } 74 | 75 | static inline void wm_view_get_info(struct wm_view* view, const char** title, const char** app_id, const char** role){ 76 | (*view->vtable->get_info)(view, title, app_id, role); 77 | } 78 | 79 | static inline void wm_view_request_size(struct wm_view* view, int width, int height){ 80 | (*view->vtable->request_size)(view, width, height); 81 | } 82 | 83 | static inline void wm_view_get_size_constraints(struct wm_view* view, int** constraints, int* n_constraints){ 84 | (*view->vtable->get_size_constraints)(view, constraints, n_constraints); 85 | } 86 | 87 | static inline void wm_view_get_size(struct wm_view* view, int* width, int* height){ 88 | (*view->vtable->get_size)(view, width, height); 89 | } 90 | 91 | static inline void wm_view_get_offset(struct wm_view* view, int* offset_x, int* offset_y){ 92 | (*view->vtable->get_offset)(view, offset_x, offset_y); 93 | } 94 | 95 | static inline void wm_view_focus(struct wm_view* view, struct wm_seat* seat){ 96 | (*view->vtable->focus)(view, seat); 97 | } 98 | 99 | static inline void wm_view_set_floating(struct wm_view* view, bool floating){ 100 | view->floating = floating; 101 | (*view->vtable->set_floating)(view, floating); 102 | } 103 | 104 | static inline void wm_view_set_resizing(struct wm_view* view, bool resizing){ 105 | view->resizing = resizing; 106 | (*view->vtable->set_resizing)(view, resizing); 107 | } 108 | 109 | static inline void wm_view_set_fullscreen(struct wm_view* view, bool fullscreen){ 110 | view->fullscreen = fullscreen; 111 | (*view->vtable->set_fullscreen)(view, fullscreen); 112 | } 113 | 114 | static inline void wm_view_set_maximized(struct wm_view* view, bool maximized){ 115 | view->maximized = maximized; 116 | (*view->vtable->set_maximized)(view, maximized); 117 | } 118 | 119 | static inline void wm_view_set_activated(struct wm_view* view, bool activated){ 120 | view->focused = activated; 121 | (*view->vtable->set_activated)(view, activated); 122 | } 123 | 124 | static inline bool wm_view_is_floating(struct wm_view* view){ 125 | return view->floating; 126 | } 127 | 128 | static inline bool wm_view_is_focused(struct wm_view* view){ 129 | return view->focused; 130 | } 131 | 132 | static inline bool wm_view_is_fullscreen(struct wm_view* view){ 133 | return view->fullscreen; 134 | } 135 | 136 | static inline bool wm_view_is_maximized(struct wm_view* view){ 137 | return view->maximized; 138 | } 139 | 140 | static inline bool wm_view_is_resizing(struct wm_view* view){ 141 | return view->resizing; 142 | } 143 | 144 | static inline void wm_view_request_close(struct wm_view* view){ 145 | (*view->vtable->request_close)(view); 146 | } 147 | 148 | static inline struct wlr_surface* wm_view_surface_at(struct wm_view* view, double at_x, double at_y, double* sx, double* sy){ 149 | return (*view->vtable->surface_at)(view, at_x, at_y, sx, sy); 150 | } 151 | 152 | static inline void wm_view_for_each_surface(struct wm_view* view, wm_surface_iterator_func_t iterator, void* user_data){ 153 | (*view->vtable->for_each_surface)(view, iterator, user_data); 154 | } 155 | 156 | 157 | static inline struct wm_view* wm_view_get_parent(struct wm_view* view){ 158 | return (*view->vtable->get_parent)(view); 159 | } 160 | 161 | static inline void wm_view_structure_printf(FILE* file, struct wm_view* view){ 162 | (*view->vtable->structure_printf)(file, view); 163 | } 164 | 165 | 166 | 167 | #endif 168 | -------------------------------------------------------------------------------- /include/wm/wm_view_layer.h: -------------------------------------------------------------------------------- 1 | #ifndef WM_VIEW_LAYER_H 2 | #define WM_VIEW_LAYER_H 3 | 4 | #include 5 | #include 6 | 7 | #include "wm/wm_view.h" 8 | 9 | struct wm_view_layer; 10 | 11 | struct wm_layer_subsurface { 12 | struct wl_list link; //wm_view_layer::subsurfaces / wm_popup_layer::subsurfaces / wm_layer_subsurface::subsurfaces 13 | 14 | struct wm_view_layer* root; 15 | 16 | struct wlr_subsurface* wlr_subsurface; 17 | 18 | struct wl_list subsurfaces; 19 | 20 | struct wl_listener map; 21 | struct wl_listener unmap; 22 | struct wl_listener destroy; 23 | struct wl_listener new_subsurface; 24 | struct wl_listener surface_commit; 25 | }; 26 | 27 | void wm_layer_subsurface_init(struct wm_layer_subsurface* subsurface, struct wm_view_layer* root, struct wlr_subsurface* wlr_subsurface); 28 | void wm_layer_subsurface_destroy(struct wm_layer_subsurface* subsurface); 29 | 30 | 31 | struct wm_popup_layer { 32 | struct wl_list link; //wm_view_layer::popups / wm_popup_layer::popups 33 | 34 | struct wm_view_layer* root; 35 | 36 | struct wlr_xdg_popup* wlr_xdg_popup; 37 | 38 | struct wl_list popups; 39 | struct wl_list subsurfaces; 40 | 41 | struct wl_listener map; 42 | struct wl_listener unmap; 43 | struct wl_listener destroy; 44 | struct wl_listener new_popup; 45 | struct wl_listener new_subsurface; 46 | struct wl_listener surface_commit; 47 | }; 48 | 49 | void wm_popup_layer_init(struct wm_popup_layer* popup, struct wm_view_layer* root, struct wlr_xdg_popup* wlr_xdg_popup); 50 | void wm_popup_layer_destroy(struct wm_popup_layer* popup); 51 | 52 | struct wm_view_layer { 53 | struct wm_view super; 54 | 55 | struct wlr_layer_surface_v1* wlr_layer_surface; 56 | 57 | int width; 58 | int height; 59 | 60 | struct wl_list popups; 61 | struct wl_list subsurfaces; 62 | 63 | int size_constraints[10]; 64 | 65 | struct wl_listener map; 66 | struct wl_listener unmap; 67 | struct wl_listener destroy; 68 | struct wl_listener new_popup; 69 | struct wl_listener new_subsurface; 70 | 71 | struct wl_listener surface_commit; 72 | }; 73 | 74 | void wm_view_layer_init(struct wm_view_layer* view, struct wm_server* server, struct wlr_layer_surface_v1* surface); 75 | 76 | 77 | #endif // 78 | -------------------------------------------------------------------------------- /include/wm/wm_view_xdg.h: -------------------------------------------------------------------------------- 1 | #ifndef WM_VIEW_XDG_H 2 | #define WM_VIEW_XDG_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "wm/wm_view.h" 9 | 10 | struct wm_view_xdg; 11 | 12 | struct wm_xdg_subsurface { 13 | struct wl_list link; //wm_view_xdg::subsurfaces / wm_popup_xdg::subsurfaces / wm_xdg_subsurface::subsurfaces 14 | 15 | struct wm_view_xdg* toplevel; 16 | 17 | struct wlr_subsurface* wlr_subsurface; 18 | 19 | struct wl_list subsurfaces; 20 | 21 | struct wl_listener map; 22 | struct wl_listener unmap; 23 | struct wl_listener destroy; 24 | struct wl_listener new_subsurface; 25 | struct wl_listener surface_commit; 26 | }; 27 | 28 | void wm_xdg_subsurface_init(struct wm_xdg_subsurface* subsurface, struct wm_view_xdg* toplevel, struct wlr_subsurface* wlr_subsurface); 29 | void wm_xdg_subsurface_destroy(struct wm_xdg_subsurface* subsurface); 30 | 31 | 32 | struct wm_popup_xdg { 33 | struct wl_list link; //wm_view_xdg::popups / wm_popup_xdg::popups 34 | 35 | struct wm_view_xdg* toplevel; 36 | 37 | struct wlr_xdg_popup* wlr_xdg_popup; 38 | 39 | struct wl_list popups; 40 | struct wl_list subsurfaces; 41 | 42 | struct wl_listener map; 43 | struct wl_listener unmap; 44 | struct wl_listener destroy; 45 | struct wl_listener new_popup; 46 | struct wl_listener new_subsurface; 47 | struct wl_listener surface_commit; 48 | }; 49 | 50 | void wm_popup_xdg_init(struct wm_popup_xdg* popup, struct wm_view_xdg* toplevel, struct wlr_xdg_popup* wlr_xdg_popup); 51 | void wm_popup_xdg_destroy(struct wm_popup_xdg* popup); 52 | 53 | struct wm_view_xdg { 54 | struct wm_view super; 55 | 56 | struct wlr_xdg_surface* wlr_xdg_surface; 57 | struct wlr_xdg_toplevel_decoration_v1* wlr_deco; 58 | struct wlr_server_decoration* wlr_server_deco; 59 | 60 | struct wl_list popups; 61 | struct wl_list subsurfaces; 62 | 63 | bool initialized; 64 | 65 | int floating_set; /* -1: not at all, 0: false, 1: true */ 66 | bool constrain_popups_to_toplevel; /* false = constrain to output */ 67 | 68 | int width; 69 | int height; 70 | 71 | int size_constraints[4]; 72 | 73 | struct wl_listener map; 74 | struct wl_listener unmap; 75 | struct wl_listener destroy; 76 | struct wl_listener new_popup; 77 | struct wl_listener new_subsurface; 78 | 79 | struct wl_listener surface_configure; 80 | struct wl_listener surface_ack_configure; 81 | struct wl_listener surface_commit; 82 | 83 | struct wl_listener deco_request_mode; 84 | struct wl_listener deco_destroy; 85 | struct wl_listener server_deco_mode; 86 | struct wl_listener server_deco_destroy; 87 | 88 | struct wl_listener request_fullscreen; 89 | struct wl_listener request_move; 90 | struct wl_listener request_resize; 91 | struct wl_listener request_maximize; 92 | struct wl_listener request_minimize; 93 | struct wl_listener request_show_window_menu; 94 | }; 95 | 96 | void wm_view_xdg_init(struct wm_view_xdg* view, struct wm_server* server, struct wlr_xdg_surface* surface); 97 | bool wm_view_is_xdg(struct wm_view* view); 98 | 99 | void wm_view_xdg_register_server_decoration(struct wm_view_xdg* view, struct wlr_server_decoration* wlr_deco); 100 | void wm_view_xdg_register_decoration(struct wm_view_xdg* view, struct wlr_xdg_toplevel_decoration_v1* wlr_deco); 101 | 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /include/wm/wm_view_xwayland.h: -------------------------------------------------------------------------------- 1 | #ifndef WM_VIEW_XWAYLAND_H 2 | #define WM_VIEW_XWAYLAND_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "wm/wm_view.h" 9 | 10 | struct wm_view_xwayland; 11 | 12 | struct wm_view_xwayland_child { 13 | struct wl_list link; // wm_view_xwayland::children 14 | struct wm_view_xwayland* parent; 15 | 16 | struct wlr_xwayland_surface* wlr_xwayland_surface; 17 | 18 | bool mapped; 19 | 20 | struct wl_listener request_configure; 21 | struct wl_listener map; 22 | struct wl_listener unmap; 23 | struct wl_listener destroy; 24 | struct wl_listener surface_commit; 25 | }; 26 | 27 | void wm_view_xwayland_child_init(struct wm_view_xwayland_child* child, struct wm_view_xwayland* parent, struct wlr_xwayland_surface* surface); 28 | void wm_view_xwayland_child_destroy(struct wm_view_xwayland_child* child); 29 | 30 | 31 | struct wm_view_xwayland { 32 | struct wm_view super; 33 | 34 | struct wlr_xwayland_surface* wlr_xwayland_surface; 35 | 36 | struct wl_list children; 37 | 38 | struct wm_view_xwayland* parent; 39 | 40 | int size_constraints[4]; 41 | 42 | struct wl_listener request_configure; 43 | struct wl_listener set_parent; 44 | struct wl_listener set_pid; 45 | struct wl_listener map; 46 | struct wl_listener unmap; 47 | struct wl_listener destroy; 48 | struct wl_listener surface_commit; 49 | }; 50 | 51 | void wm_view_xwayland_init(struct wm_view_xwayland* view, struct wm_server* server, struct wlr_xwayland_surface* surface); 52 | 53 | int wm_view_is_xwayland(struct wm_view* view); 54 | 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /include/wm/wm_widget.h: -------------------------------------------------------------------------------- 1 | #ifndef WM_WIDGET_H 2 | #define WM_WIDGET_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "wm_content.h" 9 | 10 | struct wm_server; 11 | 12 | struct wm_widget { 13 | struct wm_content super; 14 | 15 | /* 16 | * Widgets are fixed to one output - only for comparison; don't dereference 17 | */ 18 | struct wm_output* wm_output; 19 | 20 | /* Either texture needs to be set, or primitive */ 21 | struct wlr_texture* wlr_texture; 22 | struct { 23 | char* name; 24 | int n_params_int; 25 | int* params_int; 26 | int n_params_float; 27 | float* params_float; 28 | } primitive; 29 | }; 30 | 31 | void wm_widget_init(struct wm_widget* widget, struct wm_server* server); 32 | 33 | void wm_widget_set_pixels(struct wm_widget* widget, uint32_t format, uint32_t stride, uint32_t width, uint32_t height, const void* data); 34 | 35 | void wm_widget_set_primitive(struct wm_widget* widget, char* name, int n_params_int, int* params_int, int n_params_float, float* params_float); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'pywm', 3 | 'c', 4 | version: '0.1.0', 5 | license: 'MIT', 6 | meson_version: '>=0.48.0', 7 | default_options : [ 8 | 'c_std=c11', 9 | 'warning_level=2' 10 | ], 11 | ) 12 | 13 | add_project_arguments( 14 | '-Wno-unused-parameter', 15 | language: 'c', 16 | ) 17 | 18 | add_project_arguments( 19 | '-DWLR_USE_UNSTABLE', 20 | language: 'c', 21 | ) 22 | 23 | if get_option('custom_renderer').enabled() 24 | add_project_arguments( 25 | '-DWM_CUSTOM_RENDERER', 26 | language: 'c', 27 | ) 28 | endif 29 | 30 | 31 | # Dependencies 32 | 33 | wayland_server = dependency('wayland-server') 34 | wayland_client = dependency('wayland-client') 35 | wayland_cursor = dependency('wayland-cursor') 36 | wayland_egl = dependency('wayland-egl') 37 | wayland_protos = dependency('wayland-protocols', version: '>=1.14') 38 | xkbcommon = dependency('xkbcommon') 39 | libinput = dependency('libinput') 40 | pixman = dependency('pixman-1') 41 | xwayland = dependency('xwayland', required: false) 42 | pthread = meson.get_compiler('c').find_library('pthread') 43 | math = meson.get_compiler('c').find_library('m') 44 | 45 | has_xwayland = xwayland.found() and get_option('xwayland').enabled() 46 | 47 | wlroots = subproject('wlroots', default_options: ['default_library=static']).get_variable('wlroots') 48 | 49 | if has_xwayland 50 | add_project_arguments( 51 | '-DWM_HAS_XWAYLAND', 52 | language: 'c', 53 | ) 54 | endif 55 | 56 | subdir('protocols') 57 | 58 | if get_option('custom_renderer').enabled() 59 | subdir('src/wm/shaders') 60 | endif 61 | 62 | 63 | deps = [ 64 | wlroots, 65 | wayland_server, 66 | server_protos, 67 | xkbcommon, 68 | libinput, 69 | pixman, 70 | xwayland, 71 | pthread, 72 | math 73 | ] 74 | 75 | sources = [ 76 | 'src/wm/wm.c', 77 | 'src/wm/wm_server.c', 78 | 'src/wm/wm_renderer.c', 79 | 'src/wm/wm_seat.c', 80 | 'src/wm/wm_keyboard.c', 81 | 'src/wm/wm_pointer.c', 82 | 'src/wm/wm_cursor.c', 83 | 'src/wm/wm_layout.c', 84 | 'src/wm/wm_output.c', 85 | 'src/wm/wm_content.c', 86 | 'src/wm/wm_view.c', 87 | 'src/wm/wm_view_xdg.c', 88 | 'src/wm/wm_view_layer.c', 89 | 'src/wm/wm_widget.c', 90 | 'src/wm/wm_config.c', 91 | 'src/wm/wm_idle_inhibit.c', 92 | 'src/wm/wm_drag.c', 93 | 'src/wm/wm_composite.c', 94 | ] 95 | 96 | if get_option('custom_renderer').enabled() 97 | sources += [ 98 | texture_shaders_c, 99 | primitive_shaders_c, 100 | quad_shaders_c 101 | ] 102 | endif 103 | 104 | if has_xwayland 105 | sources += [ 106 | 'src/wm/wm_view_xwayland.c', 107 | ] 108 | endif 109 | 110 | py_sources = [ 111 | 'src/py/_pywmmodule.c', 112 | 'src/py/_pywm_callbacks.c', 113 | 'src/py/_pywm_view.c', 114 | 'src/py/_pywm_widget.c' 115 | ] 116 | 117 | incs = include_directories('include') 118 | 119 | 120 | executable( 121 | 'pywm', 122 | sources + ['src/main.c'], 123 | include_directories: incs, 124 | dependencies: deps, 125 | ) 126 | 127 | python = import('python').find_installation('python3') 128 | 129 | python.extension_module( 130 | '_pywm', 131 | sources + py_sources, 132 | include_directories: incs, 133 | dependencies: deps + [python.dependency()], 134 | ) 135 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('custom_renderer', type : 'feature', value : 'enabled') 2 | option('xwayland', type : 'feature', value : 'enabled') 3 | -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | disallow_untyped_defs=True 3 | disallow_untyped_globals=True 4 | -------------------------------------------------------------------------------- /protocols/idle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | . 18 | ]]> 19 | 20 | 21 | This interface allows to monitor user idle time on a given seat. The interface 22 | allows to register timers which trigger after no user activity was registered 23 | on the seat for a given interval. It notifies when user activity resumes. 24 | 25 | This is useful for applications wanting to perform actions when the user is not 26 | interacting with the system, e.g. chat applications setting the user as away, power 27 | management features to dim screen, etc.. 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /protocols/meson.build: -------------------------------------------------------------------------------- 1 | wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir') 2 | 3 | wayland_scanner = find_program('wayland-scanner') 4 | 5 | # should check wayland_scanner's version, but it is hard to get 6 | if wayland_server.version().version_compare('>=1.14.91') 7 | code_type = 'private-code' 8 | else 9 | code_type = 'code' 10 | endif 11 | 12 | wayland_scanner_code = generator( 13 | wayland_scanner, 14 | output: '@BASENAME@-protocol.c', 15 | arguments: [code_type, '@INPUT@', '@OUTPUT@'], 16 | ) 17 | 18 | wayland_scanner_client = generator( 19 | wayland_scanner, 20 | output: '@BASENAME@-client-protocol.h', 21 | arguments: ['client-header', '@INPUT@', '@OUTPUT@'], 22 | ) 23 | 24 | wayland_scanner_server = generator( 25 | wayland_scanner, 26 | output: '@BASENAME@-protocol.h', 27 | arguments: ['server-header', '@INPUT@', '@OUTPUT@'], 28 | ) 29 | 30 | client_protocols = [ 31 | [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], 32 | [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], 33 | ['wlr-layer-shell-unstable-v1.xml'], 34 | ['idle.xml'], 35 | ['wlr-input-inhibitor-unstable-v1.xml'], 36 | ] 37 | 38 | server_protocols = [ 39 | [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], 40 | [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'], 41 | [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], 42 | ['wlr-layer-shell-unstable-v1.xml'], 43 | ['wlr-input-inhibitor-unstable-v1.xml'], 44 | ] 45 | 46 | client_protos_src = [] 47 | client_protos_headers = [] 48 | 49 | server_protos_src = [] 50 | server_protos_headers = [] 51 | 52 | foreach p : client_protocols 53 | xml = join_paths(p) 54 | client_protos_src += wayland_scanner_code.process(xml) 55 | client_protos_headers += wayland_scanner_client.process(xml) 56 | endforeach 57 | 58 | foreach p : server_protocols 59 | xml = join_paths(p) 60 | server_protos_src += wayland_scanner_code.process(xml) 61 | server_protos_headers += wayland_scanner_server.process(xml) 62 | endforeach 63 | 64 | lib_client_protos = static_library( 65 | 'client_protos', 66 | client_protos_src + client_protos_headers, 67 | dependencies: [wayland_client] 68 | ) # for the include directory 69 | 70 | client_protos = declare_dependency( 71 | link_with: lib_client_protos, 72 | sources: client_protos_headers, 73 | ) 74 | 75 | lib_server_protos = static_library( 76 | 'server_protos', 77 | server_protos_src + server_protos_headers, 78 | dependencies: [wayland_client] 79 | ) # for the include directory 80 | 81 | server_protos = declare_dependency( 82 | link_with: lib_server_protos, 83 | sources: server_protos_headers, 84 | ) 85 | -------------------------------------------------------------------------------- /protocols/wlr-input-inhibitor-unstable-v1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Copyright © 2018 Drew DeVault 5 | 6 | Permission to use, copy, modify, distribute, and sell this 7 | software and its documentation for any purpose is hereby granted 8 | without fee, provided that the above copyright notice appear in 9 | all copies and that both that copyright notice and this permission 10 | notice appear in supporting documentation, and that the name of 11 | the copyright holders not be used in advertising or publicity 12 | pertaining to distribution of the software without specific, 13 | written prior permission. The copyright holders make no 14 | representations about the suitability of this software for any 15 | purpose. It is provided "as is" without express or implied 16 | warranty. 17 | 18 | THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS 19 | SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 | FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 22 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 23 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 24 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 25 | THIS SOFTWARE. 26 | 27 | 28 | 29 | 30 | Clients can use this interface to prevent input events from being sent to 31 | any surfaces but its own, which is useful for example in lock screen 32 | software. It is assumed that access to this interface will be locked down 33 | to whitelisted clients by the compositor. 34 | 35 | 36 | 37 | 38 | Activates the input inhibitor. As long as the inhibitor is active, the 39 | compositor will not send input events to other clients. 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | While this resource exists, input to clients other than the owner of the 52 | inhibitor resource will not receive input events. The client that owns 53 | this resource will receive all input events normally. The compositor will 54 | also disable all of its own input processing (such as keyboard shortcuts) 55 | while the inhibitor is active. 56 | 57 | The compositor may continue to send input events to selected clients, 58 | such as an on-screen keyboard (via the input-method protocol). 59 | 60 | 61 | 62 | 63 | Destroy the inhibitor and allow other clients to receive input. 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /pywm/__init__.py: -------------------------------------------------------------------------------- 1 | from .pywm import ( 2 | PyWM, 3 | PyWMModifiers, 4 | PyWMOutput, 5 | PyWMDownstreamState, 6 | PYWM_MOD_SHIFT, 7 | PYWM_MOD_CAPS, 8 | PYWM_MOD_CTRL, 9 | PYWM_MOD_ALT, 10 | PYWM_MOD_MOD2, 11 | PYWM_MOD_MOD3, 12 | PYWM_MOD_LOGO, 13 | PYWM_MOD_MOD5, 14 | PYWM_RELEASED, 15 | PYWM_PRESSED, 16 | PYWM_TRANSFORM_NORMAL, 17 | PYWM_TRANSFORM_90, 18 | PYWM_TRANSFORM_180, 19 | PYWM_TRANSFORM_270, 20 | PYWM_TRANSFORM_FLIPPED, 21 | PYWM_TRANSFORM_FLIPPED_90, 22 | PYWM_TRANSFORM_FLIPPED_180, 23 | PYWM_TRANSFORM_FLIPPED_270, 24 | ) 25 | from .pywm_view import ( 26 | PyWMView, 27 | PyWMViewDownstreamState, 28 | PyWMViewUpstreamState 29 | ) 30 | 31 | from .pywm_widget import ( 32 | PyWMWidget, 33 | PyWMWidgetDownstreamState 34 | ) 35 | 36 | from .pywm_background_widget import PyWMBackgroundWidget 37 | from .pywm_cairo_widget import PyWMCairoWidget 38 | from .pywm_blur_widget import PyWMBlurWidget 39 | 40 | from .damage_tracked import DamageTracked 41 | from ._pywm import debug_performance 42 | -------------------------------------------------------------------------------- /pywm/_pywm.pyi: -------------------------------------------------------------------------------- 1 | from typing import Any, Callable 2 | 3 | def run(**kwargs: dict[str, Any]) -> None: ... 4 | def register(func: str, call: Callable[..., Any]) -> None: ... 5 | def damage(code: int) -> None: ... 6 | def debug_performance(key: str) -> None: ... 7 | -------------------------------------------------------------------------------- /pywm/damage_tracked.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from typing import Optional 3 | 4 | class DamageTracked: 5 | def __init__(self, parent: Optional[DamageTracked]=None) -> None: 6 | self._damage_parent = parent 7 | self._damage_children: list[DamageTracked] = [] 8 | 9 | if self._damage_parent is not None: 10 | self._damage_parent._damage_children += [self] 11 | 12 | self._damaged = False 13 | 14 | def damage_finish(self) -> None: 15 | if self._damage_parent is not None: 16 | self._damage_parent._damage_children.remove(self) 17 | 18 | def damage(self, propagate: bool=True) -> None: 19 | self._damaged = True 20 | if propagate: 21 | for c in self._damage_children: 22 | c.damage(propagate=True) 23 | 24 | def is_damaged(self) -> bool: 25 | res = self._damaged 26 | self._damaged = False 27 | return res 28 | -------------------------------------------------------------------------------- /pywm/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbuchermn/pywm/829a3ae963b27db7fe4a3022854a9e6f8bf1fd41/pywm/py.typed -------------------------------------------------------------------------------- /pywm/pywm_background_widget.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from typing import TYPE_CHECKING, Any 3 | 4 | import numpy as np 5 | import logging 6 | import hashlib 7 | from imageio import imread # type: ignore 8 | 9 | from .pywm_widget import ( 10 | PyWMWidget, 11 | ) 12 | 13 | if TYPE_CHECKING: 14 | from .pywm import PyWM, PyWMOutput, ViewT 15 | 16 | logger = logging.getLogger(__name__) 17 | 18 | _cache: list[tuple[str, str, np.ndarray]] = [] 19 | 20 | def _load(path: str) -> np.ndarray: 21 | global _cache 22 | 23 | cs = hashlib.md5(open(path, 'rb').read()).hexdigest() 24 | for p, c, a in _cache: 25 | if p == path and c == cs: 26 | return a 27 | 28 | im = imread(path) 29 | im_alpha = np.zeros(shape=(im.shape[0], im.shape[1], 4), 30 | dtype=np.uint8) 31 | im_alpha[:, :, 0] = im[:, :, 2] 32 | im_alpha[:, :, 1] = im[:, :, 1] 33 | im_alpha[:, :, 2] = im[:, :, 0] 34 | im_alpha[:, :, 3] = 255 35 | 36 | _cache = [(p, c, a) for p, c, a in _cache if p != path] + [(path, cs, im_alpha)] 37 | return im_alpha 38 | 39 | 40 | class PyWMBackgroundWidget(PyWMWidget): 41 | def __init__(self, wm: PyWM[ViewT], output: PyWMOutput, path: str, *args: Any, **kwargs: Any): 42 | """ 43 | transpose == 't': matrix transpose 44 | transpose == 'f': flip the image 45 | """ 46 | super().__init__(wm, output, *args, **kwargs) 47 | 48 | # Prevent division by zero 49 | self.width = 1 50 | self.height = 1 51 | 52 | try: 53 | im_alpha = _load(path) 54 | self.width = im_alpha.shape[1] 55 | self.height = im_alpha.shape[0] 56 | 57 | im_alpha = im_alpha.reshape((self.width * self.height * 4), order='C') 58 | self.set_pixels(4*self.width, 59 | self.width, self.height, 60 | im_alpha.tobytes()) 61 | except Exception as e: 62 | logger.warn("Unable to load background: %s - defaulting to 100x100 black", str(e)) 63 | 64 | self.width = 100 65 | self.height = 100 66 | self.set_primitive("rect", [], [0., 0., 0., 1.]) 67 | -------------------------------------------------------------------------------- /pywm/pywm_blur_widget.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from typing import TYPE_CHECKING, Optional, Any 3 | 4 | from .pywm_widget import PyWMWidget 5 | 6 | if TYPE_CHECKING: 7 | from .pywm import PyWM, ViewT, PyWMOutput 8 | 9 | class PyWMBlurWidget(PyWMWidget): 10 | def __init__(self, wm: PyWM[ViewT], output: Optional[PyWMOutput], *args: Any, **kwargs: Any) -> None: 11 | super().__init__(wm, output, *args, **kwargs) 12 | self.set_blur() 13 | 14 | def set_blur(self, radius: int=1, passes: int=2) -> None: 15 | self.set_composite("blur", [radius, passes], []) 16 | -------------------------------------------------------------------------------- /pywm/pywm_cairo_widget.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from typing import TYPE_CHECKING, Any 3 | 4 | import cairo 5 | import numpy as np 6 | from abc import abstractmethod 7 | 8 | from .pywm_widget import PyWMWidget 9 | 10 | if TYPE_CHECKING: 11 | from .pywm import PyWM, PyWMOutput, ViewT 12 | 13 | 14 | class PyWMCairoWidget(PyWMWidget): 15 | def __init__(self, wm: PyWM[ViewT], output: PyWMOutput, width: int, height: int, *args: Any, **kwargs: Any) -> None: 16 | super().__init__(wm, output, *args, **kwargs) 17 | 18 | self.width = max(1, width) 19 | self.height = max(1, height) 20 | 21 | def render(self) -> None: 22 | surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 23 | self.width, self.height) 24 | self._render(surface) 25 | buf = surface.get_data() 26 | data: np.ndarray = np.ndarray(shape=(self.width, self.height, 4), 27 | dtype=np.uint8, 28 | buffer=buf) 29 | data = data.reshape((self.width * self.height * 4), order='C') 30 | self.set_pixels(4*self.width, 31 | self.width, self.height, data.tobytes()) 32 | 33 | @abstractmethod 34 | def _render(self, surface: cairo.ImageSurface) -> None: 35 | pass 36 | -------------------------------------------------------------------------------- /pywm/pywm_widget.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from typing import TYPE_CHECKING, TypeVar, Optional, Generic 3 | 4 | from abc import abstractmethod 5 | 6 | from .damage_tracked import DamageTracked 7 | 8 | if TYPE_CHECKING: 9 | from .pywm import PyWM, PyWMOutput, ViewT 10 | PyWMT = TypeVar('PyWMT', bound=PyWM) 11 | else: 12 | PyWMT = TypeVar('PyWMT') 13 | 14 | 15 | class PyWMWidgetDownstreamState: 16 | def __init__(self, z_index: float=0, box: tuple[float, float, float, float]=(0, 0, 0, 0), mask: tuple[float, float, float, float]=(-1, -1, -1, -1), opacity: float=1., corner_radius: float=0, lock_enabled: bool=True, workspace: Optional[tuple[float, float, float, float]]=None, primitive: Optional[str]=None) -> None: 17 | self.z_index = float(z_index) 18 | self.box = (float(box[0]), float(box[1]), float(box[2]), float(box[3])) 19 | self.mask = (float(mask[0]), float(mask[1]), float(mask[2]), float(mask[3])) 20 | self.opacity = opacity 21 | self.corner_radius = corner_radius 22 | self.lock_enabled=lock_enabled 23 | self.workspace = workspace 24 | self.primitive = primitive 25 | 26 | def copy(self) -> PyWMWidgetDownstreamState: 27 | return PyWMWidgetDownstreamState(self.z_index, self.box, self.mask, self.opacity, self.corner_radius, self.lock_enabled, self.workspace) 28 | 29 | def get(self, root: PyWM[ViewT], output: Optional[PyWMOutput], pixels: Optional[tuple[int, int, int, bytes]], primitive: Optional[tuple[str, list[int], list[float]]]) -> tuple[bool, tuple[float, float, float, float], tuple[float, float, float, float], int, float, float, float, tuple[float, float, float, float], Optional[tuple[int, int, int, bytes]], Optional[tuple[str, list[int], list[float]]]]: 30 | return ( 31 | self.lock_enabled, 32 | root.round(*self.box, wh_logical=False), 33 | self.mask, 34 | output._key if output is not None else -1, 35 | self.opacity, 36 | self.corner_radius, 37 | self.z_index, 38 | root.round(*self.workspace, wh_logical=False) if self.workspace is not None else (0, 0, -1, -1), 39 | pixels, 40 | primitive 41 | ) 42 | 43 | class PyWMWidget(Generic[PyWMT], DamageTracked): 44 | def __init__(self, wm: PyWMT, output: Optional[PyWMOutput], override_parent: Optional[DamageTracked]=None) -> None: 45 | DamageTracked.__init__(self, wm if override_parent is None else override_parent) 46 | self._handle = -1 47 | self._is_composite = False 48 | 49 | self.wm = wm 50 | self.output = output 51 | 52 | self._down_state = PyWMWidgetDownstreamState(0, (0, 0, 0, 0)) 53 | 54 | """ 55 | (stride, width, height, data) 56 | """ 57 | self._pending_pixels: Optional[tuple[int, int, int, bytes]] = None 58 | 59 | self._pending_primitive: Optional[tuple[str, list[int], list[float]]] = None 60 | 61 | def _update(self) -> tuple[bool, tuple[float, float, float, float], tuple[float, float, float, float], int, float, float, float, tuple[float, float, float, float], Optional[tuple[int, int, int, bytes]], Optional[tuple[str, list[int], list[float]]]]: 62 | if self.is_damaged(): 63 | self._down_state = self.process() 64 | pixels = self._pending_pixels 65 | primitive = self._pending_primitive 66 | self._pending_pixels = None 67 | self._pending_primitive = None 68 | return self._down_state.get(self.wm, self.output, pixels, primitive) 69 | 70 | def destroy(self) -> None: 71 | self.wm.widget_destroy(self) 72 | 73 | def set_pixels(self, stride: int, width: int, height: int, data: bytes) -> None: 74 | self._pending_pixels = (stride, width, height, data) 75 | 76 | def set_primitive(self, name: str, params_int: list[int], params_float: list[float]) -> None: 77 | self._pending_primitive = name, params_int, params_float 78 | 79 | def set_composite(self, name: str, params_int: list[int], params_float: list[float]) -> None: 80 | self._is_composite = True 81 | self._pending_primitive = name, params_int, params_float 82 | 83 | @abstractmethod 84 | def process(self) -> PyWMWidgetDownstreamState: 85 | """ 86 | return next down_state based on whatever state the implementation uses 87 | """ 88 | pass 89 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | imageio 2 | numpy 3 | pycairo 4 | evdev 5 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import glob 3 | import shutil 4 | from setuptools import setup 5 | 6 | proc = subprocess.Popen(["meson", "build"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 7 | stdout, stderr = proc.communicate() 8 | if proc.returncode != 0: 9 | raise Exception("Fatal: Error executing 'meson build': \n%r\n%r" % (stdout, stderr)) 10 | 11 | 12 | proc1 = subprocess.Popen(["ninja", "-C", "build"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 13 | stdout, stderr = proc1.communicate() 14 | if proc1.returncode != 0: 15 | raise Exception("Fatal: Error executing 'ninja -C build': \n%r\n%r" % (stdout, stderr)) 16 | 17 | so = None 18 | for f in glob.glob('build/_pywm.*.so'): 19 | so = f 20 | 21 | if so is not None: 22 | try: 23 | shutil.copy(so, 'pywm/_pywm.so') 24 | except shutil.SameFileError: 25 | pass 26 | else: 27 | raise Exception("Fatal: Could not find shared library") 28 | 29 | setup(name='pywm', 30 | version='0.3', 31 | description='wlroots-based Wayland compositor with Python frontend', 32 | url="https://github.com/jbuchermn/pywm", 33 | author='Jonas Bucher', 34 | author_email='j.bucher.mn@gmail.com', 35 | package_data={'pywm': ['_pywm.so', 'py.typed']}, 36 | packages=['pywm'], 37 | install_requires=['evdev', 'imageio', 'pycairo', 'numpy']) 38 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "wm/wm_config.h" 8 | #include "wm/wm.h" 9 | 10 | int main(int argc, char** argv){ 11 | struct wm_config config; 12 | wm_config_init_default(&config); 13 | wm_init(&config); 14 | wm_run(); 15 | wm_destroy(); 16 | 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /src/py/_pywm_view.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "wm/wm.h" 7 | #include "wm/wm_view.h" 8 | #include "wm/wm_output.h" 9 | #ifdef WM_HAS_XWAYLAND 10 | #include "wm/wm_view_xwayland.h" 11 | #endif 12 | #include "wm/wm_util.h" 13 | 14 | #include "py/_pywm_view.h" 15 | #include "py/_pywm_callbacks.h" 16 | 17 | static struct _pywm_views views = { 0 }; 18 | 19 | void _pywm_view_init(struct _pywm_view* _view, struct wm_view* view){ 20 | static long handle = 0; 21 | handle++; 22 | 23 | _view->handle = handle; 24 | _view->view = view; 25 | _view->next_view = NULL; 26 | 27 | _view->update_cnt = 0; 28 | } 29 | 30 | 31 | void _pywm_view_update(struct _pywm_view* view){ 32 | 33 | /* General info */ 34 | PyObject* args_general = Py_None; 35 | if(++view->update_cnt % 20 == 1){ 36 | long parent_handle = 0; 37 | struct wm_view* parent = wm_view_get_parent(view->view); 38 | if(parent){ 39 | parent_handle = _pywm_views_get_handle(parent); 40 | } 41 | 42 | pid_t pid; 43 | uid_t uid; 44 | gid_t gid; 45 | wm_view_get_credentials(view->view, &pid, &uid, &gid); 46 | 47 | const char* title; 48 | const char* app_id; 49 | const char* role; 50 | wm_view_get_info(view->view, &title, &app_id, &role); 51 | 52 | /* If title is not yet there - resend */ 53 | if(!title || !strcmp(title, "")){ 54 | view->update_cnt--; 55 | } 56 | 57 | #ifdef WM_HAS_XWAYLAND 58 | bool xwayland = wm_view_is_xwayland(view->view); 59 | #else 60 | bool xwayland = false; 61 | #endif 62 | 63 | args_general = Py_BuildValue( 64 | "(lOisss)", 65 | parent_handle, 66 | xwayland ? Py_True : Py_False, 67 | pid, 68 | app_id, 69 | role, 70 | title); 71 | } 72 | 73 | /* Current info */ 74 | int width, height; 75 | wm_view_get_size(view->view, &width, &height); 76 | 77 | int is_mapped = view->view->mapped; 78 | 79 | int is_floating = wm_view_is_floating(view->view); 80 | int is_focused = wm_view_is_focused(view->view); 81 | int is_fullscreen = wm_view_is_fullscreen(view->view); 82 | int is_maximized = wm_view_is_maximized(view->view); 83 | int is_resizing = wm_view_is_resizing(view->view); 84 | 85 | int is_inhibiting_idle = wm_view_is_inhibiting_idle(view->view); 86 | 87 | struct wm_output* fixed_output = wm_content_get_output(&view->view->super); 88 | int fixed_output_key = fixed_output ? fixed_output->key : -1; 89 | 90 | int* size_constraints; 91 | int n_constraints; 92 | wm_view_get_size_constraints(view->view, &size_constraints, &n_constraints); 93 | PyObject* args_size_constraints = PyList_New(n_constraints); 94 | for (int i=0; iview, &offset_x, &offset_y); 101 | 102 | bool shows_csd = wm_view_shows_csd(view->view); 103 | 104 | PyObject* args = Py_BuildValue( 105 | "(lOiiOOOOOOOOiiOi)", 106 | 107 | view->handle, 108 | args_general, 109 | 110 | width, 111 | height, 112 | 113 | is_mapped ? Py_True : Py_False, 114 | is_floating ? Py_True : Py_False, 115 | is_focused ? Py_True : Py_False, 116 | is_fullscreen ? Py_True : Py_False, 117 | is_maximized ? Py_True : Py_False, 118 | is_resizing ? Py_True : Py_False, 119 | is_inhibiting_idle ? Py_True : Py_False, 120 | 121 | args_size_constraints, 122 | 123 | offset_x, 124 | offset_y, 125 | shows_csd ? Py_True : Py_False, 126 | fixed_output_key); 127 | 128 | 129 | 130 | PyObject* res = PyObject_Call(_pywm_callbacks_get_all()->update_view, args, NULL); 131 | if(args_general != Py_None) 132 | Py_XDECREF(args_general); 133 | Py_XDECREF(args_size_constraints); 134 | Py_XDECREF(args); 135 | 136 | if(res && res != Py_None){ 137 | double x, y, w, h; 138 | double opacity; 139 | double mask_x, mask_y, mask_w, mask_h; 140 | double corner_radius; 141 | int floating, focus_pending, resizing_pending, fullscreen_pending, maximized_pending, close_pending; 142 | int width_pending, height_pending; 143 | int accepts_input; 144 | double z_index; 145 | int lock_enabled; 146 | int new_fixed_output_key; 147 | double workspace_x, workspace_y, workspace_w, workspace_h; 148 | 149 | if(!PyArg_ParseTuple(res, 150 | "(dddd)(dddd)dddppi(ii)iiiiii(dddd)", 151 | &x, &y, &w, &h, 152 | &mask_x, &mask_y, &mask_w, &mask_h, 153 | &opacity, 154 | &corner_radius, 155 | 156 | &z_index, 157 | &accepts_input, 158 | &lock_enabled, 159 | &floating, 160 | 161 | &width_pending, &height_pending, 162 | &focus_pending, 163 | &fullscreen_pending, 164 | &maximized_pending, 165 | &resizing_pending, 166 | &close_pending, 167 | &new_fixed_output_key, 168 | &workspace_x, &workspace_y, &workspace_w, &workspace_h 169 | )){ 170 | fprintf(stderr, "Error parsing update view return...\n"); 171 | PyErr_SetString(PyExc_TypeError, "Cannot parse update_view return"); 172 | }else{ 173 | 174 | wm_content_set_opacity(&view->view->super, opacity); 175 | wm_content_set_mask(&view->view->super, mask_x, mask_y, mask_w, mask_h); 176 | wm_content_set_corner_radius(&view->view->super, corner_radius); 177 | if(floating >= 0) 178 | wm_view_set_floating(view->view, floating); 179 | wm_content_set_box(&view->view->super, x, y, w, h); 180 | 181 | /* Set output before triggering configure in request_size */ 182 | if(new_fixed_output_key != fixed_output_key) 183 | wm_content_set_output(&view->view->super, new_fixed_output_key, NULL); 184 | 185 | if(width_pending > 0 && height_pending > 0) 186 | wm_view_request_size(view->view, width_pending, height_pending); 187 | 188 | if(focus_pending != -1 && focus_pending) 189 | wm_focus_view(view->view); 190 | if(resizing_pending != -1) 191 | wm_view_set_resizing(view->view, resizing_pending); 192 | if(fullscreen_pending != -1) 193 | wm_view_set_fullscreen(view->view, fullscreen_pending); 194 | if(maximized_pending != -1) 195 | wm_view_set_maximized(view->view, maximized_pending); 196 | if(close_pending != -1 && close_pending) 197 | wm_view_request_close(view->view); 198 | wm_content_set_z_index(&view->view->super, z_index); 199 | wm_content_set_lock_enabled(&view->view->super, lock_enabled); 200 | 201 | view->view->accepts_input = accepts_input; 202 | wm_content_set_workspace(&view->view->super, workspace_x, workspace_y, workspace_w, workspace_h); 203 | } 204 | 205 | } 206 | 207 | Py_XDECREF(res); 208 | } 209 | 210 | long _pywm_views_add(struct wm_view* view){ 211 | struct _pywm_view* it; 212 | for(it = views.first_view; it && it->next_view; it=it->next_view); 213 | struct _pywm_view** insert; 214 | if(it){ 215 | insert = &it->next_view; 216 | }else{ 217 | insert = &views.first_view; 218 | } 219 | 220 | *insert = malloc(sizeof(struct _pywm_view)); 221 | _pywm_view_init(*insert, view); 222 | return (*insert)->handle; 223 | } 224 | 225 | long _pywm_views_remove(struct wm_view* view){ 226 | struct _pywm_view* remove = NULL; 227 | 228 | if(views.first_view && views.first_view->view == view){ 229 | remove = views.first_view; 230 | views.first_view = remove->next_view; 231 | }else{ 232 | struct _pywm_view* prev; 233 | for(prev = views.first_view; prev && prev->next_view && prev->next_view->view != view; prev=prev->next_view); 234 | 235 | if(prev && prev->next_view){ 236 | remove = prev->next_view; 237 | prev->next_view = remove->next_view; 238 | } 239 | } 240 | 241 | if(remove){ 242 | long handle = remove->handle; 243 | free(remove); 244 | 245 | return handle; 246 | } 247 | 248 | return 0; 249 | 250 | } 251 | 252 | long _pywm_views_get_handle(struct wm_view* view){ 253 | for(struct _pywm_view* it = views.first_view; it; it=it->next_view){ 254 | if(it->view == view) return it->handle; 255 | } 256 | 257 | return 0; 258 | } 259 | 260 | void _pywm_views_update(){ 261 | for(struct _pywm_view* view=views.first_view; view; view=view->next_view){ 262 | TIMER_START(callback_update_views_single); 263 | _pywm_view_update(view); 264 | TIMER_STOP(callback_update_views_single); 265 | TIMER_PRINT(callback_update_views_single); 266 | } 267 | } 268 | 269 | void _pywm_views_update_single(struct wm_view* view){ 270 | for(struct _pywm_view* v=views.first_view; v; v=v->next_view){ 271 | if(v->view == view){ 272 | _pywm_view_update(v); 273 | break; 274 | } 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /src/py/_pywm_widget.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "wm/wm.h" 8 | #include "wm/wm_widget.h" 9 | #include "wm/wm_composite.h" 10 | #include "py/_pywm_widget.h" 11 | #include "py/_pywm_callbacks.h" 12 | #include "wm/wm_util.h" 13 | 14 | static struct _pywm_widgets widgets = { 0 }; 15 | static long next_handle = 1; 16 | 17 | void _pywm_widget_init(struct _pywm_widget* _widget, struct wm_widget* widget, struct wm_composite* composite){ 18 | _widget->handle = (next_handle++); 19 | _widget->widget = widget; 20 | _widget->composite = composite; 21 | 22 | assert((_widget->widget || _widget->composite) && !(_widget->widget && _widget->composite)); 23 | _widget->super = _widget->widget ? &_widget->widget->super : &_widget->composite->super; 24 | 25 | _widget->next_widget = NULL; 26 | } 27 | 28 | void _pywm_widget_update(struct _pywm_widget* widget){ 29 | PyObject* args = Py_BuildValue("(l)", widget->handle); 30 | PyObject* res = PyObject_Call(_pywm_callbacks_get_all()->update_widget, args, NULL); 31 | Py_XDECREF(args); 32 | if(res && res != Py_None){ 33 | double x, y, w, h; 34 | double mask_x, mask_y, mask_w, mask_h; 35 | int output_key; 36 | double opacity; 37 | double corner_radius; 38 | double z_index; 39 | int lock_enabled; 40 | double workspace_x, workspace_y, workspace_w, workspace_h; 41 | PyObject* pixels; 42 | PyObject* primitive; 43 | if(!PyArg_ParseTuple(res, 44 | "p(dddd)(dddd)iddd(dddd)OO", 45 | &lock_enabled, 46 | &x, &y, &w, &h, 47 | &mask_x, &mask_y, &mask_w, &mask_h, 48 | &output_key, 49 | &opacity, 50 | &corner_radius, 51 | &z_index, 52 | &workspace_x, &workspace_y, &workspace_w, &workspace_h, &pixels, &primitive 53 | )){ 54 | PyErr_SetString(PyExc_TypeError, "Cannot parse update_widget return"); 55 | return; 56 | } 57 | 58 | wm_content_set_opacity(widget->super, opacity); 59 | wm_content_set_corner_radius(widget->super, corner_radius); 60 | if(w >= 0.0 && h >= 0.0) 61 | wm_content_set_box(widget->super, x, y, w, h); 62 | wm_content_set_mask(widget->super, mask_x, mask_y, mask_w, mask_h); 63 | wm_content_set_z_index(widget->super, z_index); 64 | wm_content_set_lock_enabled(widget->super, lock_enabled); 65 | 66 | wm_content_set_output(widget->super, output_key, NULL); 67 | wm_content_set_workspace(widget->super, workspace_x, workspace_y, workspace_w, workspace_h); 68 | 69 | if(pixels && pixels != Py_None && widget->widget){ 70 | int stride, width, height; 71 | PyObject* data; 72 | if(!PyArg_ParseTuple(pixels, "iiiS", &stride, &width, &height, &data)){ 73 | PyErr_SetString(PyExc_TypeError, "Cannot parse pixels"); 74 | return; 75 | } 76 | 77 | wm_widget_set_pixels(widget->widget, 78 | DRM_FORMAT_ARGB8888, 79 | stride, 80 | width, 81 | height, 82 | PyBytes_AsString(data)); 83 | } 84 | 85 | if(primitive && primitive != Py_None){ 86 | char* name; 87 | PyObject* params_int; 88 | PyObject* params_float; 89 | if(!PyArg_ParseTuple(primitive, "sOO", &name, ¶ms_int, ¶ms_float)){ 90 | PyErr_SetString(PyExc_TypeError, "Cannot parse primitive"); 91 | return; 92 | } 93 | 94 | if(!params_int || !params_float || !PyList_Check(params_int) || !PyList_Check(params_float)){ 95 | PyErr_SetString(PyExc_TypeError, "Cannot parse primitive lists"); 96 | return; 97 | } 98 | 99 | int* p_int = malloc(PyList_Size(params_int) * sizeof(int)); 100 | float* p_float = malloc(PyList_Size(params_float) * sizeof(int)); 101 | 102 | for(int i=0; iwidget){ 110 | wm_widget_set_primitive(widget->widget, strdup(name), PyList_Size(params_int), p_int, PyList_Size(params_float), p_float); 111 | }else{ 112 | wm_composite_set_type(widget->composite, name, PyList_Size(params_int), p_int, PyList_Size(params_float), p_float); 113 | } 114 | 115 | } 116 | 117 | } 118 | 119 | Py_XDECREF(res); 120 | } 121 | 122 | long _pywm_widgets_add(struct wm_widget* widget, struct wm_composite* composite){ 123 | struct _pywm_widget* it; 124 | for(it = widgets.first_widget; it && it->next_widget; it=it->next_widget); 125 | struct _pywm_widget** insert; 126 | if(it){ 127 | insert = &it->next_widget; 128 | }else{ 129 | insert = &widgets.first_widget; 130 | } 131 | 132 | *insert = malloc(sizeof(struct _pywm_widget)); 133 | _pywm_widget_init(*insert, widget, composite); 134 | return (*insert)->handle; 135 | } 136 | 137 | long _pywm_widgets_remove(struct wm_content* content){ 138 | struct _pywm_widget* remove; 139 | if(widgets.first_widget && widgets.first_widget->super == content){ 140 | remove = widgets.first_widget; 141 | widgets.first_widget = remove->next_widget; 142 | }else{ 143 | struct _pywm_widget* prev; 144 | for(prev = widgets.first_widget; prev && prev->next_widget && prev->next_widget->super != content; prev=prev->next_widget); 145 | assert(prev); 146 | 147 | remove = prev->next_widget; 148 | prev->next_widget = remove->next_widget; 149 | } 150 | 151 | assert(remove); 152 | long handle = remove->handle; 153 | free(remove); 154 | 155 | return handle; 156 | } 157 | 158 | long _pywm_widgets_get_handle(struct wm_content* content){ 159 | for(struct _pywm_widget* it = widgets.first_widget; it; it=it->next_widget){ 160 | if(it->super == content) return it->handle; 161 | } 162 | 163 | return 0; 164 | } 165 | 166 | 167 | void _pywm_widgets_update(){ 168 | /* Query for a widget to destroy */ 169 | PyObject* args = Py_BuildValue("()"); 170 | PyObject* res = PyObject_Call(_pywm_callbacks_get_all()->query_destroy_widget, args, NULL); 171 | Py_XDECREF(args); 172 | if(res && res != Py_None){ 173 | long handle = PyLong_AsLong(res); 174 | if(handle < 0){ 175 | PyErr_SetString(PyExc_TypeError, "Expected long"); 176 | goto err; 177 | } 178 | 179 | struct wm_content* content = _pywm_widgets_from_handle(handle); 180 | if(!content){ 181 | PyErr_SetString(PyExc_TypeError, "Widget has been destroyed"); 182 | goto err; 183 | } 184 | _pywm_widgets_remove(content); 185 | wm_content_destroy(content); 186 | } 187 | Py_XDECREF(res); 188 | 189 | /* Query for a new widget */ 190 | args = Py_BuildValue("(l)", next_handle); 191 | res = PyObject_Call(_pywm_callbacks_get_all()->query_new_widget, args, NULL); 192 | Py_XDECREF(args); 193 | if(res && res != Py_None){ 194 | long r = PyLong_AsLong(res); 195 | if(r == 1){ 196 | struct wm_widget* widget = calloc(1, sizeof(struct wm_widget)); 197 | wm_widget_init(widget, get_wm()->server); 198 | _pywm_widgets_add(widget, NULL); 199 | }else if(r == 2){ 200 | struct wm_composite* composite = calloc(1, sizeof(struct wm_composite)); 201 | wm_composite_init(composite, get_wm()->server); 202 | _pywm_widgets_add(NULL, composite); 203 | } 204 | } 205 | Py_XDECREF(res); 206 | 207 | /* Update existing widgets */ 208 | for(struct _pywm_widget* widget = widgets.first_widget; widget; widget=widget->next_widget){ 209 | TIMER_START(callback_update_widgets_single); 210 | _pywm_widget_update(widget); 211 | TIMER_STOP(callback_update_widgets_single); 212 | TIMER_PRINT(callback_update_widgets_single); 213 | } 214 | 215 | err: 216 | ; 217 | } 218 | 219 | 220 | struct _pywm_widget* _pywm_widgets_container_from_handle(long handle){ 221 | 222 | for(struct _pywm_widget* widget = widgets.first_widget; widget; widget=widget->next_widget){ 223 | if(widget->handle == handle) return widget; 224 | } 225 | 226 | return NULL; 227 | } 228 | 229 | struct wm_content* _pywm_widgets_from_handle(long handle){ 230 | struct _pywm_widget* widget = _pywm_widgets_container_from_handle(handle); 231 | if(!widget){ 232 | return NULL; 233 | } 234 | 235 | return widget->super; 236 | } 237 | 238 | -------------------------------------------------------------------------------- /src/py/_pywmmodule.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "wm/wm.h" 9 | #include "wm/wm_config.h" 10 | #include "wm/wm_server.h" 11 | #include "wm/wm_layout.h" 12 | #include "wm/wm_util.h" 13 | #include "py/_pywm_callbacks.h" 14 | #include "py/_pywm_view.h" 15 | #include "py/_pywm_widget.h" 16 | 17 | static void sig_handler(int sig) { 18 | void *array[10]; 19 | size_t size; 20 | 21 | size = backtrace(array, 100); 22 | fprintf(stderr, "Error: signal %d:\n", sig); 23 | backtrace_symbols_fd(array, size, STDERR_FILENO); 24 | exit(1); 25 | } 26 | 27 | static void set_config(struct wm_config* conf, PyObject* dict, int reconfigure){ 28 | if(reconfigure){ 29 | wm_config_reset_default(conf); 30 | } 31 | 32 | PyObject* o; 33 | o = PyDict_GetItemString(dict, "outputs"); 34 | if(o && PyList_Check(o)){ 35 | PyObject* l = o; 36 | for(int i=0; ixkb_model, PyBytes_AsString(o), WM_CONFIG_STRLEN-1); } 60 | o = PyDict_GetItemString(dict, "xkb_layout"); if(o){ strncpy(conf->xkb_layout, PyBytes_AsString(o), WM_CONFIG_STRLEN-1); } 61 | o = PyDict_GetItemString(dict, "xkb_variant"); if(o){ strncpy(conf->xkb_variant, PyBytes_AsString(o), WM_CONFIG_STRLEN-1); } 62 | o = PyDict_GetItemString(dict, "xkb_options"); if(o){ strncpy(conf->xkb_options, PyBytes_AsString(o), WM_CONFIG_STRLEN-1); } 63 | 64 | o = PyDict_GetItemString(dict, "texture_shaders"); if(o){ strncpy(conf->texture_shaders, PyBytes_AsString(o), WM_CONFIG_STRLEN-1); } 65 | o = PyDict_GetItemString(dict, "renderer_mode"); if(o){ strncpy(conf->renderer_mode, PyBytes_AsString(o), WM_CONFIG_STRLEN-1); } 66 | 67 | o = PyDict_GetItemString(dict, "xcursor_theme"); if(o){ wm_config_set_xcursor_theme(conf, PyBytes_AsString(o)); } 68 | o = PyDict_GetItemString(dict, "xcursor_size"); if(o){ wm_config_set_xcursor_size(conf, PyLong_AsLong(o)); } 69 | 70 | o = PyDict_GetItemString(dict, "focus_follows_mouse"); if(o){ conf->focus_follows_mouse = o == Py_True; } 71 | o = PyDict_GetItemString(dict, "constrain_popups_to_toplevel"); if(o){ conf->constrain_popups_to_toplevel = o == Py_True; } 72 | o = PyDict_GetItemString(dict, "encourage_csd"); if(o){ conf->encourage_csd = o == Py_True; } 73 | 74 | o = PyDict_GetItemString(dict, "tap_to_click"); if(o){ conf->tap_to_click = o == Py_True; } 75 | o = PyDict_GetItemString(dict, "natural_scroll"); if(o){ conf->natural_scroll = o == Py_True; } 76 | 77 | o = PyDict_GetItemString(dict, "enable_xwayland"); if(o){ conf->enable_xwayland = o == Py_True; } 78 | o = PyDict_GetItemString(dict, "debug"); if(o){ conf->debug = o == Py_True; } 79 | 80 | if(reconfigure){ 81 | wlr_log(WLR_DEBUG, "Reconfiguring PyWM..."); 82 | wm_config_reconfigure(conf, get_wm()->server); 83 | wlr_log(WLR_DEBUG, "...done"); 84 | } 85 | } 86 | 87 | 88 | static void handle_update_view(struct wm_view* view){ 89 | PyGILState_STATE gil = PyGILState_Ensure(); 90 | _pywm_views_update_single(view); 91 | /* Decoration widgets might depend on the view state */ 92 | _pywm_widgets_update(); 93 | PyGILState_Release(gil); 94 | } 95 | 96 | static void handle_update(){ 97 | PyGILState_STATE gil = PyGILState_Ensure(); 98 | 99 | TIMER_START(callback_update_pywm); 100 | PyObject* args = Py_BuildValue("()"); 101 | PyObject* res = PyObject_Call(_pywm_callbacks_get_all()->update, args, NULL); 102 | Py_XDECREF(args); 103 | 104 | int update_cursor; 105 | int update_cursor_x; 106 | int update_cursor_y; 107 | double lock_perc; 108 | int terminate; 109 | const char* open_virtual_output, *close_virtual_output; 110 | PyObject* config; 111 | 112 | if(!PyArg_ParseTuple(res, 113 | "iiidsspO", 114 | &update_cursor, 115 | &update_cursor_x, 116 | &update_cursor_y, 117 | &lock_perc, 118 | &open_virtual_output, 119 | &close_virtual_output, 120 | &terminate, 121 | &config)){ 122 | PyErr_SetString(PyExc_TypeError, "Cannot parse query return"); 123 | }else{ 124 | if(update_cursor >= 0){ 125 | wm_update_cursor(update_cursor, update_cursor_x, update_cursor_y); 126 | } 127 | wm_set_locked(lock_perc); 128 | if(terminate){ 129 | wm_terminate(); 130 | } 131 | 132 | if(strlen(open_virtual_output) > 0){ 133 | wm_open_virtual_output(open_virtual_output); 134 | } 135 | if(strlen(close_virtual_output) > 0){ 136 | wm_close_virtual_output(close_virtual_output); 137 | } 138 | if(config && config != Py_None){ 139 | set_config(get_wm()->server->wm_config, config, 1); 140 | } 141 | } 142 | Py_XDECREF(res); 143 | 144 | TIMER_STOP(callback_update_pywm); 145 | TIMER_PRINT(callback_update_pywm); 146 | 147 | TIMER_START(callback_update_views); 148 | _pywm_views_update(); 149 | TIMER_STOP(callback_update_views); 150 | TIMER_PRINT(callback_update_views); 151 | 152 | /* State of widgets (e.g. decorations) might depend on views - other way round not possible, as widgets have no upstream state */ 153 | TIMER_START(callback_update_widgets); 154 | _pywm_widgets_update(); 155 | TIMER_STOP(callback_update_widgets); 156 | TIMER_PRINT(callback_update_widgets); 157 | 158 | 159 | PyGILState_Release(gil); 160 | } 161 | 162 | 163 | 164 | static PyObject* _pywm_run(PyObject* self, PyObject* args, PyObject* kwargs){ 165 | /* Dubug: Print stacktrace upon segfault etc. */ 166 | signal(SIGSEGV, sig_handler); 167 | signal(SIGINT, sig_handler); 168 | signal(SIGTERM, sig_handler); 169 | signal(SIGHUP, sig_handler); 170 | 171 | wlr_log(WLR_INFO, "Running PyWM...\n"); 172 | 173 | int status = 0; 174 | 175 | struct wm_config conf; 176 | wm_config_init_default(&conf); 177 | 178 | if(kwargs){ 179 | set_config(&conf, kwargs, 0); 180 | } 181 | 182 | /* Register callbacks immediately, might be called during init */ 183 | get_wm()->callback_update = handle_update; 184 | get_wm()->callback_update_view = handle_update_view; 185 | _pywm_callbacks_init(); 186 | 187 | wm_init(&conf); 188 | 189 | Py_BEGIN_ALLOW_THREADS; 190 | status = wm_run(); 191 | Py_END_ALLOW_THREADS; 192 | 193 | wlr_log(WLR_INFO, "...finished\n"); 194 | 195 | return Py_BuildValue("i", status); 196 | } 197 | 198 | static PyObject* _pywm_register(PyObject* self, PyObject* args){ 199 | const char* name; 200 | PyObject* callback; 201 | 202 | if(!PyArg_ParseTuple(args, "sO", &name, &callback)){ 203 | PyErr_SetString(PyExc_TypeError, "Invalid parameters"); 204 | return NULL; 205 | } 206 | 207 | if(!PyCallable_Check(callback)){ 208 | PyErr_SetString(PyExc_TypeError, "Object is not callable"); 209 | return NULL; 210 | } 211 | 212 | PyObject** target = _pywm_callbacks_get(name); 213 | if(!target){ 214 | PyErr_SetString(PyExc_TypeError, "Unknown callback"); 215 | return NULL; 216 | } 217 | 218 | Py_XDECREF(*target); 219 | *target = callback; 220 | Py_INCREF(*target); 221 | 222 | Py_INCREF(Py_None); 223 | return Py_None; 224 | } 225 | 226 | static PyObject* _pywm_damage(PyObject* self, PyObject* args){ 227 | int code; 228 | 229 | if(!PyArg_ParseTuple(args, "i", &code)){ 230 | PyErr_SetString(PyExc_TypeError, "Invalid parameters"); 231 | return NULL; 232 | } 233 | 234 | wm_server_set_constant_damage_mode(get_wm()->server, code); 235 | 236 | Py_INCREF(Py_None); 237 | return Py_None; 238 | } 239 | 240 | static PyObject* _pywm_debugperformance(PyObject* self, PyObject* args){ 241 | const char* key; 242 | 243 | if(!PyArg_ParseTuple(args, "s", &key)){ 244 | PyErr_SetString(PyExc_TypeError, "Invalid parameters"); 245 | return NULL; 246 | } 247 | 248 | DEBUG_PERFORMANCE_PTR(key, 0); 249 | 250 | Py_INCREF(Py_None); 251 | return Py_None; 252 | } 253 | 254 | 255 | static PyMethodDef _pywm_methods[] = { 256 | { "run", (PyCFunction)_pywm_run, METH_VARARGS | METH_KEYWORDS, "Start the compositor in this thread" }, 257 | { "register", _pywm_register, METH_VARARGS, "Register callback" }, 258 | { "damage", _pywm_damage, METH_VARARGS, "Track damage, or set mode to continuous damage" }, 259 | { "debug_performance", _pywm_debugperformance, METH_VARARGS, "Debug uitlity - uses DEBUG_PERFORMANCE macro" }, 260 | 261 | { NULL, NULL, 0, NULL } 262 | }; 263 | 264 | static struct PyModuleDef _pywm = { 265 | PyModuleDef_HEAD_INIT, 266 | "_pywm", 267 | "", 268 | -1, 269 | _pywm_methods, 270 | NULL, 271 | NULL, 272 | NULL, 273 | NULL 274 | }; 275 | 276 | PyMODINIT_FUNC PyInit__pywm(void){ 277 | return PyModule_Create(&_pywm); 278 | } 279 | -------------------------------------------------------------------------------- /src/wm/shaders/.gitignore: -------------------------------------------------------------------------------- 1 | texture_shaders.c 2 | -------------------------------------------------------------------------------- /src/wm/shaders/generate_primitive_shaders.py: -------------------------------------------------------------------------------- 1 | import re 2 | import os 3 | import sys 4 | 5 | base = os.path.dirname(os.path.realpath(__file__)) 6 | base_textures = os.path.join(base, 'primitive') 7 | 8 | texture_files = [ 9 | 'vertex.glsl', 10 | 'fragment.glsl', 11 | ] 12 | 13 | regexp_int = re.compile(r'.*int params_int\[(\d*)\]'); 14 | regexp_float = re.compile(r'.*float params_float\[(\d*)\]'); 15 | 16 | with open(sys.argv[1], "w") as out: 17 | out.write(""" 18 | #define _POSIX_C_SOURCE 200809L 19 | #include "wm/wm_renderer.h" 20 | 21 | void wm_primitive_shaders_init(struct wm_renderer* renderer){ 22 | 23 | """) 24 | 25 | skip = True 26 | shaders: list[str] = [] 27 | for subdir, _, files in os.walk(base_textures): 28 | if skip: 29 | skip = False 30 | continue 31 | 32 | n_params_int = 0 33 | n_params_float = 0 34 | strs = [] 35 | successful = True 36 | for f in texture_files: 37 | if f not in files: 38 | print("Could not find %s shader in %s" % (f, subdir)) 39 | successful = False 40 | continue 41 | 42 | with open(os.path.join(subdir, f), 'r') as file: 43 | strs += ["\"" + file.read().replace("\n", "\\n\"\n\"") + "\""] 44 | 45 | if not successful: 46 | continue 47 | 48 | match_int = regexp_int.match("".join(strs).replace("\"", "").replace("\n", "")) 49 | if match_int is not None: 50 | n_params_int = int(match_int.groups()[0]) 51 | 52 | match_float = regexp_float.match("".join(strs).replace("\"", "").replace("\n", "")) 53 | if match_float is not None: 54 | n_params_float = int(match_float.groups()[0]) 55 | 56 | print("[DEBUG] Texture has %d, %d parameters" % (n_params_int, n_params_float)) 57 | 58 | shaders += [f""" 59 | wm_renderer_add_primitive_shader(renderer, "{os.path.split(subdir)[1]}", {",".join(strs)}, {n_params_int}, {n_params_float}); 60 | """] 61 | 62 | out.write(f""" 63 | wm_renderer_init_primitive_shaders(renderer, {len(shaders)}); 64 | """) 65 | 66 | for s in shaders: 67 | out.write(s) 68 | 69 | out.write("}"); 70 | 71 | -------------------------------------------------------------------------------- /src/wm/shaders/generate_quad_shaders.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | base = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'quad') 5 | 6 | files = [ 7 | 'vertex.glsl', 8 | 'fragment.glsl', 9 | 'fragment_downsample.glsl', 10 | 'fragment_upsample.glsl', 11 | ] 12 | 13 | with open(sys.argv[1], "w") as out: 14 | out.write("#define _POSIX_C_SOURCE 200809L\n") 15 | 16 | 17 | for f in files: 18 | print("Generating %s" % f) 19 | src = "" 20 | with open(os.path.join(base, f), 'r') as inp: 21 | src = "\"" + inp.read().replace("\n", "\\n\"\n\"") + "\"" 22 | out.write("static const char %s[] = %s;\n" % ("quad_" + f.replace('.glsl', '_src'), src)) 23 | 24 | -------------------------------------------------------------------------------- /src/wm/shaders/generate_texture_shaders.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | base = os.path.dirname(os.path.realpath(__file__)) 5 | base_textures = os.path.join(base, 'texture') 6 | 7 | texture_files = [ 8 | 'vertex.glsl', 9 | 'fragment_rgba.glsl', 10 | 'fragment_rgbx.glsl', 11 | 'fragment_ext.glsl', 12 | ] 13 | 14 | with open(sys.argv[1], "w") as out: 15 | out.write(""" 16 | #define _POSIX_C_SOURCE 200809L 17 | #include "wm/wm_renderer.h" 18 | 19 | void wm_texture_shaders_init(struct wm_renderer* renderer){ 20 | 21 | """) 22 | 23 | skip = True 24 | shaders: list[str] = [] 25 | for subdir, _, files in os.walk(base_textures): 26 | if skip: 27 | skip = False 28 | continue 29 | 30 | strs = [] 31 | successful = True 32 | for f in texture_files: 33 | if f not in files: 34 | print("Could not find %s shader in %s" % (f, subdir)) 35 | successful = False 36 | continue 37 | 38 | with open(os.path.join(subdir, f), 'r') as file: 39 | strs += ["\"" + file.read().replace("\n", "\\n\"\n\"") + "\""] 40 | 41 | if not successful: 42 | continue 43 | 44 | shaders += [f""" 45 | wm_renderer_add_texture_shaders(renderer, "{os.path.split(subdir)[1]}", {",".join(strs)}); 46 | """] 47 | 48 | out.write(f""" 49 | wm_renderer_init_texture_shaders(renderer, {len(shaders)}); 50 | """) 51 | 52 | for s in shaders: 53 | out.write(s) 54 | 55 | out.write("}"); 56 | 57 | -------------------------------------------------------------------------------- /src/wm/shaders/meson.build: -------------------------------------------------------------------------------- 1 | prog_python = import('python').find_installation('python3') 2 | 3 | texture_shaders = [ 4 | 'noeffect', 5 | 'basic', 6 | 'fancy' 7 | ] 8 | 9 | primitive_shaders = [ 10 | 'rect', 11 | 'rounded_corners_rect', 12 | 'rounded_corners_border', 13 | 'corner' 14 | ] 15 | 16 | quad_shader_files = [ 17 | 'quad/vertex.glsl', 18 | 'quad/fragment.glsl', 19 | 'quad/fragment_downsample.glsl', 20 | 'quad/fragment_upsample.glsl', 21 | ] 22 | 23 | texture_shader_files = [] 24 | foreach s : texture_shaders 25 | texture_shader_files += [ 26 | './texture/' + s + '/vertex.glsl', 27 | './texture/' + s + '/fragment_rgba.glsl', 28 | './texture/' + s + '/fragment_rgbx.glsl', 29 | './texture/' + s + '/fragment_ext.glsl', 30 | ] 31 | endforeach 32 | 33 | primitive_shader_files = [] 34 | foreach s : primitive_shaders 35 | primitive_shader_files += [ 36 | './primitive/' + s + '/vertex.glsl', 37 | './primitive/' + s + '/fragment.glsl', 38 | ] 39 | endforeach 40 | 41 | texture_shaders_c = custom_target( 42 | 'texture_shaders.c', 43 | output : 'texture_shaders.c', 44 | input : ['generate_texture_shaders.py'] + texture_shader_files, 45 | command : [prog_python, '@INPUT0@', '@OUTPUT@'] 46 | ) 47 | 48 | primitive_shaders_c = custom_target( 49 | 'primitive_shaders.c', 50 | output : 'primitive_shaders.c', 51 | input : ['generate_primitive_shaders.py'] + primitive_shader_files, 52 | command : [prog_python, '@INPUT0@', '@OUTPUT@'] 53 | ) 54 | 55 | quad_shaders_c = custom_target( 56 | 'quad_shaders.c', 57 | output : 'quad_shaders.c', 58 | input : ['generate_quad_shaders.py'] + quad_shader_files, 59 | command : [prog_python, '@INPUT0@', '@OUTPUT@'] 60 | ) 61 | -------------------------------------------------------------------------------- /src/wm/shaders/primitive/corner/fragment.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | uniform float params_float[4]; 3 | uniform int params_int[1]; 4 | uniform float alpha; 5 | varying vec2 v_texcoord; 6 | 7 | 8 | uniform float width; 9 | uniform float height; 10 | 11 | void main() { 12 | if(params_int[0] == 0){ 13 | if(length(vec2(v_texcoord.x*width, v_texcoord.y*height) - vec2(0, 0)) < 14 | params_float[0]) discard; 15 | }else if(params_int[0] == 1){ 16 | if(length(vec2(v_texcoord.x*width, v_texcoord.y*height) - vec2(width, 0)) < 17 | params_float[0]) discard; 18 | }else if(params_int[0] == 2){ 19 | if(length(vec2(v_texcoord.x*width, v_texcoord.y*height) - vec2(0, height)) < 20 | params_float[0]) discard; 21 | }else if(params_int[0] == 3){ 22 | if(length(vec2(v_texcoord.x*width, v_texcoord.y*height) - vec2(width, height)) < 23 | params_float[0]) discard; 24 | } 25 | gl_FragColor = vec4(params_float[1], params_float[2], params_float[3], 1.) * alpha; 26 | } 27 | 28 | -------------------------------------------------------------------------------- /src/wm/shaders/primitive/corner/vertex.glsl: -------------------------------------------------------------------------------- 1 | uniform mat3 proj; 2 | attribute vec2 pos; 3 | attribute vec2 texcoord; 4 | varying vec2 v_texcoord; 5 | 6 | void main() { 7 | gl_Position = vec4(proj * vec3(pos, 1.0), 1.0); 8 | v_texcoord = texcoord; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/wm/shaders/primitive/rect/fragment.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | uniform float params_float[4]; 3 | uniform float alpha; 4 | varying vec2 v_texcoord; 5 | 6 | uniform float width; 7 | uniform float height; 8 | 9 | void main() { 10 | gl_FragColor = vec4( 11 | params_float[0], 12 | params_float[1], 13 | params_float[2], 14 | 1. 15 | ) * params_float[3] * alpha; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/wm/shaders/primitive/rect/vertex.glsl: -------------------------------------------------------------------------------- 1 | uniform mat3 proj; 2 | attribute vec2 pos; 3 | attribute vec2 texcoord; 4 | varying vec2 v_texcoord; 5 | 6 | void main() { 7 | gl_Position = vec4(proj * vec3(pos, 1.0), 1.0); 8 | v_texcoord = texcoord; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/wm/shaders/primitive/rounded_corners_border/fragment.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | uniform float params_float[6]; 3 | uniform float alpha; 4 | varying vec2 v_texcoord; 5 | 6 | uniform float width; 7 | uniform float height; 8 | 9 | void main() { 10 | if(v_texcoord.x*width > width - params_float[4] && v_texcoord.y*height > height - params_float[4]){ 11 | if(length(vec2(v_texcoord.x*width, v_texcoord.y*height) - 12 | vec2(width - params_float[4], height - params_float[4])) > params_float[4]) discard; 13 | if(length(vec2(v_texcoord.x*width, v_texcoord.y*height) - 14 | vec2(width - params_float[4], height - params_float[4])) < params_float[4] - params_float[5]) discard; 15 | 16 | }else if(v_texcoord.x*width > width - params_float[4] && v_texcoord.y*height < params_float[4]){ 17 | if(length(vec2(v_texcoord.x*width, v_texcoord.y*height) - 18 | vec2(width - params_float[4], params_float[4])) > params_float[4]) discard; 19 | if(length(vec2(v_texcoord.x*width, v_texcoord.y*height) - 20 | vec2(width - params_float[4], params_float[4])) < params_float[4] - params_float[5]) discard; 21 | 22 | }else if(v_texcoord.x*width < params_float[4] && v_texcoord.y*height > height - params_float[4]){ 23 | if(length(vec2(v_texcoord.x*width, v_texcoord.y*height) - 24 | vec2(params_float[4], height - params_float[4])) > params_float[4]) discard; 25 | if(length(vec2(v_texcoord.x*width, v_texcoord.y*height) - 26 | vec2(params_float[4], height - params_float[4])) < params_float[4] - params_float[5]) discard; 27 | 28 | }else if(v_texcoord.x*width < params_float[4] && v_texcoord.y*height < params_float[4]){ 29 | if(length(vec2(v_texcoord.x*width, v_texcoord.y*height) - 30 | vec2(params_float[4], params_float[4])) > params_float[4]) discard; 31 | if(length(vec2(v_texcoord.x*width, v_texcoord.y*height) - 32 | vec2(params_float[4], params_float[4])) < params_float[4] - params_float[5]) discard; 33 | 34 | }else{ 35 | if(v_texcoord.x*width > params_float[5] && v_texcoord.x*width < width - params_float[5] && 36 | v_texcoord.y*height > params_float[5] && v_texcoord.y*height < height - params_float[5]) discard; 37 | } 38 | gl_FragColor = vec4( 39 | params_float[0], 40 | params_float[1], 41 | params_float[2], 42 | 1. 43 | ) * params_float[3] * alpha; 44 | } 45 | 46 | -------------------------------------------------------------------------------- /src/wm/shaders/primitive/rounded_corners_border/vertex.glsl: -------------------------------------------------------------------------------- 1 | uniform mat3 proj; 2 | attribute vec2 pos; 3 | attribute vec2 texcoord; 4 | varying vec2 v_texcoord; 5 | 6 | void main() { 7 | gl_Position = vec4(proj * vec3(pos, 1.0), 1.0); 8 | v_texcoord = texcoord; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/wm/shaders/primitive/rounded_corners_rect/fragment.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | uniform float params_float[5]; 3 | uniform float alpha; 4 | varying vec2 v_texcoord; 5 | 6 | uniform float width; 7 | uniform float height; 8 | 9 | void main() { 10 | if(v_texcoord.x*width > width - params_float[4] && v_texcoord.y*height > height - params_float[4]){ 11 | if(length(vec2(v_texcoord.x*width, v_texcoord.y*height) - 12 | vec2(width - params_float[4], height - params_float[4])) > 13 | params_float[4]) discard; 14 | } 15 | if(v_texcoord.x*width > width - params_float[4] && v_texcoord.y*height < params_float[4]){ 16 | if(length(vec2(v_texcoord.x*width, v_texcoord.y*height) - 17 | vec2(width - params_float[4], params_float[4])) > 18 | params_float[4]) discard; 19 | } 20 | if(v_texcoord.x*width < params_float[4] && v_texcoord.y*height > height - params_float[4]){ 21 | if(length(vec2(v_texcoord.x*width, v_texcoord.y*height) - 22 | vec2(params_float[4], height - params_float[4])) > 23 | params_float[4]) discard; 24 | } 25 | if(v_texcoord.x*width < params_float[4] && v_texcoord.y*height < params_float[4]){ 26 | if(length(vec2(v_texcoord.x*width, v_texcoord.y*height) - 27 | vec2(params_float[4], params_float[4])) > 28 | params_float[4]) discard; 29 | } 30 | gl_FragColor = vec4( 31 | params_float[0], 32 | params_float[1], 33 | params_float[2], 34 | 1. 35 | ) * params_float[3] * alpha; 36 | } 37 | 38 | -------------------------------------------------------------------------------- /src/wm/shaders/primitive/rounded_corners_rect/vertex.glsl: -------------------------------------------------------------------------------- 1 | uniform mat3 proj; 2 | attribute vec2 pos; 3 | attribute vec2 texcoord; 4 | varying vec2 v_texcoord; 5 | 6 | void main() { 7 | gl_Position = vec4(proj * vec3(pos, 1.0), 1.0); 8 | v_texcoord = texcoord; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/wm/shaders/quad/fragment.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | varying vec2 v_texcoord; 3 | uniform sampler2D tex; 4 | 5 | void main() { 6 | gl_FragColor = texture2D(tex, v_texcoord); 7 | } 8 | -------------------------------------------------------------------------------- /src/wm/shaders/quad/fragment_downsample.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | varying vec2 v_texcoord; 3 | uniform sampler2D tex; 4 | uniform vec2 halfpixel; 5 | uniform float offset; 6 | 7 | void main() { 8 | vec4 sum = texture2D(tex, v_texcoord) * 4.0; 9 | sum += texture2D(tex, v_texcoord - halfpixel.xy * offset); 10 | sum += texture2D(tex, v_texcoord + halfpixel.xy * offset); 11 | sum += texture2D(tex, v_texcoord + vec2(halfpixel.x, -halfpixel.y) * offset); 12 | sum += texture2D(tex, v_texcoord - vec2(halfpixel.x, -halfpixel.y) * offset); 13 | gl_FragColor = sum / 8.; 14 | } 15 | -------------------------------------------------------------------------------- /src/wm/shaders/quad/fragment_upsample.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | varying vec2 v_texcoord; 3 | uniform sampler2D tex; 4 | 5 | uniform float width; 6 | uniform float height; 7 | uniform float padding_l; 8 | uniform float padding_t; 9 | uniform float padding_r; 10 | uniform float padding_b; 11 | uniform float cornerradius; 12 | 13 | uniform vec2 halfpixel; 14 | uniform float offset; 15 | 16 | void main() { 17 | float x = gl_FragCoord.x; 18 | float y = gl_FragCoord.y; 19 | 20 | if(x < padding_l) discard; 21 | if(y < padding_t) discard; 22 | if(x > width - padding_r) discard; 23 | if(y > height - padding_b) discard; 24 | if(x < cornerradius + padding_l && y < cornerradius + padding_t){ 25 | if(length(vec2(x, y) - 26 | vec2(cornerradius + padding_l, cornerradius + padding_t)) > cornerradius) 27 | discard; 28 | } 29 | if(x > width - cornerradius - padding_r && y < cornerradius + padding_t){ 30 | if(length(vec2(x, y) - 31 | vec2(width - cornerradius - padding_r, cornerradius + padding_t)) > cornerradius) 32 | discard; 33 | } 34 | if(x < cornerradius + padding_l && y > height - cornerradius - padding_b){ 35 | if(length(vec2(x, y) - 36 | vec2(cornerradius + padding_l, height - cornerradius - padding_b)) > cornerradius) 37 | discard; 38 | } 39 | if(x > width - cornerradius - padding_r && y > height - cornerradius - padding_b){ 40 | if(length(vec2(x, y) - 41 | vec2(width - cornerradius - padding_r, height - cornerradius - padding_b)) > cornerradius) 42 | discard; 43 | } 44 | 45 | vec4 sum = texture2D(tex, v_texcoord + vec2(-halfpixel.x * 2.0, 0.0) * offset); 46 | sum += texture2D(tex, v_texcoord + vec2(-halfpixel.x, halfpixel.y) * offset) * 2.0; 47 | sum += texture2D(tex, v_texcoord + vec2(0.0, halfpixel.y * 2.0) * offset); 48 | sum += texture2D(tex, v_texcoord + vec2(halfpixel.x, halfpixel.y) * offset) * 2.0; 49 | sum += texture2D(tex, v_texcoord + vec2(halfpixel.x * 2.0, 0.0) * offset); 50 | sum += texture2D(tex, v_texcoord + vec2(halfpixel.x, -halfpixel.y) * offset) * 2.0; 51 | sum += texture2D(tex, v_texcoord + vec2(0.0, -halfpixel.y * 2.0) * offset); 52 | sum += texture2D(tex, v_texcoord + vec2(-halfpixel.x, -halfpixel.y) * offset) * 2.0; 53 | gl_FragColor = sum / 12.; 54 | } 55 | -------------------------------------------------------------------------------- /src/wm/shaders/quad/vertex.glsl: -------------------------------------------------------------------------------- 1 | attribute vec2 pos; 2 | attribute vec2 texcoord; 3 | varying vec2 v_texcoord; 4 | 5 | void main() { 6 | gl_Position = vec4(pos, 1.0, 1.0); 7 | v_texcoord = texcoord; 8 | } 9 | -------------------------------------------------------------------------------- /src/wm/shaders/texture/basic/fragment_ext.glsl: -------------------------------------------------------------------------------- 1 | #extension GL_OES_EGL_image_external : require 2 | precision mediump float; 3 | 4 | varying vec2 v_texcoord; 5 | uniform samplerExternalOES texture0; 6 | uniform float alpha; 7 | 8 | uniform float offset_x; 9 | uniform float offset_y; 10 | uniform float scale_x; 11 | uniform float scale_y; 12 | uniform float width; 13 | uniform float height; 14 | 15 | uniform float padding_l; 16 | uniform float padding_t; 17 | uniform float padding_r; 18 | uniform float padding_b; 19 | uniform float cornerradius; 20 | uniform float lock_perc; 21 | 22 | void main() { 23 | float x = (v_texcoord.x - offset_x)*scale_x; 24 | float y = (v_texcoord.y - offset_y)*scale_y; 25 | 26 | if(x < padding_l) discard; 27 | if(y < padding_t) discard; 28 | if(x > width - padding_r) discard; 29 | if(y > height - padding_b) discard; 30 | if(x < cornerradius + padding_l && y < cornerradius + padding_t){ 31 | if(length(vec2(x, y) - 32 | vec2(cornerradius + padding_l, cornerradius + padding_t)) > cornerradius) 33 | discard; 34 | } 35 | if(x > width - cornerradius - padding_r && y < cornerradius + padding_t){ 36 | if(length(vec2(x, y) - 37 | vec2(width - cornerradius - padding_r, cornerradius + padding_t)) > cornerradius) 38 | discard; 39 | } 40 | if(x < cornerradius + padding_l && y > height - cornerradius - padding_b){ 41 | if(length(vec2(x, y) - 42 | vec2(cornerradius + padding_l, height - cornerradius - padding_b)) > cornerradius) 43 | discard; 44 | } 45 | if(x > width - cornerradius - padding_r && y > height - cornerradius - padding_b){ 46 | if(length(vec2(x, y) - 47 | vec2(width - cornerradius - padding_r, height - cornerradius - padding_b)) > cornerradius) 48 | discard; 49 | } 50 | 51 | gl_FragColor = texture2D(texture0, v_texcoord) * alpha; 52 | }; 53 | -------------------------------------------------------------------------------- /src/wm/shaders/texture/basic/fragment_rgba.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec2 v_texcoord; 4 | uniform sampler2D tex; 5 | uniform float alpha; 6 | 7 | uniform float offset_x; 8 | uniform float offset_y; 9 | uniform float scale_x; 10 | uniform float scale_y; 11 | uniform float width; 12 | uniform float height; 13 | 14 | uniform float padding_l; 15 | uniform float padding_t; 16 | uniform float padding_r; 17 | uniform float padding_b; 18 | uniform float cornerradius; 19 | uniform float lock_perc; 20 | 21 | void main() { 22 | float x = (v_texcoord.x - offset_x)*scale_x; 23 | float y = (v_texcoord.y - offset_y)*scale_y; 24 | 25 | if(x < padding_l) discard; 26 | if(y < padding_t) discard; 27 | if(x > width - padding_r) discard; 28 | if(y > height - padding_b) discard; 29 | if(x < cornerradius + padding_l && y < cornerradius + padding_t){ 30 | if(length(vec2(x, y) - 31 | vec2(cornerradius + padding_l, cornerradius + padding_t)) > cornerradius) 32 | discard; 33 | } 34 | if(x > width - cornerradius - padding_r && y < cornerradius + padding_t){ 35 | if(length(vec2(x, y) - 36 | vec2(width - cornerradius - padding_r, cornerradius + padding_t)) > cornerradius) 37 | discard; 38 | } 39 | if(x < cornerradius + padding_l && y > height - cornerradius - padding_b){ 40 | if(length(vec2(x, y) - 41 | vec2(cornerradius + padding_l, height - cornerradius - padding_b)) > cornerradius) 42 | discard; 43 | } 44 | if(x > width - cornerradius - padding_r && y > height - cornerradius - padding_b){ 45 | if(length(vec2(x, y) - 46 | vec2(width - cornerradius - padding_r, height - cornerradius - padding_b)) > cornerradius) 47 | discard; 48 | } 49 | 50 | gl_FragColor = texture2D(tex, v_texcoord) * alpha; 51 | } 52 | -------------------------------------------------------------------------------- /src/wm/shaders/texture/basic/fragment_rgbx.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec2 v_texcoord; 4 | uniform sampler2D tex; 5 | uniform float alpha; 6 | 7 | uniform float offset_x; 8 | uniform float offset_y; 9 | uniform float scale_x; 10 | uniform float scale_y; 11 | uniform float width; 12 | uniform float height; 13 | 14 | uniform float padding_l; 15 | uniform float padding_t; 16 | uniform float padding_r; 17 | uniform float padding_b; 18 | uniform float cornerradius; 19 | uniform float lock_perc; 20 | 21 | void main() { 22 | float x = (v_texcoord.x - offset_x)*scale_x; 23 | float y = (v_texcoord.y - offset_y)*scale_y; 24 | 25 | if(x < padding_l) discard; 26 | if(y < padding_t) discard; 27 | if(x > width - padding_r) discard; 28 | if(y > height - padding_b) discard; 29 | if(x < cornerradius + padding_l && y < cornerradius + padding_t){ 30 | if(length(vec2(x, y) - 31 | vec2(cornerradius + padding_l, cornerradius + padding_t)) > cornerradius) 32 | discard; 33 | } 34 | if(x > width - cornerradius - padding_r && y < cornerradius + padding_t){ 35 | if(length(vec2(x, y) - 36 | vec2(width - cornerradius - padding_r, cornerradius + padding_t)) > cornerradius) 37 | discard; 38 | } 39 | if(x < cornerradius + padding_l && y > height - cornerradius - padding_b){ 40 | if(length(vec2(x, y) - 41 | vec2(cornerradius + padding_l, height - cornerradius - padding_b)) > cornerradius) 42 | discard; 43 | } 44 | if(x > width - cornerradius - padding_r && y > height - cornerradius - padding_b){ 45 | if(length(vec2(x, y) - 46 | vec2(width - cornerradius - padding_r, height - cornerradius - padding_b)) > cornerradius) 47 | discard; 48 | } 49 | 50 | gl_FragColor = vec4(texture2D(tex, v_texcoord).rgb, 1.0) * alpha; 51 | } 52 | -------------------------------------------------------------------------------- /src/wm/shaders/texture/basic/vertex.glsl: -------------------------------------------------------------------------------- 1 | uniform mat3 proj; 2 | attribute vec2 pos; 3 | attribute vec2 texcoord; 4 | varying vec2 v_texcoord; 5 | 6 | void main() { 7 | gl_Position = vec4(proj * vec3(pos, 1.0), 1.0); 8 | v_texcoord = texcoord; 9 | } 10 | -------------------------------------------------------------------------------- /src/wm/shaders/texture/fancy/fragment_ext.glsl: -------------------------------------------------------------------------------- 1 | #extension GL_OES_EGL_image_external : require 2 | precision mediump float; 3 | 4 | varying vec2 v_texcoord; 5 | uniform samplerExternalOES texture0; 6 | uniform float alpha; 7 | 8 | uniform float offset_x; 9 | uniform float offset_y; 10 | uniform float scale_x; 11 | uniform float scale_y; 12 | uniform float width; 13 | uniform float height; 14 | 15 | uniform float padding_l; 16 | uniform float padding_t; 17 | uniform float padding_r; 18 | uniform float padding_b; 19 | uniform float cornerradius; 20 | uniform float lock_perc; 21 | 22 | void main() { 23 | float x = (v_texcoord.x - offset_x)*scale_x; 24 | float y = (v_texcoord.y - offset_y)*scale_y; 25 | 26 | if(x < padding_l) discard; 27 | if(y < padding_t) discard; 28 | if(x > width - padding_r) discard; 29 | if(y > height - padding_b) discard; 30 | if(x < cornerradius + padding_l && y < cornerradius + padding_t){ 31 | if(length(vec2(x, y) - 32 | vec2(cornerradius + padding_l, cornerradius + padding_t)) > cornerradius) 33 | discard; 34 | } 35 | if(x > width - cornerradius - padding_r && y < cornerradius + padding_t){ 36 | if(length(vec2(x, y) - 37 | vec2(width - cornerradius - padding_r, cornerradius + padding_t)) > cornerradius) 38 | discard; 39 | } 40 | if(x < cornerradius + padding_l && y > height - cornerradius - padding_b){ 41 | if(length(vec2(x, y) - 42 | vec2(cornerradius + padding_l, height - cornerradius - padding_b)) > cornerradius) 43 | discard; 44 | } 45 | if(x > width - cornerradius - padding_r && y > height - cornerradius - padding_b){ 46 | if(length(vec2(x, y) - 47 | vec2(width - cornerradius - padding_r, height - cornerradius - padding_b)) > cornerradius) 48 | discard; 49 | } 50 | 51 | if(lock_perc > 0.0001){ 52 | float r = sqrt((v_texcoord.x - 0.5) * (v_texcoord.x - 0.5) + 53 | (v_texcoord.y - 0.5) * (v_texcoord.y - 0.5)); 54 | float a = atan(v_texcoord.y - 0.5, v_texcoord.x - 0.5); 55 | gl_FragColor = texture2D(texture0, vec2(0.5 + r*cos(a + lock_perc * 10.0 * 56 | (0.5 - r)), 0.5 + r*sin(a + lock_perc * 10.0 * (0.5 - r)))) * alpha; 57 | }else{ 58 | gl_FragColor = texture2D(texture0, v_texcoord) * alpha; 59 | } 60 | }; 61 | -------------------------------------------------------------------------------- /src/wm/shaders/texture/fancy/fragment_rgba.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec2 v_texcoord; 4 | uniform sampler2D tex; 5 | uniform float alpha; 6 | 7 | uniform float offset_x; 8 | uniform float offset_y; 9 | uniform float scale_x; 10 | uniform float scale_y; 11 | uniform float width; 12 | uniform float height; 13 | 14 | uniform float padding_l; 15 | uniform float padding_t; 16 | uniform float padding_r; 17 | uniform float padding_b; 18 | uniform float cornerradius; 19 | uniform float lock_perc; 20 | 21 | void main() { 22 | float x = (v_texcoord.x - offset_x)*scale_x; 23 | float y = (v_texcoord.y - offset_y)*scale_y; 24 | 25 | if(x < padding_l) discard; 26 | if(y < padding_t) discard; 27 | if(x > width - padding_r) discard; 28 | if(y > height - padding_b) discard; 29 | if(x < cornerradius + padding_l && y < cornerradius + padding_t){ 30 | if(length(vec2(x, y) - 31 | vec2(cornerradius + padding_l, cornerradius + padding_t)) > cornerradius) 32 | discard; 33 | } 34 | if(x > width - cornerradius - padding_r && y < cornerradius + padding_t){ 35 | if(length(vec2(x, y) - 36 | vec2(width - cornerradius - padding_r, cornerradius + padding_t)) > cornerradius) 37 | discard; 38 | } 39 | if(x < cornerradius + padding_l && y > height - cornerradius - padding_b){ 40 | if(length(vec2(x, y) - 41 | vec2(cornerradius + padding_l, height - cornerradius - padding_b)) > cornerradius) 42 | discard; 43 | } 44 | if(x > width - cornerradius - padding_r && y > height - cornerradius - padding_b){ 45 | if(length(vec2(x, y) - 46 | vec2(width - cornerradius - padding_r, height - cornerradius - padding_b)) > cornerradius) 47 | discard; 48 | } 49 | 50 | if(lock_perc > 0.0001){ 51 | float r = sqrt((v_texcoord.x - 0.5) * (v_texcoord.x - 0.5) + 52 | (v_texcoord.y - 0.5) * (v_texcoord.y - 0.5)); 53 | float a = atan(v_texcoord.y - 0.5, v_texcoord.x - 0.5); 54 | gl_FragColor = texture2D(tex, vec2(0.5 + r*cos(a + lock_perc * 10.0 * 55 | (0.5 - r)), 0.5 + r*sin(a + lock_perc * 10.0 * (0.5 - r)))) * alpha; 56 | }else{ 57 | gl_FragColor = texture2D(tex, v_texcoord) * alpha; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/wm/shaders/texture/fancy/fragment_rgbx.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec2 v_texcoord; 4 | uniform sampler2D tex; 5 | uniform float alpha; 6 | 7 | uniform float offset_x; 8 | uniform float offset_y; 9 | uniform float scale_x; 10 | uniform float scale_y; 11 | uniform float width; 12 | uniform float height; 13 | 14 | uniform float padding_l; 15 | uniform float padding_t; 16 | uniform float padding_r; 17 | uniform float padding_b; 18 | uniform float cornerradius; 19 | uniform float lock_perc; 20 | 21 | void main() { 22 | float x = (v_texcoord.x - offset_x)*scale_x; 23 | float y = (v_texcoord.y - offset_y)*scale_y; 24 | 25 | if(x < padding_l) discard; 26 | if(y < padding_t) discard; 27 | if(x > width - padding_r) discard; 28 | if(y > height - padding_b) discard; 29 | if(x < cornerradius + padding_l && y < cornerradius + padding_t){ 30 | if(length(vec2(x, y) - 31 | vec2(cornerradius + padding_l, cornerradius + padding_t)) > cornerradius) 32 | discard; 33 | } 34 | if(x > width - cornerradius - padding_r && y < cornerradius + padding_t){ 35 | if(length(vec2(x, y) - 36 | vec2(width - cornerradius - padding_r, cornerradius + padding_t)) > cornerradius) 37 | discard; 38 | } 39 | if(x < cornerradius + padding_l && y > height - cornerradius - padding_b){ 40 | if(length(vec2(x, y) - 41 | vec2(cornerradius + padding_l, height - cornerradius - padding_b)) > cornerradius) 42 | discard; 43 | } 44 | if(x > width - cornerradius - padding_r && y > height - cornerradius - padding_b){ 45 | if(length(vec2(x, y) - 46 | vec2(width - cornerradius - padding_r, height - cornerradius - padding_b)) > cornerradius) 47 | discard; 48 | } 49 | 50 | if(lock_perc > 0.0001){ 51 | float r = sqrt((v_texcoord.x - 0.5) * (v_texcoord.x - 0.5) + 52 | (v_texcoord.y - 0.5) * (v_texcoord.y - 0.5)); 53 | float a = atan(v_texcoord.y - 0.5, v_texcoord.x - 0.5); 54 | gl_FragColor = vec4(texture2D(tex, vec2(0.5 + r*cos(a + lock_perc * 55 | 10.0 * (0.5 - r)), 0.5 + r*sin(a + lock_perc * 10.0 * (0.5 - r)))).rgb, 56 | 1.0) * alpha; 57 | }else{ 58 | gl_FragColor = vec4(texture2D(tex, v_texcoord).rgb, 1.0) * alpha; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/wm/shaders/texture/fancy/vertex.glsl: -------------------------------------------------------------------------------- 1 | uniform mat3 proj; 2 | attribute vec2 pos; 3 | attribute vec2 texcoord; 4 | varying vec2 v_texcoord; 5 | 6 | void main() { 7 | gl_Position = vec4(proj * vec3(pos, 1.0), 1.0); 8 | v_texcoord = texcoord; 9 | } 10 | -------------------------------------------------------------------------------- /src/wm/shaders/texture/noeffect/fragment_ext.glsl: -------------------------------------------------------------------------------- 1 | #extension GL_OES_EGL_image_external : require 2 | precision mediump float; 3 | 4 | varying vec2 v_texcoord; 5 | uniform samplerExternalOES texture0; 6 | uniform float alpha; 7 | 8 | uniform float offset_x; 9 | uniform float offset_y; 10 | uniform float scale_x; 11 | uniform float scale_y; 12 | uniform float width; 13 | uniform float height; 14 | 15 | uniform float padding_l; 16 | uniform float padding_t; 17 | uniform float padding_r; 18 | uniform float padding_b; 19 | uniform float cornerradius; 20 | uniform float lock_perc; 21 | 22 | void main() { 23 | gl_FragColor = texture2D(texture0, v_texcoord) * alpha; 24 | }; 25 | -------------------------------------------------------------------------------- /src/wm/shaders/texture/noeffect/fragment_rgba.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec2 v_texcoord; 4 | uniform sampler2D tex; 5 | uniform float alpha; 6 | 7 | uniform float offset_x; 8 | uniform float offset_y; 9 | uniform float scale_x; 10 | uniform float scale_y; 11 | uniform float width; 12 | uniform float height; 13 | 14 | uniform float padding_l; 15 | uniform float padding_t; 16 | uniform float padding_r; 17 | uniform float padding_b; 18 | uniform float cornerradius; 19 | uniform float lock_perc; 20 | 21 | void main() { 22 | gl_FragColor = texture2D(tex, v_texcoord) * alpha; 23 | } 24 | -------------------------------------------------------------------------------- /src/wm/shaders/texture/noeffect/fragment_rgbx.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec2 v_texcoord; 4 | uniform sampler2D tex; 5 | uniform float alpha; 6 | 7 | uniform float offset_x; 8 | uniform float offset_y; 9 | uniform float scale_x; 10 | uniform float scale_y; 11 | uniform float width; 12 | uniform float height; 13 | 14 | uniform float padding_l; 15 | uniform float padding_t; 16 | uniform float padding_r; 17 | uniform float padding_b; 18 | uniform float cornerradius; 19 | uniform float lock_perc; 20 | 21 | void main() { 22 | gl_FragColor = vec4(texture2D(tex, v_texcoord).rgb, 1.0) * alpha; 23 | } 24 | -------------------------------------------------------------------------------- /src/wm/shaders/texture/noeffect/vertex.glsl: -------------------------------------------------------------------------------- 1 | uniform mat3 proj; 2 | attribute vec2 pos; 3 | attribute vec2 texcoord; 4 | varying vec2 v_texcoord; 5 | 6 | void main() { 7 | gl_Position = vec4(proj * vec3(pos, 1.0), 1.0); 8 | v_texcoord = texcoord; 9 | } 10 | -------------------------------------------------------------------------------- /src/wm/wm_composite.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | 3 | #include 4 | #include 5 | 6 | #include "wm/wm_composite.h" 7 | #include "wm/wm_server.h" 8 | #include "wm/wm_output.h" 9 | #include "wm/wm_renderer.h" 10 | #include "wm/wm_layout.h" 11 | 12 | #include "wm/wm_util.h" 13 | 14 | struct wm_content_vtable wm_composite_vtable; 15 | 16 | void wm_composite_init(struct wm_composite* comp, struct wm_server* server){ 17 | wm_content_init(&comp->super, server); 18 | comp->super.vtable = &wm_composite_vtable; 19 | comp->type = WM_COMPOSITE_BLUR; 20 | 21 | comp->params.n_params_float = 0; 22 | comp->params.n_params_int = 0; 23 | comp->params.params_float = NULL; 24 | comp->params.params_int = NULL; 25 | } 26 | 27 | static void wm_composite_destroy(struct wm_content* super){ 28 | wm_content_base_destroy(super); 29 | struct wm_composite* comp = wm_cast(wm_composite, super); 30 | 31 | free(comp->params.params_float); 32 | free(comp->params.params_int); 33 | } 34 | 35 | void wm_composite_set_type(struct wm_composite* comp, const char* type, int n_params_int, int* params_int, int n_params_float, float* params_float){ 36 | assert(!strcmp(type, "blur")); 37 | comp->type = WM_COMPOSITE_BLUR; 38 | 39 | comp->params.n_params_int = n_params_int; 40 | comp->params.params_int = params_int; 41 | comp->params.n_params_float = n_params_float; 42 | comp->params.params_float = params_float; 43 | 44 | wm_layout_damage_from(comp->super.wm_server->wm_layout, &comp->super, NULL); 45 | } 46 | 47 | static void wm_composite_render(struct wm_content* super, struct wm_output* output, pixman_region32_t* output_damage, struct timespec now){ 48 | // Nop 49 | } 50 | 51 | static int blur_extend(int passes, int radius){ 52 | return radius * pow(2., passes); 53 | } 54 | 55 | static int wm_composite_extend(struct wm_composite* comp){ 56 | if(comp->type == WM_COMPOSITE_BLUR){ 57 | int radius = comp->params.n_params_int >= 1 ? comp->params.params_int[0] : 1; 58 | int passes = comp->params.n_params_int >= 2 ? comp->params.params_int[1] : 2; 59 | return blur_extend(passes, radius); 60 | } 61 | return 0; 62 | } 63 | 64 | static void wm_composite_extend_box(struct wlr_box* box, struct wlr_box* comp_box, int extend){ 65 | struct wlr_box extended_comp_box = { 66 | .x = comp_box->x - extend, 67 | .y = comp_box->y - extend, 68 | .width = comp_box->width + 2*extend, 69 | .height = comp_box->height + 2*extend, 70 | }; 71 | 72 | struct wlr_box extended_box = { 73 | .x = box->x - extend, 74 | .y = box->y - extend, 75 | .width = box->width + 2*extend, 76 | .height = box->height + 2*extend, 77 | }; 78 | 79 | wlr_box_intersection(box, &extended_comp_box, &extended_box); 80 | } 81 | 82 | static void wm_composite_get_effective_box(struct wm_composite* composite, struct wm_output* output, struct wlr_box* box){ 83 | double display_x, display_y, display_w, display_h; 84 | wm_content_get_box(&composite->super, &display_x, &display_y, &display_w, &display_h); 85 | 86 | box->x = round((display_x - output->layout_x) * output->wlr_output->scale); 87 | box->y = round((display_y - output->layout_y) * output->wlr_output->scale); 88 | box->width = round(display_w * output->wlr_output->scale); 89 | box->height = round(display_h * output->wlr_output->scale); 90 | 91 | if(wm_content_has_workspace(&composite->super)){ 92 | double ws_x, ws_y, ws_w, ws_h; 93 | wm_content_get_workspace(&composite->super, &ws_x, &ws_y, &ws_w, &ws_h); 94 | 95 | struct wlr_box workspace_box = { 96 | .x = round((ws_x - output->layout_x) * output->wlr_output->scale), 97 | .y = round((ws_y - output->layout_y) * output->wlr_output->scale), 98 | .width = round(ws_w * output->wlr_output->scale), 99 | .height = round(ws_h * output->wlr_output->scale)}; 100 | wlr_box_intersection(box, box, &workspace_box); 101 | } 102 | } 103 | 104 | void wm_composite_on_damage_below(struct wm_composite* comp, struct wm_output* output, struct wm_content* from, pixman_region32_t* damage){ 105 | struct wlr_box box; 106 | wm_composite_get_effective_box(comp, output, &box); 107 | 108 | int extend = wm_composite_extend(comp); 109 | int nrects; 110 | pixman_box32_t* rects = pixman_region32_rectangles(damage, &nrects); 111 | for(int i = 0; i < nrects; i++){ 112 | struct wlr_box damage_box = {.x = rects[i].x1 - extend, 113 | .y = rects[i].y1 - extend, 114 | .width = rects[i].x2 - rects[i].x1 + 2*extend, 115 | .height = rects[i].y2 - rects[i].y1 + 2*extend}; 116 | 117 | struct wlr_box inters; 118 | wlr_box_intersection(&inters, &damage_box, &box); 119 | if (wlr_box_empty(&inters)) 120 | continue; 121 | 122 | pixman_region32_t region; 123 | pixman_region32_init(®ion); 124 | pixman_region32_union_rect(®ion, ®ion, inters.x, inters.y, inters.width, inters.height); 125 | wm_layout_damage_output(output->wm_layout, output, ®ion, &comp->super); 126 | pixman_region32_fini(®ion); 127 | } 128 | } 129 | 130 | void wm_composite_apply(struct wm_composite* composite, struct wm_output* output, pixman_region32_t* damage, struct timespec now){ 131 | struct wlr_box box; 132 | wm_composite_get_effective_box(composite, output, &box); 133 | 134 | if(composite->type == WM_COMPOSITE_BLUR){ 135 | int radius = composite->params.n_params_int >= 1 ? composite->params.params_int[0] : 1; 136 | int passes = composite->params.n_params_int >= 2 ? composite->params.params_int[1] : 2; 137 | wm_renderer_apply_blur(composite->super.wm_server->wm_renderer, damage, blur_extend(passes, radius), &box, 138 | radius, passes, 139 | output->wlr_output->scale * composite->super.corner_radius); 140 | } 141 | } 142 | 143 | bool wm_content_is_composite(struct wm_content* content){ 144 | return content->vtable == &wm_composite_vtable; 145 | } 146 | 147 | static void wm_composite_printf(FILE* file, struct wm_content* super){ 148 | struct wm_composite* comp = wm_cast(wm_composite, super); 149 | fprintf(file, "wm_composite (%f, %f - %f, %f)\n", comp->super.display_x, comp->super.display_y, comp->super.display_width, comp->super.display_height); 150 | } 151 | 152 | struct wm_content_vtable wm_composite_vtable = { 153 | .destroy = &wm_composite_destroy, 154 | .render = &wm_composite_render, 155 | .damage_output = NULL, 156 | .printf = &wm_composite_printf 157 | }; 158 | 159 | 160 | struct wm_compose_chain* wm_compose_chain_from_damage(struct wm_server* server, struct wm_output* output, pixman_region32_t* damage){ 161 | 162 | struct wm_compose_chain* result = calloc(1, sizeof(struct wm_compose_chain)); 163 | pixman_region32_init(&result->damage); 164 | pixman_region32_union(&result->damage, &result->damage, damage); 165 | 166 | pixman_region32_init(&result->composite_output); 167 | 168 | struct wm_compose_chain* at = result; 169 | 170 | struct wm_content* content; 171 | bool initial = true; 172 | 173 | wl_list_for_each(content, &server->wm_contents, link){ 174 | if(initial) result->z_index = wm_content_get_z_index(content) + 1.; 175 | 176 | if(wm_content_is_composite(content)){ 177 | at->lower = calloc(1, sizeof(struct wm_compose_chain)); 178 | at->lower->higher = at; 179 | at = at->lower; 180 | 181 | at->composite = wm_cast(wm_composite, content); 182 | at->z_index = wm_content_get_z_index(content); 183 | pixman_region32_init(&at->damage); 184 | pixman_region32_init(&at->composite_output); 185 | 186 | 187 | int extend = wm_composite_extend(at->composite); 188 | struct wlr_box box; 189 | wm_composite_get_effective_box(at->composite, output, &box); 190 | 191 | int nrects; 192 | pixman_box32_t* rects = pixman_region32_rectangles(&at->higher->damage, &nrects); 193 | for(int i=0; icomposite_output, &at->composite_output, 204 | composite_output.x, composite_output.y, composite_output.width, composite_output.height); 205 | 206 | 207 | pixman_region32_union_rect(&at->damage, &at->damage, 208 | damage_box.x, damage_box.y, damage_box.width, damage_box.height); 209 | 210 | wm_composite_extend_box(&damage_box, &box, extend); 211 | pixman_region32_union_rect(&at->damage, &at->damage, 212 | damage_box.x, damage_box.y, damage_box.width, damage_box.height); 213 | 214 | } 215 | 216 | if(!pixman_region32_not_empty(&at->composite_output)){ 217 | at = at->higher; 218 | wm_compose_chain_free(at->lower); 219 | at->lower = NULL; 220 | } 221 | } 222 | 223 | initial = false; 224 | } 225 | 226 | return result; 227 | } 228 | 229 | void wm_compose_chain_free(struct wm_compose_chain* chain){ 230 | if(chain->lower){ 231 | wm_compose_chain_free(chain->lower); 232 | } 233 | pixman_region32_fini(&chain->damage); 234 | pixman_region32_fini(&chain->composite_output); 235 | free(chain); 236 | } 237 | -------------------------------------------------------------------------------- /src/wm/wm_config.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "wm/wm_config.h" 8 | #include "wm/wm_server.h" 9 | #include "wm/wm_layout.h" 10 | #include "wm/wm_seat.h" 11 | #include "wm/wm_renderer.h" 12 | 13 | static void xcursor_setenv(struct wm_config* config){ 14 | char cursor_size_fmt[16]; 15 | snprintf(cursor_size_fmt, sizeof(cursor_size_fmt), "%u", config->xcursor_size); 16 | setenv("XCURSOR_SIZE", cursor_size_fmt, 1); 17 | if (config->xcursor_theme != NULL) { 18 | setenv("XCURSOR_THEME", config->xcursor_theme, 1); 19 | } 20 | } 21 | 22 | void wm_config_init_default(struct wm_config *config) { 23 | config->enable_xwayland = false; 24 | 25 | config->callback_frequency = 10; 26 | 27 | strcpy(config->xkb_model, ""); 28 | strcpy(config->xkb_layout, ""); 29 | strcpy(config->xkb_variant, ""); 30 | strcpy(config->xkb_options, ""); 31 | strcpy(config->texture_shaders, "basic"); 32 | 33 | wl_list_init(&config->outputs); 34 | 35 | const char *cursor_theme = getenv("XCURSOR_THEME"); 36 | unsigned cursor_size = 24; 37 | const char *env_cursor_size = getenv("XCURSOR_SIZE"); 38 | if (env_cursor_size && strlen(env_cursor_size) > 0) { 39 | errno = 0; 40 | char *end; 41 | unsigned size = strtoul(env_cursor_size, &end, 10); 42 | if (!*end && errno == 0) { 43 | cursor_size = size; 44 | } 45 | } 46 | 47 | config->xcursor_theme = cursor_theme ? strdup(cursor_theme) : ""; 48 | config->xcursor_size = cursor_size; 49 | 50 | config->natural_scroll = true; 51 | config->tap_to_click = true; 52 | 53 | config->focus_follows_mouse = true; 54 | config->constrain_popups_to_toplevel = false; 55 | 56 | config->encourage_csd = true; 57 | config->debug = false; 58 | } 59 | 60 | void wm_config_reset_default(struct wm_config* config){ 61 | struct wm_config_output *output, *tmp; 62 | wl_list_for_each_safe(output, tmp, &config->outputs, link) { 63 | wl_list_remove(&output->link); 64 | free(output); 65 | } 66 | wm_config_init_default(config); 67 | } 68 | 69 | void wm_config_reconfigure(struct wm_config* config, struct wm_server* server){ 70 | wm_seat_reconfigure(server->wm_seat); 71 | wm_layout_reconfigure(server->wm_layout); 72 | wm_server_reconfigure(server); 73 | 74 | xcursor_setenv(config); 75 | wm_renderer_select_texture_shaders(server->wm_renderer, config->texture_shaders); 76 | wm_renderer_ensure_mode(server->wm_renderer, wm_config_get_renderer_mode(config)); 77 | } 78 | 79 | enum wm_renderer_mode wm_config_get_renderer_mode(struct wm_config* config){ 80 | if(!strcmp(config->renderer_mode, "wlr")){ 81 | return WM_RENDERER_WLR; 82 | }else if(!strcmp(config->renderer_mode, "pywm")){ 83 | return WM_RENDERER_PYWM; 84 | } 85 | 86 | return WM_RENDERER_PYWM; 87 | } 88 | 89 | void wm_config_set_xcursor_theme(struct wm_config* config, const char* xcursor_theme){ 90 | config->xcursor_theme = xcursor_theme; 91 | xcursor_setenv(config); 92 | } 93 | 94 | void wm_config_set_xcursor_size(struct wm_config* config, int xcursor_size){ 95 | config->xcursor_size = xcursor_size; 96 | xcursor_setenv(config); 97 | } 98 | 99 | void wm_config_add_output(struct wm_config *config, const char *name, 100 | double scale, int width, int height, int mHz, 101 | int pos_x, int pos_y, enum wl_output_transform transform) { 102 | if(!name){ 103 | wlr_log(WLR_ERROR, "Cannot add output config without name"); 104 | return; 105 | } 106 | struct wm_config_output *new = calloc(1, sizeof(struct wm_config_output)); 107 | 108 | strncpy(new->name, name, WM_CONFIG_STRLEN-1); 109 | new->scale = scale; 110 | new->width = width; 111 | new->height = height; 112 | new->mHz = mHz; 113 | new->pos_x = pos_x; 114 | new->pos_y = pos_y; 115 | new->transform = transform; 116 | wl_list_insert(&config->outputs, &new->link); 117 | } 118 | 119 | struct wm_config_output *wm_config_find_output(struct wm_config *config, 120 | const char *name) { 121 | if(!name){ 122 | return NULL; 123 | } 124 | 125 | bool found = false; 126 | struct wm_config_output *output; 127 | wl_list_for_each(output, &config->outputs, link) { 128 | if (!strcmp(output->name, name)) { 129 | found = true; 130 | break; 131 | } 132 | } 133 | 134 | if (found) 135 | return output; 136 | else 137 | return NULL; 138 | } 139 | 140 | void wm_config_destroy(struct wm_config *config) { 141 | struct wm_config_output *output, *tmp; 142 | wl_list_for_each_safe(output, tmp, &config->outputs, link) { 143 | wl_list_remove(&output->link); 144 | free(output); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/wm/wm_content.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "wm/wm_content.h" 9 | #include "wm/wm_output.h" 10 | #include "wm/wm_server.h" 11 | #include "wm/wm_layout.h" 12 | 13 | struct wm_content_vtable wm_content_base_vtable; 14 | 15 | void wm_content_init(struct wm_content* content, struct wm_server* server) { 16 | content->vtable = &wm_content_base_vtable; 17 | 18 | content->wm_server = server; 19 | content->display_x = 0.; 20 | content->display_y = 0.; 21 | content->display_width = 0.; 22 | content->display_height = 0.; 23 | content->corner_radius = 0.; 24 | 25 | content->fixed_output = NULL; 26 | content->workspace_x = 0.; 27 | content->workspace_y = 0.; 28 | content->workspace_width = -1.; 29 | content->workspace_height = -1.; 30 | 31 | 32 | content->z_index = 0; 33 | wl_list_insert(&content->wm_server->wm_contents, &content->link); 34 | 35 | content->lock_enabled = false; 36 | } 37 | 38 | void wm_content_base_destroy(struct wm_content* content) { 39 | wl_list_remove(&content->link); 40 | } 41 | 42 | void wm_content_set_output(struct wm_content* content, int key, struct wlr_output* outp){ 43 | struct wm_output* res = NULL; 44 | if(key>=0 || outp){ 45 | struct wm_output* output; 46 | wl_list_for_each(output, &content->wm_server->wm_layout->wm_outputs, link){ 47 | if(key == output->key || (outp && outp == output->wlr_output)){ 48 | res = output; 49 | break; 50 | } 51 | } 52 | } 53 | 54 | if(!res && (outp || key>=0)){ 55 | wlr_log(WLR_ERROR, "Invalid output (%d) given to wm_content_set_output", key); 56 | } 57 | 58 | if(res == content->fixed_output){ 59 | return; 60 | } 61 | 62 | wm_layout_damage_from(content->wm_server->wm_layout, content, NULL); 63 | content->fixed_output = res; 64 | wm_layout_damage_from(content->wm_server->wm_layout, content, NULL); 65 | 66 | wm_layout_update_content_outputs(content->wm_server->wm_layout, content); 67 | } 68 | 69 | struct wm_output* wm_content_get_output(struct wm_content* content){ 70 | if(!content->fixed_output) return NULL; 71 | 72 | struct wm_output* output; 73 | wl_list_for_each(output, &content->wm_server->wm_layout->wm_outputs, link){ 74 | if(output == content->fixed_output) return output; 75 | } 76 | 77 | wlr_log(WLR_ERROR, "Removing invalid fixed output"); 78 | content->fixed_output = NULL; 79 | return NULL; 80 | } 81 | 82 | void wm_content_set_workspace(struct wm_content* content, double x, double y, double width, double height){ 83 | if(fabs(content->workspace_x - x) + 84 | fabs(content->workspace_y - y) + 85 | fabs(content->workspace_width - width) + 86 | fabs(content->workspace_height - height) < 0.01) return; 87 | 88 | wm_layout_damage_from(content->wm_server->wm_layout, content, NULL); 89 | content->workspace_x = x; 90 | content->workspace_y = y; 91 | content->workspace_width = width; 92 | content->workspace_height = height; 93 | wm_layout_damage_from(content->wm_server->wm_layout, content, NULL); 94 | 95 | wm_layout_update_content_outputs(content->wm_server->wm_layout, content); 96 | } 97 | 98 | void wm_content_get_workspace(struct wm_content* content, double* workspace_x, double* workspace_y, double* workspace_width, double* workspace_height){ 99 | *workspace_x = content->workspace_x; 100 | *workspace_y = content->workspace_y; 101 | *workspace_width = content->workspace_width; 102 | *workspace_height = content->workspace_height; 103 | } 104 | 105 | bool wm_content_has_workspace(struct wm_content* content){ 106 | return !(content->workspace_width < 0 || content->workspace_height < 0); 107 | } 108 | 109 | bool wm_content_is_on_output(struct wm_content* content, struct wm_output* output){ 110 | double display_x, display_y, display_width, display_height; 111 | wm_content_get_box(content, &display_x, &display_y, &display_width, &display_height); 112 | struct wlr_box box = { 113 | .x = display_x, 114 | .y = display_y, 115 | .width = display_width, 116 | .height = display_height 117 | }; 118 | 119 | return content->fixed_output == output || (wlr_output_layout_intersects(output->wm_layout->wlr_output_layout, output->wlr_output, &box) && content->fixed_output == NULL); 120 | } 121 | 122 | void wm_content_set_box(struct wm_content* content, double x, double y, double width, double height) { 123 | if(fabs(content->display_x - x) + 124 | fabs(content->display_y - y) + 125 | fabs(content->display_width - width) + 126 | fabs(content->display_height - height) < 0.01) return; 127 | 128 | wm_layout_damage_from(content->wm_server->wm_layout, content, NULL); 129 | content->display_x = x; 130 | content->display_y = y; 131 | content->display_width = width; 132 | content->display_height = height; 133 | wm_layout_damage_from(content->wm_server->wm_layout, content, NULL); 134 | 135 | wm_layout_update_content_outputs(content->wm_server->wm_layout, content); 136 | } 137 | 138 | void wm_content_get_box(struct wm_content* content, double* display_x, double* display_y, double* display_width, double* display_height){ 139 | *display_x = content->display_x; 140 | *display_y = content->display_y; 141 | *display_width = content->display_width; 142 | *display_height = content->display_height; 143 | } 144 | 145 | void wm_content_set_z_index(struct wm_content* content, double z_index){ 146 | if(fabs(z_index - content->z_index) < 0.0001) return; 147 | 148 | content->z_index = z_index; 149 | wm_layout_damage_from(content->wm_server->wm_layout, content, NULL); 150 | } 151 | 152 | double wm_content_get_z_index(struct wm_content* content){ 153 | return content->z_index; 154 | } 155 | 156 | void wm_content_set_opacity(struct wm_content* content, double opacity){ 157 | if(fabs(content->opacity - opacity) < 0.0001) return; 158 | 159 | content->opacity = opacity; 160 | wm_layout_damage_from(content->wm_server->wm_layout, content, NULL); 161 | } 162 | 163 | double wm_content_get_opacity(struct wm_content* content){ 164 | return content->opacity; 165 | } 166 | 167 | void wm_content_set_mask(struct wm_content* content, double mask_x, double mask_y, double mask_w, double mask_h){ 168 | if(fabs(content->mask_x - mask_x) + 169 | fabs(content->mask_y - mask_y) + 170 | fabs(content->mask_w - mask_w) + 171 | fabs(content->mask_h - mask_h) < 0.0001) return; 172 | 173 | content->mask_x = mask_x; 174 | content->mask_y = mask_y; 175 | content->mask_w = mask_w; 176 | content->mask_h = mask_h; 177 | 178 | wm_layout_damage_from(content->wm_server->wm_layout, content, NULL); 179 | } 180 | void wm_content_get_mask(struct wm_content* content, double* mask_x, double* mask_y, double* mask_w, double* mask_h){ 181 | *mask_x = content->mask_x; 182 | *mask_y = content->mask_y; 183 | *mask_w = content->mask_w; 184 | *mask_h = content->mask_h; 185 | 186 | if(*mask_w < 0) *mask_w = -*mask_x + content->display_width + 1; 187 | if(*mask_h < 0) *mask_h = -*mask_h + content->display_height + 1; 188 | } 189 | 190 | void wm_content_set_corner_radius(struct wm_content* content, double corner_radius){ 191 | if(fabs(content->corner_radius - corner_radius) < 0.01) return; 192 | 193 | content->corner_radius = corner_radius; 194 | wm_layout_damage_from(content->wm_server->wm_layout, content, NULL); 195 | } 196 | 197 | void wm_content_set_lock_enabled(struct wm_content* content, bool lock_enabled){ 198 | if(lock_enabled == content->lock_enabled) return; 199 | 200 | content->lock_enabled = lock_enabled; 201 | wm_layout_damage_from(content->wm_server->wm_layout, content, NULL); 202 | } 203 | 204 | double wm_content_get_corner_radius(struct wm_content* content){ 205 | return content->corner_radius; 206 | } 207 | 208 | void wm_content_destroy(struct wm_content* content){ 209 | wm_layout_damage_from(content->wm_server->wm_layout, content, NULL); 210 | (*content->vtable->destroy)(content); 211 | } 212 | 213 | void wm_content_render(struct wm_content* content, struct wm_output* output, pixman_region32_t* output_damage, struct timespec now){ 214 | if(!wm_content_is_on_output(content, output)) return; 215 | 216 | pixman_region32_t damage_on_workspace; 217 | pixman_region32_init(&damage_on_workspace); 218 | pixman_region32_copy(&damage_on_workspace, output_damage); 219 | if(wm_content_has_workspace(content)){ 220 | int x = round((content->workspace_x - output->layout_x) * output->wlr_output->scale); 221 | int y = round((content->workspace_y - output->layout_y) * output->wlr_output->scale); 222 | int w = round(content->workspace_width * output->wlr_output->scale); 223 | int h = round(content->workspace_height * output->wlr_output->scale); 224 | pixman_region32_intersect_rect(&damage_on_workspace, &damage_on_workspace, x, y, w, h); 225 | } 226 | 227 | (*content->vtable->render)(content, output, &damage_on_workspace, now); 228 | 229 | pixman_region32_fini(&damage_on_workspace); 230 | } 231 | 232 | void wm_content_damage_output_base(struct wm_content* content, struct wm_output* output, struct wlr_surface* origin){ 233 | pixman_region32_t region; 234 | pixman_region32_init(®ion); 235 | 236 | double x, y, w, h; 237 | wm_content_get_box(content, &x, &y, &w, &h); 238 | x -= output->layout_x; 239 | y -= output->layout_y; 240 | 241 | x *= output->wlr_output->scale; 242 | y *= output->wlr_output->scale; 243 | w *= output->wlr_output->scale; 244 | h *= output->wlr_output->scale; 245 | pixman_region32_union_rect(®ion, ®ion, 246 | floor(x), floor(y), 247 | ceil(x + w) - floor(x), ceil(y + h) - floor(y)); 248 | 249 | if(wm_content_has_workspace(content)){ 250 | double workspace_x, workspace_y, workspace_w, workspace_h; 251 | wm_content_get_workspace(content, &workspace_x, &workspace_y, 252 | &workspace_w, &workspace_h); 253 | workspace_x = (workspace_x - output->layout_x) * output->wlr_output->scale; 254 | workspace_y = (workspace_y - output->layout_y) * output->wlr_output->scale; 255 | workspace_w *= output->wlr_output->scale; 256 | workspace_h *= output->wlr_output->scale; 257 | pixman_region32_intersect_rect( 258 | ®ion, ®ion, 259 | floor(workspace_x), 260 | floor(workspace_y), 261 | ceil(workspace_x + workspace_w) - floor(workspace_x), 262 | ceil(workspace_y + workspace_h) - floor(workspace_y)); 263 | } 264 | 265 | wm_layout_damage_output(output->wm_layout, output, ®ion, content); 266 | pixman_region32_fini(®ion); 267 | } 268 | 269 | struct wm_content_vtable wm_content_base_vtable = { 270 | .destroy = wm_content_base_destroy, 271 | }; 272 | -------------------------------------------------------------------------------- /src/wm/wm_drag.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | 3 | #include 4 | #include 5 | #include 6 | #include "wm/wm_drag.h" 7 | #include "wm/wm_seat.h" 8 | #include "wm/wm_util.h" 9 | #include "wm/wm_layout.h" 10 | #include "wm/wm_server.h" 11 | #include "wm/wm_output.h" 12 | #include "wm/wm_cursor.h" 13 | #include "wm/wm_renderer.h" 14 | 15 | struct wm_content_vtable wm_drag_vtable; 16 | 17 | static void wm_drag_icon_destroy(struct wm_drag* drag){ 18 | if(drag->wlr_drag_icon){ 19 | wl_list_remove(&drag->icon_map.link); 20 | wl_list_remove(&drag->icon_unmap.link); 21 | wl_list_remove(&drag->icon_destroy.link); 22 | wl_list_remove(&drag->icon_surface_commit.link); 23 | drag->wlr_drag_icon = NULL; 24 | } 25 | 26 | } 27 | 28 | static void handle_destroy(struct wl_listener* listener, void* data){ 29 | wlr_log(WLR_DEBUG, "Drag: destroying drag (and icon)"); 30 | struct wm_drag* drag = wl_container_of(listener, drag, destroy); 31 | 32 | wm_layout_damage_from(drag->wm_seat->wm_server->wm_layout, &drag->super, NULL); 33 | wm_content_destroy(&drag->super); 34 | free(drag); 35 | } 36 | 37 | static void icon_handle_destroy(struct wl_listener* listener, void* data){ 38 | struct wm_drag* drag = wl_container_of(listener, drag, icon_destroy); 39 | wlr_log(WLR_DEBUG, "Drag: destroying icon"); 40 | 41 | wm_drag_icon_destroy(drag); 42 | } 43 | 44 | static void icon_handle_surface_commit(struct wl_listener* listener, void* data){ 45 | struct wm_drag* drag = wl_container_of(listener, drag, icon_surface_commit); 46 | wm_drag_update_position(drag); 47 | } 48 | 49 | static void icon_handle_map(struct wl_listener* listener, void* data){ 50 | struct wm_drag* drag = wl_container_of(listener, drag, icon_map); 51 | 52 | wlr_log(WLR_DEBUG, "Drag: surface map"); 53 | wm_drag_update_position(drag); 54 | } 55 | static void icon_handle_unmap(struct wl_listener* listener, void* data){ 56 | struct wm_drag* drag = wl_container_of(listener, drag, icon_unmap); 57 | 58 | wlr_log(WLR_DEBUG, "Drag: surface unmap"); 59 | wm_layout_damage_from(drag->wm_seat->wm_server->wm_layout, &drag->super, NULL); 60 | } 61 | 62 | void wm_drag_init(struct wm_drag* drag, struct wm_seat* seat, struct wlr_drag* wlr_drag){ 63 | wm_content_init(&drag->super, seat->wm_server); 64 | drag->super.vtable = &wm_drag_vtable; 65 | wm_content_set_opacity(&drag->super, 0.5); 66 | wm_content_set_z_index(&drag->super, WM_DRAG_Z_INDEX); 67 | wm_content_set_box(&drag->super, 0, 0, 0, 0); 68 | 69 | drag->wm_seat = seat; 70 | drag->wlr_drag = wlr_drag; 71 | 72 | drag->destroy.notify = handle_destroy; 73 | wl_signal_add(&wlr_drag->events.destroy, &drag->destroy); 74 | 75 | drag->wlr_drag_icon = NULL; 76 | if(wlr_drag->icon){ 77 | drag->wlr_drag_icon = wlr_drag->icon; 78 | 79 | drag->icon_destroy.notify = icon_handle_destroy; 80 | wl_signal_add(&wlr_drag->icon->events.destroy, &drag->icon_destroy); 81 | 82 | drag->icon_map.notify = icon_handle_map; 83 | wl_signal_add(&wlr_drag->icon->events.map, &drag->icon_map); 84 | 85 | drag->icon_unmap.notify = icon_handle_unmap; 86 | wl_signal_add(&wlr_drag->icon->events.unmap, &drag->icon_unmap); 87 | 88 | drag->icon_surface_commit.notify = icon_handle_surface_commit; 89 | wl_signal_add(&wlr_drag->icon->surface->events.commit, &drag->icon_surface_commit); 90 | } 91 | } 92 | 93 | void wm_drag_update_position(struct wm_drag* drag){ 94 | wm_layout_damage_from(drag->wm_seat->wm_server->wm_layout, &drag->super, NULL); 95 | /* Surface width / height are not set correctly - hacky way via output scale */ 96 | if(!drag->wlr_drag_icon || !drag->wlr_drag_icon->surface) return; 97 | 98 | double width = drag->wlr_drag_icon->surface->current.buffer_width; 99 | double height = drag->wlr_drag_icon->surface->current.buffer_height; 100 | wm_content_set_box(&drag->super, 101 | drag->wm_seat->wm_cursor->wlr_cursor->x - .5*width, 102 | drag->wm_seat->wm_cursor->wlr_cursor->y - .5*height, 103 | width, height); 104 | wm_layout_damage_from(drag->wm_seat->wm_server->wm_layout, &drag->super, NULL); 105 | } 106 | 107 | static void wm_drag_destroy(struct wm_content* super){ 108 | struct wm_drag* drag = wm_cast(wm_drag, super); 109 | wl_list_remove(&drag->destroy.link); 110 | wm_drag_icon_destroy(drag); 111 | 112 | wm_content_base_destroy(super); 113 | } 114 | 115 | static void wm_drag_render(struct wm_content* super, struct wm_output* output, pixman_region32_t* output_damage, struct timespec now){ 116 | struct wm_drag* drag = wm_cast(wm_drag, super); 117 | if(!drag->wlr_drag_icon) return; 118 | 119 | struct wlr_box unscaled = { 120 | .x = round((drag->super.display_x - output->layout_x) * output->wlr_output->scale), 121 | .y = round((drag->super.display_y - output->layout_y) * output->wlr_output->scale), 122 | .width = round(drag->super.display_width * output->wlr_output->scale), 123 | .height = round(drag->super.display_height * output->wlr_output->scale)}; 124 | 125 | /* drag icons do not seem to properly handle scaling - therefore simply crop by output_scale */ 126 | struct wlr_box box = { 127 | .x = unscaled.x + .5 * unscaled.width * (1. - 1. / output->wlr_output->scale), 128 | .y = unscaled.y + .5 * unscaled.height * (1. - 1. / output->wlr_output->scale), 129 | .width = unscaled.width / output->wlr_output->scale, 130 | .height = unscaled.height / output->wlr_output->scale 131 | }; 132 | 133 | struct wlr_texture *texture = wlr_surface_get_texture(drag->wlr_drag_icon->surface); 134 | if (!texture) { 135 | return; 136 | } 137 | wm_renderer_render_texture_at( 138 | output->wm_server->wm_renderer, output_damage, 139 | drag->wlr_drag_icon->surface, texture, &box, 140 | wm_content_get_opacity(super), &box, 0, 141 | super->lock_enabled ? 0.0 : super->wm_server->lock_perc); 142 | } 143 | 144 | bool wm_content_is_drag(struct wm_content* content){ 145 | return content->vtable == &wm_drag_vtable; 146 | } 147 | 148 | static void wm_drag_printf(FILE* file, struct wm_content* super){ 149 | struct wm_drag* drag = wm_cast(wm_drag, super); 150 | fprintf(file, "wm_drag (%f, %f - %f, %f)\n", drag->super.display_x, drag->super.display_y, drag->super.display_width, drag->super.display_height); 151 | } 152 | 153 | struct wm_content_vtable wm_drag_vtable = { 154 | .destroy = &wm_drag_destroy, 155 | .render = &wm_drag_render, 156 | .damage_output = NULL, 157 | .printf = &wm_drag_printf, 158 | }; 159 | -------------------------------------------------------------------------------- /src/wm/wm_idle_inhibit.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "wm/wm_server.h" 9 | #include "wm/wm_view.h" 10 | #include "wm/wm_idle_inhibit.h" 11 | 12 | static void handle_destroy(struct wl_listener* listener, void* data){ 13 | wlr_log(WLR_DEBUG, "Inhibit: Destroying idle inhibitor"); 14 | struct wm_idle_inhibitor* inhibitor = wl_container_of(listener, inhibitor, destroy); 15 | 16 | wm_idle_inhibitor_destroy(inhibitor); 17 | free(inhibitor); 18 | } 19 | 20 | void wm_idle_inhibitor_init(struct wm_idle_inhibitor* inhibitor, struct wm_idle_inhibit* parent, struct wlr_idle_inhibitor_v1* wlr_inhibitor){ 21 | inhibitor->wlr_inhibitor = wlr_inhibitor; 22 | inhibitor->parent = parent; 23 | inhibitor->view = wm_server_view_for_surface(parent->wm_server, wlr_inhibitor->surface); 24 | 25 | inhibitor->destroy.notify = handle_destroy; 26 | wl_signal_add(&inhibitor->wlr_inhibitor->events.destroy, &inhibitor->destroy); 27 | 28 | if(inhibitor->view){ 29 | wm_view_set_inhibiting_idle(inhibitor->view, true); 30 | } 31 | 32 | } 33 | 34 | void wm_idle_inhibitor_destroy(struct wm_idle_inhibitor* inhibitor){ 35 | wl_list_remove(&inhibitor->destroy.link); 36 | 37 | if(inhibitor->view){ 38 | wm_view_set_inhibiting_idle(inhibitor->view, false); 39 | } 40 | } 41 | 42 | static void handle_new_idle_inhibitor(struct wl_listener* listener, void* data){ 43 | wlr_log(WLR_DEBUG, "Inhibit: New idle inhibitor"); 44 | struct wm_idle_inhibit* inhibit = wl_container_of(listener, inhibit, new_idle_inhibitor); 45 | struct wlr_idle_inhibitor_v1* wlr_inhibitor = data; 46 | 47 | struct wm_idle_inhibitor* inhibitor = calloc(1, sizeof(struct wm_idle_inhibitor)); 48 | wm_idle_inhibitor_init(inhibitor, inhibit, wlr_inhibitor); 49 | } 50 | 51 | void wm_idle_inhibit_init(struct wm_idle_inhibit* inhibit, struct wm_server* server){ 52 | inhibit->wm_server = server; 53 | inhibit->wlr_idle_inhibit_manager = wlr_idle_inhibit_v1_create(server->wl_display); 54 | 55 | inhibit->new_idle_inhibitor.notify = handle_new_idle_inhibitor; 56 | wl_signal_add(&inhibit->wlr_idle_inhibit_manager->events.new_inhibitor, &inhibit->new_idle_inhibitor); 57 | } 58 | 59 | void wm_idle_inhibit_destroy(struct wm_idle_inhibit* inhibit){ 60 | wl_list_remove(&inhibit->new_idle_inhibitor.link); 61 | } 62 | -------------------------------------------------------------------------------- /src/wm/wm_keyboard.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "wm/wm_keyboard.h" 11 | #include "wm/wm_seat.h" 12 | #include "wm/wm_server.h" 13 | #include "wm/wm_config.h" 14 | #include "wm/wm.h" 15 | 16 | 17 | #define KEYS_STRING_LENGTH 256 18 | 19 | /* 20 | * Callbacks 21 | */ 22 | static void handle_destroy(struct wl_listener* listener, void* data){ 23 | struct wm_keyboard* keyboard = wl_container_of(listener, keyboard, destroy); 24 | wm_keyboard_destroy(keyboard); 25 | } 26 | 27 | static void handle_key(struct wl_listener* listener, void* data){ 28 | struct wm_keyboard* keyboard = wl_container_of(listener, keyboard, key); 29 | struct wlr_event_keyboard_key* event = data; 30 | 31 | xkb_keycode_t keycode = event->keycode + 8; 32 | size_t keysyms_len; 33 | const xkb_keysym_t* keysyms; 34 | 35 | xkb_layout_index_t layout_index = xkb_state_key_get_layout( 36 | keyboard->wlr_input_device->keyboard->xkb_state, keycode); 37 | keysyms_len = xkb_keymap_key_get_syms_by_level( 38 | keyboard->wlr_input_device->keyboard->keymap, 39 | keycode, layout_index, 0, &keysyms); 40 | 41 | /* Translated logic consuming modifiers */ 42 | size_t keysyms_trans_len; 43 | const xkb_keysym_t* keysyms_trans; 44 | keysyms_trans_len = xkb_state_key_get_syms( 45 | keyboard->wlr_input_device->keyboard->xkb_state, keycode, &keysyms_trans); 46 | 47 | /* Copied from sway - switch VT on CTRL-ALT-Fx */ 48 | for (size_t i = 0; i < keysyms_trans_len; ++i) { 49 | xkb_keysym_t keysym = keysyms_trans[i]; 50 | if (keysym >= XKB_KEY_XF86Switch_VT_1 && 51 | keysym <= XKB_KEY_XF86Switch_VT_12) { 52 | if (wlr_backend_is_multi( 53 | keyboard->wm_seat->wm_server->wlr_backend)) { 54 | struct wlr_session *session = wlr_backend_get_session( 55 | keyboard->wm_seat->wm_server->wlr_backend); 56 | if (session) { 57 | unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1; 58 | wlr_session_change_vt(session, vt); 59 | } 60 | } 61 | return; 62 | } 63 | } 64 | 65 | char keys[KEYS_STRING_LENGTH] = { 0 }; 66 | size_t at=0; 67 | for(size_t i=0; iwm_seat->wm_server->wm_config->debug){ 74 | if(!strcmp(keys, "F1") && event->state == WL_KEYBOARD_KEY_STATE_PRESSED){ 75 | wm_server_printf(stderr, keyboard->wm_seat->wm_server); 76 | } 77 | } 78 | 79 | if(wm_callback_key(event, keys)){ 80 | return; 81 | } 82 | 83 | wm_seat_dispatch_key(keyboard->wm_seat, keyboard->wlr_input_device, event); 84 | } 85 | 86 | static void handle_modifiers(struct wl_listener* listener, void* data){ 87 | struct wm_keyboard* keyboard = wl_container_of(listener, keyboard, modifiers); 88 | 89 | if(wm_callback_modifiers(&keyboard->wlr_input_device->keyboard->modifiers)){ 90 | return; 91 | } 92 | 93 | wm_seat_dispatch_modifiers(keyboard->wm_seat, keyboard->wlr_input_device); 94 | } 95 | 96 | /* 97 | * Class implementation 98 | */ 99 | void wm_keyboard_init(struct wm_keyboard* keyboard, struct wm_seat* seat, struct wlr_input_device* input_device){ 100 | keyboard->wm_seat = seat; 101 | keyboard->wlr_input_device = input_device; 102 | 103 | wm_keyboard_reconfigure(keyboard); 104 | 105 | /* Handlers */ 106 | keyboard->destroy.notify = handle_destroy; 107 | wl_signal_add(&keyboard->wlr_input_device->events.destroy, &keyboard->destroy); 108 | 109 | keyboard->key.notify = handle_key; 110 | wl_signal_add(&keyboard->wlr_input_device->keyboard->events.key, &keyboard->key); 111 | 112 | keyboard->modifiers.notify = handle_modifiers; 113 | wl_signal_add(&keyboard->wlr_input_device->keyboard->events.modifiers, &keyboard->modifiers); 114 | } 115 | 116 | void wm_keyboard_destroy(struct wm_keyboard* keyboard){ 117 | wl_list_remove(&keyboard->destroy.link); 118 | wl_list_remove(&keyboard->key.link); 119 | wl_list_remove(&keyboard->modifiers.link); 120 | wl_list_remove(&keyboard->link); 121 | } 122 | 123 | void wm_keyboard_reconfigure(struct wm_keyboard* keyboard){ 124 | struct xkb_rule_names rules = {0}; 125 | rules.model = keyboard->wm_seat->wm_server->wm_config->xkb_model; 126 | rules.layout = keyboard->wm_seat->wm_server->wm_config->xkb_layout; 127 | rules.variant = keyboard->wm_seat->wm_server->wm_config->xkb_variant; 128 | rules.options = keyboard->wm_seat->wm_server->wm_config->xkb_options; 129 | 130 | struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); 131 | assert(context); 132 | 133 | struct xkb_keymap* keymap = xkb_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); 134 | if(keymap){ 135 | wlr_keyboard_set_keymap(keyboard->wlr_input_device->keyboard, keymap); 136 | wlr_keyboard_set_repeat_info(keyboard->wlr_input_device->keyboard, 25, 600); 137 | 138 | xkb_keymap_unref(keymap); 139 | }else{ 140 | wlr_log(WLR_ERROR, "Could not load keymap for %s / %s / %s", rules.model, rules.layout, rules.options); 141 | } 142 | 143 | xkb_context_unref(context); 144 | } 145 | -------------------------------------------------------------------------------- /src/wm/wm_layout.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | 3 | #include 4 | #include 5 | #include 6 | #include "wm/wm_layout.h" 7 | #include "wm/wm_output.h" 8 | #include "wm/wm.h" 9 | #include "wm/wm_view.h" 10 | #include "wm/wm_server.h" 11 | #include "wm/wm_config.h" 12 | #include "wm/wm_util.h" 13 | #include "wm/wm_composite.h" 14 | 15 | /* 16 | * Callbacks 17 | */ 18 | 19 | static int _wm_output_key = 0; 20 | static void handle_change(struct wl_listener* listener, void* data){ 21 | struct wm_layout* layout = wl_container_of(listener, layout, change); 22 | 23 | struct wm_output* output; 24 | int fastest_mHz = -1; 25 | wl_list_for_each(output, &layout->wm_outputs, link){ 26 | output->key = _wm_output_key++; 27 | 28 | if(output->wlr_output->refresh > fastest_mHz){ 29 | layout->refresh_master_output = output->key; 30 | fastest_mHz = output->wlr_output->refresh; 31 | wlr_log(WLR_DEBUG, "Following master output: %d", layout->refresh_master_output); 32 | } 33 | 34 | struct wlr_output_layout_output* o = wlr_output_layout_get(layout->wlr_output_layout, output->wlr_output); 35 | if(!o){ 36 | wlr_log(WLR_ERROR, "Output not in output layout: %s", output->wlr_output->name); 37 | }else{ 38 | output->layout_x = o->x; 39 | output->layout_y = o->y; 40 | } 41 | } 42 | 43 | wm_callback_layout_change(layout); 44 | wm_layout_damage_whole(layout); 45 | } 46 | 47 | /* 48 | * Class implementation 49 | */ 50 | void wm_layout_init(struct wm_layout* layout, struct wm_server* server){ 51 | layout->wm_server = server; 52 | wl_list_init(&layout->wm_outputs); 53 | 54 | layout->wlr_output_layout = wlr_output_layout_create(); 55 | assert(layout->wlr_output_layout); 56 | 57 | layout->change.notify = &handle_change; 58 | wl_signal_add(&layout->wlr_output_layout->events.change, &layout->change); 59 | } 60 | 61 | void wm_layout_destroy(struct wm_layout* layout) { 62 | wl_list_remove(&layout->change.link); 63 | } 64 | 65 | static void place(struct wm_layout* layout, struct wm_output* output){ 66 | struct wm_config_output* config = wm_config_find_output(layout->wm_server->wm_config, output->wlr_output->name); 67 | if(!config || (config->pos_x < WM_CONFIG_POS_MIN || config->pos_y < WM_CONFIG_POS_MIN)){ 68 | wlr_log(WLR_INFO, "Layout: Placing automatically"); 69 | wlr_output_layout_add_auto(layout->wlr_output_layout, output->wlr_output); 70 | }else{ 71 | wlr_log(WLR_INFO, "Layout: Placing at %d / %d", config->pos_x, config->pos_y); 72 | wlr_output_layout_add(layout->wlr_output_layout, output->wlr_output, config->pos_x, config->pos_y); 73 | } 74 | } 75 | 76 | void wm_layout_add_output(struct wm_layout* layout, struct wlr_output* out){ 77 | struct wm_output* output = calloc(1, sizeof(struct wm_output)); 78 | wm_output_init(output, layout->wm_server, layout, out); 79 | wl_list_insert(&layout->wm_outputs, &output->link); 80 | 81 | place(layout, output); 82 | } 83 | 84 | void wm_layout_remove_output(struct wm_layout* layout, struct wm_output* output){ 85 | wlr_output_layout_remove(layout->wlr_output_layout, output->wlr_output); 86 | } 87 | 88 | void wm_layout_reconfigure(struct wm_layout* layout){ 89 | struct wm_output* output; 90 | wl_list_for_each(output, &layout->wm_outputs, link){ 91 | wm_output_reconfigure(output); 92 | place(layout, output); 93 | } 94 | } 95 | 96 | 97 | void wm_layout_damage_whole(struct wm_layout* layout){ 98 | struct wm_output* output; 99 | wl_list_for_each(output, &layout->wm_outputs, link){ 100 | DEBUG_PERFORMANCE(damage, output->key); 101 | wlr_output_damage_add_whole(output->wlr_output_damage); 102 | 103 | if(layout->refresh_master_output != layout->refresh_scheduled){ 104 | layout->refresh_scheduled = output->key; 105 | } 106 | DEBUG_PERFORMANCE(schedule_frame, output->key); 107 | } 108 | 109 | } 110 | 111 | 112 | void wm_layout_damage_from(struct wm_layout* layout, struct wm_content* content, struct wlr_surface* origin){ 113 | struct wm_output* output; 114 | wl_list_for_each(output, &layout->wm_outputs, link){ 115 | if(!wm_content_is_on_output(content, output)) continue; 116 | DEBUG_PERFORMANCE(damage, output->key); 117 | 118 | if(!content->lock_enabled && wm_server_is_locked(layout->wm_server)){ 119 | wm_content_damage_output(content, output, NULL); 120 | }else{ 121 | wm_content_damage_output(content, output, origin); 122 | } 123 | DEBUG_PERFORMANCE(schedule_frame, output->key); 124 | } 125 | } 126 | 127 | void wm_layout_damage_output(struct wm_layout* layout, struct wm_output* output, pixman_region32_t* damage, struct wm_content* from){ 128 | wlr_output_damage_add(output->wlr_output_damage, damage); 129 | 130 | struct wm_content* content; 131 | wl_list_for_each(content, &layout->wm_server->wm_contents, link){ 132 | if(!wm_content_is_composite(content)) continue; 133 | struct wm_composite* comp = wm_cast(wm_composite, content); 134 | if(comp->super.z_index > from->z_index && &comp->super != from){ 135 | wm_composite_on_damage_below(comp, output, from, damage); 136 | } 137 | } 138 | 139 | if(layout->refresh_master_output != layout->refresh_scheduled){ 140 | layout->refresh_scheduled = output->key; 141 | } 142 | } 143 | 144 | void wm_layout_start_update(struct wm_layout* layout){ 145 | layout->refresh_scheduled = -1; 146 | } 147 | int wm_layout_get_refresh_output(struct wm_layout* layout){ 148 | return layout->refresh_scheduled; 149 | } 150 | 151 | struct send_enter_leave_data { 152 | bool enter; 153 | struct wm_output* output; 154 | }; 155 | 156 | static void send_enter_leave_it(struct wlr_surface *surface, int sx, int sy, bool constrained, void *data){ 157 | struct send_enter_leave_data* edata = data; 158 | if(edata->enter){ 159 | wlr_surface_send_enter(surface, edata->output->wlr_output); 160 | }else{ 161 | wlr_surface_send_leave(surface, edata->output->wlr_output); 162 | } 163 | } 164 | 165 | void wm_layout_update_content_outputs(struct wm_layout* layout, struct wm_content* content){ 166 | if(!wm_content_is_view(content)) return; 167 | struct wm_view* view = wm_cast(wm_view, content); 168 | 169 | struct wm_output* output; 170 | wl_list_for_each(output, &layout->wm_outputs, link){ 171 | struct send_enter_leave_data data = { 172 | .enter = wm_content_is_on_output(&view->super, output), 173 | .output = output}; 174 | wm_view_for_each_surface(view, send_enter_leave_it, &data); 175 | } 176 | } 177 | 178 | void wm_layout_printf(FILE* file, struct wm_layout* layout){ 179 | fprintf(file, "wm_layout\n"); 180 | struct wm_output* output; 181 | wl_list_for_each(output, &layout->wm_outputs, link){ 182 | int width, height; 183 | wlr_output_transformed_resolution(output->wlr_output, &width, &height); 184 | fprintf(file, " wm_output: %s (%d x %d) at %d, %d\n", output->wlr_output->name, width, height, output->layout_x, output->layout_y); 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/wm/wm_pointer.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "wm/wm_server.h" 10 | #include "wm/wm_config.h" 11 | #include "wm/wm_pointer.h" 12 | #include "wm/wm_seat.h" 13 | 14 | /* 15 | * Callbacks 16 | */ 17 | static void handle_destroy(struct wl_listener* listener, void* data){ 18 | struct wm_pointer* pointer = wl_container_of(listener, pointer, destroy); 19 | wm_pointer_destroy(pointer); 20 | } 21 | 22 | /* 23 | * Class implementation 24 | */ 25 | void wm_pointer_init(struct wm_pointer* pointer, struct wm_seat* seat, struct wlr_input_device* input_device){ 26 | pointer->wm_seat = seat; 27 | pointer->wlr_input_device = input_device; 28 | 29 | wm_pointer_reconfigure(pointer); 30 | 31 | pointer->destroy.notify = handle_destroy; 32 | wl_signal_add(&pointer->wlr_input_device->events.destroy, &pointer->destroy); 33 | } 34 | 35 | void wm_pointer_destroy(struct wm_pointer* pointer){ 36 | wl_list_remove(&pointer->link); 37 | } 38 | 39 | void wm_pointer_reconfigure(struct wm_pointer* pointer){ 40 | if(wlr_input_device_is_libinput(pointer->wlr_input_device)){ 41 | struct libinput_device* device = wlr_libinput_get_device_handle(pointer->wlr_input_device); 42 | if(libinput_device_config_scroll_has_natural_scroll(device)){ 43 | libinput_device_config_scroll_set_natural_scroll_enabled(device, pointer->wm_seat->wm_server->wm_config->natural_scroll); 44 | } 45 | libinput_device_config_tap_set_enabled(device, pointer->wm_seat->wm_server->wm_config->tap_to_click); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/wm/wm_widget.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | 3 | #include 4 | 5 | #include "wm/wm_widget.h" 6 | #include "wm/wm_server.h" 7 | #include "wm/wm_output.h" 8 | #include "wm/wm_renderer.h" 9 | #include "wm/wm_layout.h" 10 | 11 | #include "wm/wm_util.h" 12 | 13 | struct wm_content_vtable wm_widget_vtable; 14 | 15 | void wm_widget_init(struct wm_widget* widget, struct wm_server* server){ 16 | wm_content_init(&widget->super, server); 17 | widget->super.vtable = &wm_widget_vtable; 18 | 19 | widget->wlr_texture = NULL; 20 | 21 | widget->primitive.name = NULL; 22 | widget->primitive.params_int = NULL; 23 | widget->primitive.params_float = NULL; 24 | } 25 | 26 | static void wm_widget_destroy(struct wm_content* super){ 27 | struct wm_widget* widget = wm_cast(wm_widget, super); 28 | wlr_texture_destroy(widget->wlr_texture); 29 | 30 | free(widget->primitive.name); 31 | free(widget->primitive.params_int); 32 | free(widget->primitive.params_float); 33 | 34 | wm_content_base_destroy(super); 35 | } 36 | 37 | void wm_widget_set_pixels(struct wm_widget* widget, uint32_t format, uint32_t stride, uint32_t width, uint32_t height, const void* data){ 38 | if(widget->wlr_texture){ 39 | wlr_texture_write_pixels(widget->wlr_texture, stride, width, height, 0, 0, 0, 0, data); 40 | }else{ 41 | widget->wlr_texture = wlr_texture_from_pixels(widget->super.wm_server->wm_renderer->wlr_renderer, 42 | format, stride, width, height, data); 43 | } 44 | wm_widget_set_primitive(widget, NULL, 0, NULL, 0, NULL); 45 | 46 | wm_layout_damage_from(widget->super.wm_server->wm_layout, &widget->super, NULL); 47 | } 48 | 49 | void wm_widget_set_primitive(struct wm_widget* widget, char* name, int n_params_int, int* params_int, int n_params_float, float* params_float){ 50 | if(widget->primitive.name) free(widget->primitive.name); 51 | if(widget->primitive.params_int) free(widget->primitive.params_int); 52 | if(widget->primitive.params_float) free(widget->primitive.params_float); 53 | 54 | widget->primitive.name = NULL; 55 | widget->primitive.params_int = NULL; 56 | widget->primitive.params_float = NULL; 57 | 58 | widget->primitive.name = name; 59 | widget->primitive.params_int = params_int; 60 | widget->primitive.params_float = params_float; 61 | 62 | widget->primitive.n_params_int = n_params_int; 63 | widget->primitive.n_params_float = n_params_float; 64 | 65 | if(name && widget->wlr_texture){ 66 | wlr_texture_destroy(widget->wlr_texture); 67 | widget->wlr_texture = NULL; 68 | } 69 | 70 | wm_layout_damage_from(widget->super.wm_server->wm_layout, &widget->super, NULL); 71 | } 72 | 73 | 74 | static void wm_widget_render(struct wm_content* super, struct wm_output* output, pixman_region32_t* output_damage, struct timespec now){ 75 | struct wm_widget* widget = wm_cast(wm_widget, super); 76 | 77 | double display_x, display_y, display_w, display_h; 78 | wm_content_get_box(&widget->super, &display_x, &display_y, &display_w, &display_h); 79 | 80 | struct wlr_box box = { 81 | .x = round((display_x - output->layout_x) * output->wlr_output->scale), 82 | .y = round((display_y - output->layout_y) * output->wlr_output->scale), 83 | .width = round(display_w * output->wlr_output->scale), 84 | .height = round(display_h * output->wlr_output->scale)}; 85 | 86 | if (widget->wlr_texture){ 87 | 88 | double mask_x, mask_y, mask_w, mask_h; 89 | wm_content_get_mask(&widget->super, &mask_x, &mask_y, &mask_w, &mask_h); 90 | 91 | struct wlr_box mask = { 92 | .x = round((display_x - output->layout_x + mask_x) * output->wlr_output->scale), 93 | .y = round((display_y - output->layout_y + mask_y) * output->wlr_output->scale), 94 | .width = round(mask_w * output->wlr_output->scale), 95 | .height = round(mask_h * output->wlr_output->scale)}; 96 | 97 | double corner_radius = 98 | wm_content_get_corner_radius(&widget->super) * output->wlr_output->scale; 99 | 100 | wm_renderer_render_texture_at( 101 | output->wm_server->wm_renderer, output_damage, 102 | NULL, widget->wlr_texture, &box, 103 | wm_content_get_opacity(super), &mask, corner_radius, 104 | super->lock_enabled ? 0.0 : super->wm_server->lock_perc); 105 | }else if(widget->primitive.name){ 106 | #ifdef WM_CUSTOM_RENDERER 107 | wm_renderer_select_primitive_shader(output->wm_server->wm_renderer, widget->primitive.name); 108 | if(!wm_renderer_check_primitive_params(output->wm_server->wm_renderer, widget->primitive.n_params_int, widget->primitive.n_params_float)){ 109 | return; 110 | } 111 | wm_renderer_render_primitive(output->wm_server->wm_renderer, output_damage, &box, 112 | wm_content_get_opacity(super) * (1. - (super->lock_enabled ? 0.0 : super->wm_server->lock_perc)), 113 | widget->primitive.params_int, widget->primitive.params_float); 114 | #endif 115 | } 116 | } 117 | 118 | static void wm_widget_printf(FILE* file, struct wm_content* super){ 119 | struct wm_widget* widget = wm_cast(wm_widget, super); 120 | fprintf(file, "wm_widget (%f, %f - %f, %f)\n", widget->super.display_x, widget->super.display_y, widget->super.display_width, widget->super.display_height); 121 | } 122 | 123 | struct wm_content_vtable wm_widget_vtable = { 124 | .destroy = &wm_widget_destroy, 125 | .render = &wm_widget_render, 126 | .damage_output = NULL, 127 | .printf = &wm_widget_printf 128 | }; 129 | --------------------------------------------------------------------------------