pax_global_header00006660000000000000000000000064141567175050014525gustar00rootroot0000000000000052 comment=991c766e7598729bc2f74dd61eb4264bac6cb4ac wayvnc-0.4.1/000077500000000000000000000000001415671750500130365ustar00rootroot00000000000000wayvnc-0.4.1/.gitignore000066400000000000000000000001171415671750500150250ustar00rootroot00000000000000build subprojects *.swp .clang_complete .ycm_extra_conf.py perf.* *.pem .vimrc wayvnc-0.4.1/CONTRIBUTING.md000066400000000000000000000026041415671750500152710ustar00rootroot00000000000000# Contributing to wayvnc ## Commit Messages Please, try to write good commit messages. Do your best to follow these 7 rules, borrowed from [Chris Beams](https://chris.beams.io/posts/git-commit/): 1. Separate subject from body with a blank line 2. Limit the subject line to 50 characters 3. Capitalize the subject line 4. Do not end the subject line with a period 5. Use the imperative mood in the subject line 6. Wrap the body at 72 characters 7. Use the body to explain what and why vs. how If you wish to know why we follow these rules, please read Chris Beams' blog entry, linked above. ## Style This project follows the the [Linux kernel's style guide](https://www.kernel.org/doc/html/latest/process/coding-style.html#codingstyle) as far as coding style is concererned, with the following exceptions: * When declaring pointer variables, the asterisk (`*`) is placed on the left with the type rather than the variable name. Declaring multiple variables in the same line is not allowed. * Wrapped argument lists should not be aligned. Use two tabs instead. There is a lot of code that uses aligned argument lists in the project, but I have come to the conclusion that these alignments are not very nice to maintain. ## No Brown M&Ms All pull requests must contain the following sentence in the description: I have read and understood CONTRIBUTING.md and its associated documents. wayvnc-0.4.1/COPYING000066400000000000000000000013431415671750500140720ustar00rootroot00000000000000Copyright (c) 2019 - 2020 Andri Yngvason Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. wayvnc-0.4.1/FAQ.md000066400000000000000000000015251415671750500137720ustar00rootroot00000000000000# FAQ **Q: How can I run wayvnc in headless mode/over an SSH session?** A: Set the environment variables `WLR_BACKENDS=headless` and `WLR_LIBINPUT_NO_DEVICES=1` before starting sway, then run wayvnc as normal. **Q: How can I pass my mod-key from Sway to the remote desktop session?** A: Create an almost empty mode in your sway config. Example: ``` mode passthrough { bindsym $mod+Pause mode default } bindsym $mod+Pause mode passthrough ``` This makes it so that when you press $mod+Pause, all keybindings, except the one to switch back, are disabled. **Q: Not all symbols show up when I'm typing. What can I do to fix this?** A: Try setting the keyboard layout in wayvnc to the one that most closely matches the keyboard layout that you're using on the client side. An exact layout isn't needed, just one that has all the symbols that you use. wayvnc-0.4.1/README.md000066400000000000000000000062441415671750500143230ustar00rootroot00000000000000# wayvnc ## Introduction This is a VNC server for wlroots based Wayland compositors. It attaches to a running Wayland session, creates virtual input devices and exposes a single display via the RFB protocol. The Wayland session may be a headless one, so it is also possible to run wayvnc without a physical display attached. Please check the [FAQ](FAQ.md) for answers to common questions. For further support, join the #wayvnc IRC channel on freenode, or ask your questions on the GitHub [discussion forum](https://github.com/any1/wayvnc/discussions) for the project. ## Installing ``` # archlinux yay -S wayvnc # FreeBSD pkg install wayvnc # Fedora dnf install wayvnc # openSUSE Tumbleweed zypper install wayvnc # Void Linux xbps-install wayvnc ``` ## Building ### Runtime Dependencies * aml * drm * gbm (optional) * libxkbcommon * neatvnc * pam (optional) * pixman ### Build Dependencies * GCC * meson * ninja * pkg-config #### For archlinux ``` pacman -S base-devel libglvnd libxkbcommon pixman gnutls ``` #### For fedora 31 ``` dnf install -y meson gcc ninja-build pkg-config egl-wayland egl-wayland-devel \ mesa-libEGL-devel mesa-libEGL libwayland-egl libglvnd-devel \ libglvnd-core-devel libglvnd mesa-libGLES-devel mesa-libGLES \ libxkbcommon-devel libxkbcommon libwayland-client libwayland \ wayland-devel gnutls-devel ``` The easiest way to satisfy the neatvnc and aml dependencies is to link to them in the subprojects directory: ``` git clone https://github.com/any1/wayvnc.git git clone https://github.com/any1/neatvnc.git git clone https://github.com/any1/aml.git mkdir wayvnc/subprojects cd wayvnc/subprojects ln -s ../../neatvnc . ln -s ../../aml . cd - mkdir neatvnc/subprojects cd neatvnc/subprojects ln -s ../../aml . cd - meson build ninja -C build ``` ## Running Wayvnc can be run from the build directory like so: ``` ./build/wayvnc ``` :radioactive: The server only accepts connections from localhost by default. To accept connections via any interface, set the address to `0.0.0.0` like this: ``` ./build/wayvnc 0.0.0.0 ``` :warning: Do not do this on a public network or the internet without user authentication enabled. The best way to protect your VNC connection is to use SSH tunneling while listening on localhost, but users can also be authenticated when connecting to Wayvnc. ### Encryption & Authentication You'll need a private X509 key and a certificate. A self signed key with a certificate can be generated like so: ``` openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \ -keyout key.pem -out cert.pem -subj /CN=localhost \ -addext subjectAltName=DNS:localhost,DNS:localhost,IP:127.0.0.1 ``` Replace `localhost` and `127.0.0.1` in the command above with your public facing host name and IP address, respectively, or just keep them as is if you're testing locally. Create a config with the authentication info and load it using the `--config` command line option or place it at the default location `$HOME/.config/wayvnc/config`. ``` address=0.0.0.0 enable_auth=true username=luser password=p455w0rd private_key_file=/path/to/key.pem certificate_file=/path/to/cert.pem ``` ## Compatible Software See https://github.com/any1/neatvnc#client-compatibility wayvnc-0.4.1/include/000077500000000000000000000000001415671750500144615ustar00rootroot00000000000000wayvnc-0.4.1/include/buffer.h000066400000000000000000000047701415671750500161130ustar00rootroot00000000000000/* * Copyright (c) 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #pragma once #include "sys/queue.h" #include "config.h" #include #include #include #include struct wl_buffer; struct gbm_bo; enum wv_buffer_type { WV_BUFFER_UNSPEC = 0, WV_BUFFER_SHM, #ifdef ENABLE_SCREENCOPY_DMABUF WV_BUFFER_DMABUF, #endif }; struct wv_buffer { enum wv_buffer_type type; TAILQ_ENTRY(wv_buffer) link; struct wl_buffer* wl_buffer; void* pixels; size_t size; int width, height, stride; uint32_t format; bool y_inverted; struct pixman_region16 damage; /* The following is only applicable to DMABUF */ struct gbm_bo* bo; void* bo_map_handle; }; TAILQ_HEAD(wv_buffer_queue, wv_buffer); struct wv_buffer_pool { struct wv_buffer_queue queue; enum wv_buffer_type type; int width, height, stride; uint32_t format; }; enum wv_buffer_type wv_buffer_get_available_types(void); struct wv_buffer* wv_buffer_create(enum wv_buffer_type, int width, int height, int stride, uint32_t fourcc); void wv_buffer_destroy(struct wv_buffer* self); int wv_buffer_map(struct wv_buffer* self); void wv_buffer_unmap(struct wv_buffer* self); void wv_buffer_damage_rect(struct wv_buffer* self, int x, int y, int width, int height); void wv_buffer_damage_whole(struct wv_buffer* self); void wv_buffer_damage_clear(struct wv_buffer* self); struct wv_buffer_pool* wv_buffer_pool_create(enum wv_buffer_type, int width, int height, int stride, uint32_t format); void wv_buffer_pool_destroy(struct wv_buffer_pool* pool); void wv_buffer_pool_resize(struct wv_buffer_pool* pool, enum wv_buffer_type, int width, int height, int stride, uint32_t format); struct wv_buffer* wv_buffer_pool_acquire(struct wv_buffer_pool* pool); void wv_buffer_pool_release(struct wv_buffer_pool* pool, struct wv_buffer* buffer); wayvnc-0.4.1/include/cfg.h000066400000000000000000000025701415671750500153750ustar00rootroot00000000000000/* * Copyright (c) 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #pragma once #include #include #define X_CFG_LIST \ X(bool, enable_auth) \ X(string, private_key_file) \ X(string, certificate_file) \ X(string, username) \ X(string, password) \ X(string, address) \ X(uint, port) \ X(bool, enable_pam) \ X(string, xkb_rules) \ X(string, xkb_model) \ X(string, xkb_layout) \ X(string, xkb_variant) \ X(string, xkb_options) \ struct cfg { #define string char* #define uint uint32_t #define X(type, name) type name; X_CFG_LIST #undef X #undef uint #undef string }; int cfg_load(struct cfg* self, const char* path); void cfg_destroy(struct cfg* self); wayvnc-0.4.1/include/damage-refinery.h000066400000000000000000000023321415671750500176710ustar00rootroot00000000000000/* * Copyright (c) 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #pragma once #include struct pixman_region16; struct wv_buffer; struct damage_refinery { uint32_t* hashes; uint32_t width; uint32_t height; }; int damage_refinery_init(struct damage_refinery* self, uint32_t width, uint32_t height); void damage_refinery_destroy(struct damage_refinery* self); void damage_refine(struct damage_refinery* self, struct pixman_region16* refined, struct pixman_region16* hint, const struct wv_buffer* buffer); wayvnc-0.4.1/include/data-control.h000066400000000000000000000027311415671750500172240ustar00rootroot00000000000000/* * Copyright (c) 2020 Scott Moreau * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #pragma once #include #include "wlr-data-control-unstable-v1.h" struct data_control { struct wl_display* wl_display; struct nvnc* server; struct zwlr_data_control_manager_v1* manager; struct zwlr_data_control_device_v1* device; struct zwlr_data_control_source_v1* selection; struct zwlr_data_control_source_v1* primary_selection; struct zwlr_data_control_offer_v1* offer; const char* mime_type; char* cb_data; size_t cb_len; }; void data_control_init(struct data_control* self, struct wl_display* wl_display, struct nvnc* server, struct wl_seat* seat); void data_control_destroy(struct data_control* self); void data_control_to_clipboard(struct data_control* self, const char* text, size_t len); wayvnc-0.4.1/include/intset.h000066400000000000000000000022351415671750500161420ustar00rootroot00000000000000/* * Copyright (c) 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #pragma once #include #include #include struct intset { size_t cap; size_t len; int32_t* storage; }; int intset_init(struct intset* self, size_t cap); void intset_destroy(struct intset* self); int intset_set(struct intset* self, int32_t value); void intset_clear(struct intset* self, int32_t value); bool intset_is_set(const struct intset* self, int32_t value); wayvnc-0.4.1/include/keyboard.h000066400000000000000000000027521415671750500164400ustar00rootroot00000000000000/* * Copyright (c) 2019 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #pragma once #include #include #include #include "intset.h" struct zwp_virtual_keyboard_v1; struct table_entry; struct keyboard { struct zwp_virtual_keyboard_v1* virtual_keyboard; struct xkb_context* context; struct xkb_keymap* keymap; struct xkb_state* state; size_t lookup_table_size; size_t lookup_table_length; struct table_entry* lookup_table; struct intset key_state; }; int keyboard_init(struct keyboard* self, const struct xkb_rule_names* rule_names); void keyboard_destroy(struct keyboard* self); void keyboard_feed(struct keyboard* self, xkb_keysym_t symbol, bool is_pressed); void keyboard_feed_code(struct keyboard* self, xkb_keycode_t code, bool is_pressed); wayvnc-0.4.1/include/logging.h000066400000000000000000000020431415671750500162570ustar00rootroot00000000000000/* * Copyright (c) 2019 - 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #pragma once #include #ifdef NDEBUG #define log_debug(...) #else #define log_debug(...) fprintf(stderr, "DEBUG: " __VA_ARGS__) #endif #define log_error(...) fprintf(stderr, "ERROR: " __VA_ARGS__) #define log_warning(...) fprintf(stderr, "Warning: " __VA_ARGS__) wayvnc-0.4.1/include/murmurhash.h000066400000000000000000000027071415671750500170330ustar00rootroot00000000000000/* * The MIT License (MIT) * * Copyright (c) 2014 Joseph Werle * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef MURMURHASH_H #define MURMURHASH_H 1 #include #define MURMURHASH_VERSION "0.0.3" #ifdef __cplusplus extern "C" { #endif /** * Returns a murmur hash of `key' based on `seed' * using the MurmurHash3 algorithm */ uint32_t murmurhash (const char *, uint32_t, uint32_t); #ifdef __cplusplus } #endif #endif wayvnc-0.4.1/include/output.h000066400000000000000000000046501415671750500161770ustar00rootroot00000000000000/* * Copyright (c) 2019 - 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #pragma once #include #include #include struct zxdg_output_v1; struct output { struct wl_output* wl_output; struct zxdg_output_v1* xdg_output; struct wl_list link; uint32_t id; uint32_t width; uint32_t height; uint32_t x; uint32_t y; enum wl_output_transform transform; char make[256]; char model[256]; char name[256]; char description[256]; bool is_dimension_changed; bool is_transform_changed; void (*on_dimension_change)(struct output*); void (*on_transform_change)(struct output*); void* userdata; }; struct output* output_new(struct wl_output* wl_output, uint32_t id); void output_destroy(struct output* output); void output_set_xdg_output(struct output* output, struct zxdg_output_v1* xdg_output); void output_list_destroy(struct wl_list* list); struct output* output_find_by_id(struct wl_list* list, uint32_t id); struct output* output_find_by_name(struct wl_list* list, const char* name); struct output* output_first(struct wl_list* list); uint32_t output_get_transformed_width(const struct output* self); uint32_t output_get_transformed_height(const struct output* self); void output_transform_coord(const struct output* self, uint32_t src_x, uint32_t src_y, uint32_t* dst_x, uint32_t* dst_y); void output_transform_box_coord(const struct output* self, uint32_t src_x0, uint32_t src_y0, uint32_t src_x1, uint32_t src_y1, uint32_t* dst_x0, uint32_t* dst_y0, uint32_t* dst_x1, uint32_t* dst_y1); wayvnc-0.4.1/include/pam_auth.h000066400000000000000000000015471415671750500164370ustar00rootroot00000000000000/* * Copyright (c) 2020 Nicholas Sica * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #pragma once #include bool pam_auth(const char* username, const char* password); wayvnc-0.4.1/include/pixels.h000066400000000000000000000020461415671750500161400ustar00rootroot00000000000000/* * Copyright (c) 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #pragma once #include #include #include #include enum wl_shm_format fourcc_to_wl_shm(uint32_t in); bool fourcc_to_pixman_fmt(pixman_format_code_t* dst, uint32_t src); uint32_t fourcc_from_wl_shm(enum wl_shm_format in); wayvnc-0.4.1/include/pixman-renderer.h000066400000000000000000000020001415671750500177220ustar00rootroot00000000000000/* * Copyright (c) 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #pragma once #include struct nvnc_fb; struct wv_buffer; struct pixman_region16; void wv_pixman_render(struct nvnc_fb* dst, const struct wv_buffer* src, enum wl_output_transform transform, struct pixman_region16* damage); wayvnc-0.4.1/include/pointer.h000066400000000000000000000024001415671750500163060ustar00rootroot00000000000000/* * Copyright (c) 2019 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #pragma once #include #include #include "wlr-virtual-pointer-unstable-v1.h" #include "output.h" struct pointer { struct nvnc* vnc; struct zwlr_virtual_pointer_v1* pointer; enum nvnc_button_mask current_mask; uint32_t current_x; uint32_t current_y; const struct output* output; }; int pointer_init(struct pointer* self); void pointer_destroy(struct pointer* self); void pointer_set(struct pointer* self, uint32_t x, uint32_t y, enum nvnc_button_mask button_mask); wayvnc-0.4.1/include/screencopy.h000066400000000000000000000040771415671750500170140ustar00rootroot00000000000000/* * Copyright (c) 2019 - 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #pragma once #include #include "wlr-screencopy-unstable-v1.h" #include "smooth.h" #include "buffer.h" struct zwlr_screencopy_manager_v1; struct zwlr_screencopy_frame_v1; struct wl_output; struct wl_buffer; struct wl_shm; struct aml_timer; struct renderer; enum screencopy_status { SCREENCOPY_STOPPED = 0, SCREENCOPY_IN_PROGRESS, SCREENCOPY_FAILED, SCREENCOPY_FATAL, SCREENCOPY_DONE, }; struct screencopy { enum screencopy_status status; struct wv_buffer_pool* pool; struct wv_buffer* front; struct wv_buffer* back; struct zwlr_screencopy_manager_v1* manager; struct zwlr_screencopy_frame_v1* frame; void* userdata; void (*on_done)(struct screencopy*); uint64_t last_time; uint64_t start_time; struct aml_timer* timer; struct smooth delay_smoother; double delay; bool is_immediate_copy; bool overlay_cursor; struct wl_output* wl_output; uint32_t wl_shm_width, wl_shm_height, wl_shm_stride; enum wl_shm_format wl_shm_format; bool have_linux_dmabuf; uint32_t dmabuf_width, dmabuf_height; uint32_t fourcc; double rate_limit; }; void screencopy_init(struct screencopy* self); void screencopy_destroy(struct screencopy* self); int screencopy_start(struct screencopy* self); int screencopy_start_immediate(struct screencopy* self); void screencopy_stop(struct screencopy* self); wayvnc-0.4.1/include/seat.h000066400000000000000000000024141415671750500155670ustar00rootroot00000000000000/* * Copyright (c) 2019 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #pragma once #include #include struct seat { struct wl_seat* wl_seat; struct wl_list link; uint32_t id; uint32_t capabilities; char name[256]; }; struct seat* seat_new(struct wl_seat* wl_seat, uint32_t id); void seat_destroy(struct seat* self); void seat_list_destroy(struct wl_list* list); struct seat* seat_find_by_name(struct wl_list* list, const char* name); struct seat* seat_find_by_id(struct wl_list* list, uint32_t id); struct seat* seat_first(struct wl_list* list); wayvnc-0.4.1/include/shm.h000066400000000000000000000015221415671750500154210ustar00rootroot00000000000000/* * Copyright (c) 2019 - 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #pragma once #include int shm_alloc_fd(size_t size); wayvnc-0.4.1/include/smooth.h000066400000000000000000000022721415671750500161460ustar00rootroot00000000000000/* * Copyright (c) 2019 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #pragma once #include /* * This is an exponential smoothing filter with a time constant. * * The time constant must be set prior to applying the filter. It is, rougly * speaking, the response time of the filter. * * See: https://en.wikipedia.org/wiki/Exponential_smoothing */ struct smooth { double time_constant; uint64_t last_time; double last_result; }; double smooth(struct smooth* self, double input); wayvnc-0.4.1/include/strlcpy.h000066400000000000000000000015541415671750500163370ustar00rootroot00000000000000/* * Copyright (c) 2019 - 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #pragma once #include size_t strlcpy(char *dst, const char *src, size_t siz); wayvnc-0.4.1/include/time-util.h000066400000000000000000000026601415671750500165470ustar00rootroot00000000000000/* * Copyright (c) 2019 - 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #pragma once #include #include static inline uint64_t timespec_to_us(const struct timespec* ts) { return (uint64_t)ts->tv_sec * UINT64_C(1000000) + (uint64_t)ts->tv_nsec / UINT64_C(1000); } static inline uint64_t timespec_to_ms(const struct timespec* ts) { return (uint64_t)ts->tv_sec * UINT64_C(1000) + (uint64_t)ts->tv_nsec / UINT64_C(1000000); } static inline uint64_t gettime_us(void) { struct timespec ts = { 0 }; clock_gettime(CLOCK_MONOTONIC, &ts); return timespec_to_us(&ts); } static inline uint64_t gettime_ms(void) { struct timespec ts = { 0 }; clock_gettime(CLOCK_MONOTONIC, &ts); return timespec_to_ms(&ts); } wayvnc-0.4.1/include/transform-util.h000066400000000000000000000024511415671750500176220ustar00rootroot00000000000000/* * Copyright (c) 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #pragma once #include #include void wv_region_transform(struct pixman_region16 *dst, struct pixman_region16 *src, enum wl_output_transform transform, int width, int height); void wv_pixman_transform_from_wl_output_transform(pixman_transform_t* dst, enum wl_output_transform src, int width, int height); enum wl_output_transform wv_output_transform_invert(enum wl_output_transform tr); enum wl_output_transform wv_output_transform_compose( enum wl_output_transform tr_a, enum wl_output_transform tr_b); wayvnc-0.4.1/include/tst.h000066400000000000000000000127251415671750500154530ustar00rootroot00000000000000/* * Copyright (c) 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #pragma once #include #include #include #include #define XSTR(s) STR(s) #define STR(s) #s #define ASSERT_TRUE(expr) do { \ if (!(expr)) { \ fprintf(stderr, "FAILED " XSTR(__LINE__) ": Expected " XSTR(expr) " to be true\n"); \ return 1; \ } \ } while(0) #define ASSERT_FALSE(expr) do { \ if (expr) { \ fprintf(stderr, "FAILED " XSTR(__LINE__) ": Expected " XSTR(expr) " to be false\n"); \ return 1; \ } \ } while(0) #define TST_ASSERT_EQ_(value, expr, type, fmt) do { \ type expr_ = (expr); \ if (expr_ != (value)) { \ fprintf(stderr, "FAILED " XSTR(__LINE__) ": Expected " XSTR(expr) " to be equal to " XSTR(value) "; was " fmt "\n", \ expr_); \ return 1; \ } \ } while(0) #define ASSERT_INT_EQ(value, expr) TST_ASSERT_EQ_(value, expr, int, "%d") #define ASSERT_UINT_EQ(value, expr) TST_ASSERT_EQ_(value, expr, unsigned int, "%u") #define ASSERT_INT32_EQ(value, expr) TST_ASSERT_EQ_(value, expr, int32_t, "%" PRIi32) #define ASSERT_UINT32_EQ(value, expr) TST_ASSERT_EQ_(value, expr, uint32_t, "%" PRIu32) #define ASSERT_DOUBLE_EQ(value, expr) TST_ASSERT_EQ_(value, expr, double, "%f") #define ASSERT_PTR_EQ(value, expr) TST_ASSERT_EQ_(value, expr, void*, "%p") #define TST_ASSERT_GT_(value, expr, type, fmt) do { \ type expr_ = (expr); \ if (!(expr_ > (value))) { \ fprintf(stderr, "FAILED " XSTR(__LINE__) ": Expected " XSTR(expr) " to be greater than " XSTR(value) "; was " fmt "\n", \ expr_); \ return 1; \ } \ } while(0) #define ASSERT_INT_GT(value, expr) TST_ASSERT_GT_(value, expr, int, "%d") #define ASSERT_UINT_GT(value, expr) TST_ASSERT_GT_(value, expr, unsigned int, "%u") #define ASSERT_INT32_GT(value, expr) TST_ASSERT_GT_(value, expr, int32_t, "%" PRIi32) #define ASSERT_UINT32_GT(value, expr) TST_ASSERT_GT_(value, expr, uint32_t, "%" PRIu32) #define ASSERT_DOUBLE_GT(value, expr) TST_ASSERT_GT_(value, expr, double, "%f") #define TST_ASSERT_GE_(value, expr, type, fmt) do { \ type expr_ = (expr); \ if (!(expr_ >= (value))) { \ fprintf(stderr, "FAILED " XSTR(__LINE__) ": Expected " XSTR(expr) " to be greater than or equal to " XSTR(value) "; was " fmt "\n", \ expr_); \ return 1; \ } \ } while(0) #define ASSERT_INT_GE(value, expr) TST_ASSERT_GE_(value, expr, int, "%d") #define ASSERT_UINT_GE(value, expr) TST_ASSERT_GE_(value, expr, unsigned int, "%u") #define ASSERT_INT32_GE(value, expr) TST_ASSERT_GE_(value, expr, int32_t, "%" PRIi32) #define ASSERT_UINT32_GE(value, expr) TST_ASSERT_GE_(value, expr, uint32_t, "%" PRIu32) #define ASSERT_DOUBLE_GE(value, expr) TST_ASSERT_GE_(value, expr, double, "%f") #define TST_ASSERT_LT_(value, expr, type, fmt) do { \ type expr_ = (expr); \ if (!(expr_ < (value))) { \ fprintf(stderr, "FAILED " XSTR(__LINE__) ": Expected " XSTR(expr) " to be less than " XSTR(value) "; was " fmt "\n", \ expr_); \ return 1; \ } \ } while(0) #define ASSERT_INT_LT(value, expr) TST_ASSERT_LT_(value, expr, int, "%d") #define ASSERT_UINT_LT(value, expr) TST_ASSERT_LT_(value, expr, unsigned int, "%u") #define ASSERT_INT32_LT(value, expr) TST_ASSERT_LT_(value, expr, int32_t, "%" PRIi32) #define ASSERT_UINT32_LT(value, expr) TST_ASSERT_LT_(value, expr, uint32_t, "%" PRIu32) #define ASSERT_DOUBLE_LT(value, expr) TST_ASSERT_LT_(value, expr, double, "%f") #define TST_ASSERT_LE_(value, expr, type, fmt) do { \ type expr_ = (expr); \ if (!(expr_ <= (value))) { \ fprintf(stderr, "FAILED " XSTR(__LINE__) ": Expected " XSTR(expr) " to be less than or equal to " XSTR(value) "; was " fmt "\n", \ expr_); \ return 1; \ } \ } while(0) #define ASSERT_INT_LE(value, expr) TST_ASSERT_LE_(value, expr, int, "%d") #define ASSERT_UINT_LE(value, expr) TST_ASSERT_LE_(value, expr, unsigned int, "%u") #define ASSERT_INT32_LE(value, expr) TST_ASSERT_LE_(value, expr, int32_t, "%" PRIi32) #define ASSERT_UINT32_LE(value, expr) TST_ASSERT_LE_(value, expr, uint32_t, "%" PRIu32) #define ASSERT_DOUBLE_LE(value, expr) TST_ASSERT_LE_(value, expr, double, "%f") #define ASSERT_STR_EQ(value, expr) do { \ const char* expr_ = (expr); \ if (strcmp(expr_, (value)) != 0) { \ fprintf(stderr, "FAILED " XSTR(__LINE__) ": Expected " XSTR(expr) " to be " XSTR(value) "; was \"%s\"\n", \ expr_); \ return 1; \ } \ } while(0) #define ASSERT_NEQ(value, expr) do { \ if ((expr) != (value)) { \ fprintf(stderr, "FAILED " XSTR(__LINE__) ": Expected " XSTR(expr) " to NOT be " XSTR(value) "\n"); \ return 1; \ } \ } while(0) #define ASSERT_STR_NEQ(value, expr) do { \ if (strcmp((expr), (value)) == 0) { \ fprintf(stderr, "FAILED " XSTR(__LINE__) ": Expected " XSTR(expr) " to NOT be " XSTR(value) "\n"); \ return 1; \ } \ } while(0) #define RUN_TEST(test) do { \ if(!(test())) \ fprintf(stderr, XSTR(test) " passed\n"); \ else \ r = 1; \ } while(0); wayvnc-0.4.1/include/usdt.h000066400000000000000000000020331415671750500156070ustar00rootroot00000000000000/* * Copyright (c) 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #pragma once #include "config.h" #ifdef HAVE_USDT #include #else #define DTRACE_PROBE(...) #define DTRACE_PROBE1(...) #define DTRACE_PROBE2(...) #define DTRACE_PROBE3(...) #define DTRACE_PROBE4(...) #define DTRACE_PROBE5(...) #define DTRACE_PROBE6(...) #endif wayvnc-0.4.1/include/util.h000066400000000000000000000015051415671750500156100ustar00rootroot00000000000000/* * Copyright (c) 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #pragma once #define UDIV_UP(a, b) (((a) + (b) - 1) / (b)) wayvnc-0.4.1/meson.build000066400000000000000000000067131415671750500152070ustar00rootroot00000000000000project( 'wayvnc', 'c', version: '0.4.1', license: 'ISC', default_options: [ 'c_std=gnu11', ], ) buildtype = get_option('buildtype') host_system = host_machine.system() prefix = get_option('prefix') c_args = [ '-DPROJECT_VERSION="@0@"'.format(meson.project_version()), '-D_GNU_SOURCE', ] git = find_program('git', native: true, required: false) if git.found() git_describe = run_command([git, 'describe', '--tags', '--long']) git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD']) if git_describe.returncode() == 0 and git_branch.returncode() == 0 c_args += '-DGIT_VERSION="@0@ (@1@)"'.format( git_describe.stdout().strip(), git_branch.stdout().strip(), ) endif endif if buildtype != 'debug' and buildtype != 'debugoptimized' c_args += '-DNDEBUG' endif add_project_arguments(c_args, language: 'c') cc = meson.get_compiler('c') libm = cc.find_library('m', required: false) librt = cc.find_library('rt', required: false) libpam = cc.find_library('pam', required: get_option('pam')) pixman = dependency('pixman-1') gbm = dependency('gbm', required: get_option('screencopy-dmabuf')) drm = dependency('libdrm') xkbcommon = dependency('xkbcommon', version: '>=1.0.0') wayland_client = dependency('wayland-client') neatvnc_version = '>=0.4.0' neatvnc_project = subproject( 'neatvnc', required: false, version: neatvnc_version, ) aml_project = subproject('aml', required: false) if aml_project.found() aml = aml_project.get_variable('aml_dep') else aml = dependency('aml') endif if neatvnc_project.found() neatvnc = neatvnc_project.get_variable('neatvnc_dep') else neatvnc = dependency('neatvnc', version: neatvnc_version) endif inc = include_directories('include') subdir('protocols') sources = [ 'src/main.c', 'src/strlcpy.c', 'src/shm.c', 'src/screencopy.c', 'src/data-control.c', 'src/output.c', 'src/pointer.c', 'src/keyboard.c', 'src/seat.c', 'src/smooth.c', 'src/cfg.c', 'src/intset.c', 'src/buffer.c', 'src/pixels.c', 'src/pixman-renderer.c', 'src/transform-util.c', 'src/damage-refinery.c', 'src/murmurhash.c', ] dependencies = [ libm, librt, pixman, aml, gbm, drm, wayland_client, neatvnc, xkbcommon, client_protos, ] config = configuration_data() config.set('PREFIX', '"' + prefix + '"') if host_system == 'linux' and get_option('systemtap') and cc.has_header('sys/sdt.h') config.set('HAVE_USDT', true) endif if cc.has_function('memfd_create') config.set('HAVE_MEMFD', true) config.set('HAVE_MEMFD_CREATE', true) elif cc.has_function('SYS_memfd_create', prefix : '#include ') config.set('HAVE_MEMFD', true) endif if gbm.found() and not get_option('screencopy-dmabuf').disabled() config.set('ENABLE_SCREENCOPY_DMABUF', true) endif if libpam.found() dependencies += libpam sources += 'src/pam_auth.c' config.set('ENABLE_PAM', true) endif configure_file( output: 'config.h', configuration: config, ) executable( 'wayvnc', sources, dependencies: dependencies, include_directories: inc, install: true, ) scdoc = dependency('scdoc', native: true, required: get_option('man-pages')) if scdoc.found() scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true) sh = find_program('sh', native: true) mandir = get_option('mandir') input = 'wayvnc.scd' output = 'wayvnc.1' custom_target( output, input: input, output: output, command: [ sh, '-c', '@0@ <@INPUT@ >@1@'.format(scdoc_prog.path(), output) ], install: true, install_dir: '@0@/man1'.format(mandir) ) endif wayvnc-0.4.1/meson_options.txt000066400000000000000000000006251415671750500164760ustar00rootroot00000000000000option('screencopy-dmabuf', type: 'feature', value: 'disabled', description: 'Enable GPU-side screencopy (experimental)') option('pam', type: 'feature', value: 'auto', description: 'Enable PAM authentication') option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages') option('systemtap', type: 'boolean', value: false, description: 'Enable tracing using sdt') wayvnc-0.4.1/protocols/000077500000000000000000000000001415671750500150625ustar00rootroot00000000000000wayvnc-0.4.1/protocols/linux-dmabuf-unstable-v1.xml000066400000000000000000000427261415671750500223510ustar00rootroot00000000000000 Copyright © 2014, 2015 Collabora, Ltd. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Following the interfaces from: https://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_image_dma_buf_import.txt https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import_modifiers.txt and the Linux DRM sub-system's AddFb2 ioctl. This interface offers ways to create generic dmabuf-based wl_buffers. Immediately after a client binds to this interface, the set of supported formats and format modifiers is sent with 'format' and 'modifier' events. The following are required from clients: - Clients must ensure that either all data in the dma-buf is coherent for all subsequent read access or that coherency is correctly handled by the underlying kernel-side dma-buf implementation. - Don't make any more attachments after sending the buffer to the compositor. Making more attachments later increases the risk of the compositor not being able to use (re-import) an existing dmabuf-based wl_buffer. The underlying graphics stack must ensure the following: - The dmabuf file descriptors relayed to the server will stay valid for the whole lifetime of the wl_buffer. This means the server may at any time use those fds to import the dmabuf into any kernel sub-system that might accept it. To create a wl_buffer from one or more dmabufs, a client creates a zwp_linux_dmabuf_params_v1 object with a zwp_linux_dmabuf_v1.create_params request. All planes required by the intended format are added with the 'add' request. Finally, a 'create' or 'create_immed' request is issued, which has the following outcome depending on the import success. The 'create' request, - on success, triggers a 'created' event which provides the final wl_buffer to the client. - on failure, triggers a 'failed' event to convey that the server cannot use the dmabufs received from the client. For the 'create_immed' request, - on success, the server immediately imports the added dmabufs to create a wl_buffer. No event is sent from the server in this case. - on failure, the server can choose to either: - terminate the client by raising a fatal error. - mark the wl_buffer as failed, and send a 'failed' event to the client. If the client uses a failed wl_buffer as an argument to any request, the behaviour is compositor implementation-defined. Warning! The protocol described in this file is experimental and backward incompatible changes may be made. Backward compatible changes may be added together with the corresponding interface version bump. Backward incompatible changes are done by bumping the version number in the protocol and interface names and resetting the interface version. Once the protocol is to be declared stable, the 'z' prefix and the version number in the protocol and interface names are removed and the interface version number is reset. Objects created through this interface, especially wl_buffers, will remain valid. This temporary object is used to collect multiple dmabuf handles into a single batch to create a wl_buffer. It can only be used once and should be destroyed after a 'created' or 'failed' event has been received. This event advertises one buffer format that the server supports. All the supported formats are advertised once when the client binds to this interface. A roundtrip after binding guarantees that the client has received all supported formats. For the definition of the format codes, see the zwp_linux_buffer_params_v1::create request. Warning: the 'format' event is likely to be deprecated and replaced with the 'modifier' event introduced in zwp_linux_dmabuf_v1 version 3, described below. Please refrain from using the information received from this event. This event advertises the formats that the server supports, along with the modifiers supported for each format. All the supported modifiers for all the supported formats are advertised once when the client binds to this interface. A roundtrip after binding guarantees that the client has received all supported format-modifier pairs. For legacy support, DRM_FORMAT_MOD_INVALID (that is, modifier_hi == 0x00ffffff and modifier_lo == 0xffffffff) is allowed in this event. It indicates that the server can support the format with an implicit modifier. When a plane has DRM_FORMAT_MOD_INVALID as its modifier, it is as if no explicit modifier is specified. The effective modifier will be derived from the dmabuf. For the definition of the format and modifier codes, see the zwp_linux_buffer_params_v1::create and zwp_linux_buffer_params_v1::add requests. This temporary object is a collection of dmabufs and other parameters that together form a single logical buffer. The temporary object may eventually create one wl_buffer unless cancelled by destroying it before requesting 'create'. Single-planar formats only require one dmabuf, however multi-planar formats may require more than one dmabuf. For all formats, an 'add' request must be called once per plane (even if the underlying dmabuf fd is identical). You must use consecutive plane indices ('plane_idx' argument for 'add') from zero to the number of planes used by the drm_fourcc format code. All planes required by the format must be given exactly once, but can be given in any order. Each plane index can be set only once. Cleans up the temporary data sent to the server for dmabuf-based wl_buffer creation. This request adds one dmabuf to the set in this zwp_linux_buffer_params_v1. The 64-bit unsigned value combined from modifier_hi and modifier_lo is the dmabuf layout modifier. DRM AddFB2 ioctl calls this the fb modifier, which is defined in drm_mode.h of Linux UAPI. This is an opaque token. Drivers use this token to express tiling, compression, etc. driver-specific modifications to the base format defined by the DRM fourcc code. Warning: It should be an error if the format/modifier pair was not advertised with the modifier event. This is not enforced yet because some implementations always accept DRM_FORMAT_MOD_INVALID. Also version 2 of this protocol does not have the modifier event. This request raises the PLANE_IDX error if plane_idx is too large. The error PLANE_SET is raised if attempting to set a plane that was already set. This asks for creation of a wl_buffer from the added dmabuf buffers. The wl_buffer is not created immediately but returned via the 'created' event if the dmabuf sharing succeeds. The sharing may fail at runtime for reasons a client cannot predict, in which case the 'failed' event is triggered. The 'format' argument is a DRM_FORMAT code, as defined by the libdrm's drm_fourcc.h. The Linux kernel's DRM sub-system is the authoritative source on how the format codes should work. The 'flags' is a bitfield of the flags defined in enum "flags". 'y_invert' means the that the image needs to be y-flipped. Flag 'interlaced' means that the frame in the buffer is not progressive as usual, but interlaced. An interlaced buffer as supported here must always contain both top and bottom fields. The top field always begins on the first pixel row. The temporal ordering between the two fields is top field first, unless 'bottom_first' is specified. It is undefined whether 'bottom_first' is ignored if 'interlaced' is not set. This protocol does not convey any information about field rate, duration, or timing, other than the relative ordering between the two fields in one buffer. A compositor may have to estimate the intended field rate from the incoming buffer rate. It is undefined whether the time of receiving wl_surface.commit with a new buffer attached, applying the wl_surface state, wl_surface.frame callback trigger, presentation, or any other point in the compositor cycle is used to measure the frame or field times. There is no support for detecting missed or late frames/fields/buffers either, and there is no support whatsoever for cooperating with interlaced compositor output. The composited image quality resulting from the use of interlaced buffers is explicitly undefined. A compositor may use elaborate hardware features or software to deinterlace and create progressive output frames from a sequence of interlaced input buffers, or it may produce substandard image quality. However, compositors that cannot guarantee reasonable image quality in all cases are recommended to just reject all interlaced buffers. Any argument errors, including non-positive width or height, mismatch between the number of planes and the format, bad format, bad offset or stride, may be indicated by fatal protocol errors: INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS, OUT_OF_BOUNDS. Dmabuf import errors in the server that are not obvious client bugs are returned via the 'failed' event as non-fatal. This allows attempting dmabuf sharing and falling back in the client if it fails. This request can be sent only once in the object's lifetime, after which the only legal request is destroy. This object should be destroyed after issuing a 'create' request. Attempting to use this object after issuing 'create' raises ALREADY_USED protocol error. It is not mandatory to issue 'create'. If a client wants to cancel the buffer creation, it can just destroy this object. This event indicates that the attempted buffer creation was successful. It provides the new wl_buffer referencing the dmabuf(s). Upon receiving this event, the client should destroy the zlinux_dmabuf_params object. This event indicates that the attempted buffer creation has failed. It usually means that one of the dmabuf constraints has not been fulfilled. Upon receiving this event, the client should destroy the zlinux_buffer_params object. This asks for immediate creation of a wl_buffer by importing the added dmabufs. In case of import success, no event is sent from the server, and the wl_buffer is ready to be used by the client. Upon import failure, either of the following may happen, as seen fit by the implementation: - the client is terminated with one of the following fatal protocol errors: - INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS, OUT_OF_BOUNDS, in case of argument errors such as mismatch between the number of planes and the format, bad format, non-positive width or height, or bad offset or stride. - INVALID_WL_BUFFER, in case the cause for failure is unknown or plaform specific. - the server creates an invalid wl_buffer, marks it as failed and sends a 'failed' event to the client. The result of using this invalid wl_buffer as an argument in any request by the client is defined by the compositor implementation. This takes the same arguments as a 'create' request, and obeys the same restrictions. wayvnc-0.4.1/protocols/meson.build000066400000000000000000000021041415671750500172210ustar00rootroot00000000000000wayland_scanner = find_program('wayland-scanner') wayland_client = dependency('wayland-client') wayland_scanner_code = generator( wayland_scanner, output: '@BASENAME@.c', arguments: ['private-code', '@INPUT@', '@OUTPUT@'], ) wayland_scanner_client = generator( wayland_scanner, output: '@BASENAME@.h', arguments: ['client-header', '@INPUT@', '@OUTPUT@'], ) client_protocols = [ 'wlr-export-dmabuf-unstable-v1.xml', 'wlr-screencopy-unstable-v1.xml', 'wlr-virtual-pointer-unstable-v1.xml', 'virtual-keyboard-unstable-v1.xml', 'xdg-output-unstable-v1.xml', 'linux-dmabuf-unstable-v1.xml', 'wlr-data-control-unstable-v1.xml', ] client_protos_src = [] client_protos_headers = [] foreach xml: client_protocols client_protos_src += wayland_scanner_code.process(xml) client_protos_headers += wayland_scanner_client.process(xml) endforeach lib_client_protos = static_library( 'client_protos', client_protos_src + client_protos_headers, dependencies: [ wayland_client ] ) client_protos = declare_dependency( link_with: lib_client_protos, sources: client_protos_headers, ) wayvnc-0.4.1/protocols/virtual-keyboard-unstable-v1.xml000066400000000000000000000114261415671750500232330ustar00rootroot00000000000000 Copyright © 2008-2011 Kristian Høgsberg Copyright © 2010-2013 Intel Corporation Copyright © 2012-2013 Collabora, Ltd. Copyright © 2018 Purism SPC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. The virtual keyboard provides an application with requests which emulate the behaviour of a physical keyboard. This interface can be used by clients on its own to provide raw input events, or it can accompany the input method protocol. Provide a file descriptor to the compositor which can be memory-mapped to provide a keyboard mapping description. Format carries a value from the keymap_format enumeration. A key was pressed or released. The time argument is a timestamp with millisecond granularity, with an undefined base. All requests regarding a single object must share the same clock. Keymap must be set before issuing this request. State carries a value from the key_state enumeration. Notifies the compositor that the modifier and/or group state has changed, and it should update state. The client should use wl_keyboard.modifiers event to synchronize its internal state with seat state. Keymap must be set before issuing this request. A virtual keyboard manager allows an application to provide keyboard input events as if they came from a physical keyboard. Creates a new virtual keyboard associated to a seat. If the compositor enables a keyboard to perform arbitrary actions, it should present an error when an untrusted client requests a new keyboard. wayvnc-0.4.1/protocols/wlr-data-control-unstable-v1.xml000066400000000000000000000274161415671750500231460ustar00rootroot00000000000000 Copyright © 2018 Simon Ser Copyright © 2019 Ivan Molodetskikh Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the copyright holders not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. The copyright holders make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. This protocol allows a privileged client to control data devices. In particular, the client will be able to manage the current selection and take the role of a clipboard manager. Warning! The protocol described in this file is experimental and backward incompatible changes may be made. Backward compatible changes may be added together with the corresponding interface version bump. Backward incompatible changes are done by bumping the version number in the protocol and interface names and resetting the interface version. Once the protocol is to be declared stable, the 'z' prefix and the version number in the protocol and interface names are removed and the interface version number is reset. This interface is a manager that allows creating per-seat data device controls. Create a new data source. Create a data device that can be used to manage a seat's selection. All objects created by the manager will still remain valid, until their appropriate destroy request has been called. This interface allows a client to manage a seat's selection. When the seat is destroyed, this object becomes inert. This request asks the compositor to set the selection to the data from the source on behalf of the client. The given source may not be used in any further set_selection or set_primary_selection requests. Attempting to use a previously used source is a protocol error. To unset the selection, set the source to NULL. Destroys the data device object. The data_offer event introduces a new wlr_data_control_offer object, which will subsequently be used in either the wlr_data_control_device.selection event (for the regular clipboard selections) or the wlr_data_control_device.primary_selection event (for the primary clipboard selections). Immediately following the wlr_data_control_device.data_offer event, the new data_offer object will send out wlr_data_control_offer.offer events to describe the MIME types it offers. The selection event is sent out to notify the client of a new wlr_data_control_offer for the selection for this device. The wlr_data_control_device.data_offer and the wlr_data_control_offer.offer events are sent out immediately before this event to introduce the data offer object. The selection event is sent to a client when a new selection is set. The wlr_data_control_offer is valid until a new wlr_data_control_offer or NULL is received. The client must destroy the previous selection wlr_data_control_offer, if any, upon receiving this event. The first selection event is sent upon binding the wlr_data_control_device object. This data control object is no longer valid and should be destroyed by the client. The primary_selection event is sent out to notify the client of a new wlr_data_control_offer for the primary selection for this device. The wlr_data_control_device.data_offer and the wlr_data_control_offer.offer events are sent out immediately before this event to introduce the data offer object. The primary_selection event is sent to a client when a new primary selection is set. The wlr_data_control_offer is valid until a new wlr_data_control_offer or NULL is received. The client must destroy the previous primary selection wlr_data_control_offer, if any, upon receiving this event. If the compositor supports primary selection, the first primary_selection event is sent upon binding the wlr_data_control_device object. This request asks the compositor to set the primary selection to the data from the source on behalf of the client. The given source may not be used in any further set_selection or set_primary_selection requests. Attempting to use a previously used source is a protocol error. To unset the primary selection, set the source to NULL. The compositor will ignore this request if it does not support primary selection. The wlr_data_control_source object is the source side of a wlr_data_control_offer. It is created by the source client in a data transfer and provides a way to describe the offered data and a way to respond to requests to transfer the data. This request adds a MIME type to the set of MIME types advertised to targets. Can be called several times to offer multiple types. Calling this after wlr_data_control_device.set_selection is a protocol error. Destroys the data source object. Request for data from the client. Send the data as the specified MIME type over the passed file descriptor, then close it. This data source is no longer valid. The data source has been replaced by another data source. The client should clean up and destroy this data source. A wlr_data_control_offer represents a piece of data offered for transfer by another client (the source client). The offer describes the different MIME types that the data can be converted to and provides the mechanism for transferring the data directly from the source client. To transfer the offered data, the client issues this request and indicates the MIME type it wants to receive. The transfer happens through the passed file descriptor (typically created with the pipe system call). The source client writes the data in the MIME type representation requested and then closes the file descriptor. The receiving client reads from the read end of the pipe until EOF and then closes its end, at which point the transfer is complete. This request may happen multiple times for different MIME types. Destroys the data offer object. Sent immediately after creating the wlr_data_control_offer object. One event per offered MIME type. wayvnc-0.4.1/protocols/wlr-export-dmabuf-unstable-v1.xml000066400000000000000000000217351415671750500233320ustar00rootroot00000000000000 Copyright © 2018 Rostislav Pehlivanov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. An interface to capture surfaces in an efficient way by exporting DMA-BUFs. Warning! The protocol described in this file is experimental and backward incompatible changes may be made. Backward compatible changes may be added together with the corresponding interface version bump. Backward incompatible changes are done by bumping the version number in the protocol and interface names and resetting the interface version. Once the protocol is to be declared stable, the 'z' prefix and the version number in the protocol and interface names are removed and the interface version number is reset. This object is a manager with which to start capturing from sources. Capture the next frame of a an entire output. All objects created by the manager will still remain valid, until their appropriate destroy request has been called. This object represents a single DMA-BUF frame. If the capture is successful, the compositor will first send a "frame" event, followed by one or several "object". When the frame is available for readout, the "ready" event is sent. If the capture failed, the "cancel" event is sent. This can happen anytime before the "ready" event. Once either a "ready" or a "cancel" event is received, the client should destroy the frame. Once an "object" event is received, the client is responsible for closing the associated file descriptor. All frames are read-only and may not be written into or altered. Special flags that should be respected by the client. Main event supplying the client with information about the frame. If the capture didn't fail, this event is always emitted first before any other events. This event is followed by a number of "object" as specified by the "num_objects" argument. Event which serves to supply the client with the file descriptors containing the data for each object. After receiving this event, the client must always close the file descriptor as soon as they're done with it and even if the frame fails. This event is sent as soon as the frame is presented, indicating it is available for reading. This event includes the time at which presentation happened at. The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, each component being an unsigned 32-bit value. Whole seconds are in tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo, and the additional fractional part in tv_nsec as nanoseconds. Hence, for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part may have an arbitrary offset at start. After receiving this event, the client should destroy this object. Indicates reason for cancelling the frame. If the capture failed or if the frame is no longer valid after the "frame" event has been emitted, this event will be used to inform the client to scrap the frame. If the failure is temporary, the client may capture again the same source. If the failure is permanent, any further attempts to capture the same source will fail again. After receiving this event, the client should destroy this object. Unreferences the frame. This request must be called as soon as its no longer used. It can be called at any time by the client. The client will still have to close any FDs it has been given. wayvnc-0.4.1/protocols/wlr-screencopy-unstable-v1.xml000066400000000000000000000236661415671750500227340ustar00rootroot00000000000000 Copyright © 2018 Simon Ser Copyright © 2019 Andri Yngvason Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. This protocol allows clients to ask the compositor to copy part of the screen content to a client buffer. Warning! The protocol described in this file is experimental and backward incompatible changes may be made. Backward compatible changes may be added together with the corresponding interface version bump. Backward incompatible changes are done by bumping the version number in the protocol and interface names and resetting the interface version. Once the protocol is to be declared stable, the 'z' prefix and the version number in the protocol and interface names are removed and the interface version number is reset. This object is a manager which offers requests to start capturing from a source. Capture the next frame of an entire output. Capture the next frame of an output's region. The region is given in output logical coordinates, see xdg_output.logical_size. The region will be clipped to the output's extents. All objects created by the manager will still remain valid, until their appropriate destroy request has been called. This object represents a single frame. When created, a series of buffer events will be sent, each representing a supported buffer type. The "buffer_done" event is sent afterwards to indicate that all supported buffer types have been enumerated. The client will then be able to send a "copy" request. If the capture is successful, the compositor will send a "flags" followed by a "ready" event. For objects version 2 or lower, wl_shm buffers are always supported, ie. the "buffer" event is guaranteed to be sent. If the capture failed, the "failed" event is sent. This can happen anytime before the "ready" event. Once either a "ready" or a "failed" event is received, the client should destroy the frame. Provides information about wl_shm buffer parameters that need to be used for this frame. This event is sent once after the frame is created if wl_shm buffers are supported. Copy the frame to the supplied buffer. The buffer must have a the correct size, see zwlr_screencopy_frame_v1.buffer and zwlr_screencopy_frame_v1.linux_dmabuf. The buffer needs to have a supported format. If the frame is successfully copied, a "flags" and a "ready" events are sent. Otherwise, a "failed" event is sent. Provides flags about the frame. This event is sent once before the "ready" event. Called as soon as the frame is copied, indicating it is available for reading. This event includes the time at which presentation happened at. The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, each component being an unsigned 32-bit value. Whole seconds are in tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo, and the additional fractional part in tv_nsec as nanoseconds. Hence, for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part may have an arbitrary offset at start. After receiving this event, the client should destroy the object. This event indicates that the attempted frame copy has failed. After receiving this event, the client should destroy the object. Destroys the frame. This request can be sent at any time by the client. Same as copy, except it waits until there is damage to copy. This event is sent right before the ready event when copy_with_damage is requested. It may be generated multiple times for each copy_with_damage request. The arguments describe a box around an area that has changed since the last copy request that was derived from the current screencopy manager instance. The union of all regions received between the call to copy_with_damage and a ready event is the total damage since the prior ready event. Provides information about linux-dmabuf buffer parameters that need to be used for this frame. This event is sent once after the frame is created if linux-dmabuf buffers are supported. This event is sent once after all buffer events have been sent. The client should proceed to create a buffer of one of the supported types, and send a "copy" request. wayvnc-0.4.1/protocols/wlr-virtual-pointer-unstable-v1.xml000066400000000000000000000153571415671750500237240ustar00rootroot00000000000000 Copyright © 2019 Josef Gajdusek Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. This protocol allows clients to emulate a physical pointer device. The requests are mostly mirror opposites of those specified in wl_pointer. The pointer has moved by a relative amount to the previous request. Values are in the global compositor space. The pointer has moved in an absolute coordinate frame. Value of x can range from 0 to x_extent, value of y can range from 0 to y_extent. A button was pressed or released. Scroll and other axis requests. Indicates the set of events that logically belong together. Source information for scroll and other axis. Stop notification for scroll and other axes. Discrete step information for scroll and other axes. This event allows the client to extend data normally sent using the axis event with discrete value. This object allows clients to create individual virtual pointer objects. Creates a new virtual pointer. The optional seat is a suggestion to the compositor. Creates a new virtual pointer. The seat and the output arguments are optional. If the seat argument is set, the compositor should assign the input device to the requested seat. If the output argument is set, the compositor should map the input device to the requested output. wayvnc-0.4.1/protocols/xdg-output-unstable-v1.xml000066400000000000000000000225121415671750500220650ustar00rootroot00000000000000 Copyright © 2017 Red Hat Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. This protocol aims at describing outputs in a way which is more in line with the concept of an output on desktop oriented systems. Some information are more specific to the concept of an output for a desktop oriented system and may not make sense in other applications, such as IVI systems for example. Typically, the global compositor space on a desktop system is made of a contiguous or overlapping set of rectangular regions. Some of the information provided in this protocol might be identical to their counterparts already available from wl_output, in which case the information provided by this protocol should be preferred to their equivalent in wl_output. The goal is to move the desktop specific concepts (such as output location within the global compositor space, the connector name and types, etc.) out of the core wl_output protocol. Warning! The protocol described in this file is experimental and backward incompatible changes may be made. Backward compatible changes may be added together with the corresponding interface version bump. Backward incompatible changes are done by bumping the version number in the protocol and interface names and resetting the interface version. Once the protocol is to be declared stable, the 'z' prefix and the version number in the protocol and interface names are removed and the interface version number is reset. A global factory interface for xdg_output objects. Using this request a client can tell the server that it is not going to use the xdg_output_manager object anymore. Any objects already created through this instance are not affected. This creates a new xdg_output object for the given wl_output. An xdg_output describes part of the compositor geometry. This typically corresponds to a monitor that displays part of the compositor space. For objects version 3 onwards, after all xdg_output properties have been sent (when the object is created and when properties are updated), a wl_output.done event is sent. This allows changes to the output properties to be seen as atomic, even if they happen via multiple events. Using this request a client can tell the server that it is not going to use the xdg_output object anymore. The position event describes the location of the wl_output within the global compositor space. The logical_position event is sent after creating an xdg_output (see xdg_output_manager.get_xdg_output) and whenever the location of the output changes within the global compositor space. The logical_size event describes the size of the output in the global compositor space. For example, a surface without any buffer scale, transformation nor rotation set, with the size matching the logical_size will have the same size as the corresponding output when displayed. Most regular Wayland clients should not pay attention to the logical size and would rather rely on xdg_shell interfaces. Some clients such as Xwayland, however, need this to configure their surfaces in the global compositor space as the compositor may apply a different scale from what is advertised by the output scaling property (to achieve fractional scaling, for example). For example, for a wl_output mode 3840×2160 and a scale factor 2: - A compositor not scaling the surface buffers will advertise a logical size of 3840×2160, - A compositor automatically scaling the surface buffers will advertise a logical size of 1920×1080, - A compositor using a fractional scale of 1.5 will advertise a logical size to 2560×1620. For example, for a wl_output mode 1920×1080 and a 90 degree rotation, the compositor will advertise a logical size of 1080x1920. The logical_size event is sent after creating an xdg_output (see xdg_output_manager.get_xdg_output) and whenever the logical size of the output changes, either as a result of a change in the applied scale or because of a change in the corresponding output mode(see wl_output.mode) or transform (see wl_output.transform). This event is sent after all other properties of an xdg_output have been sent. This allows changes to the xdg_output properties to be seen as atomic, even if they happen via multiple events. For objects version 3 onwards, this event is deprecated. Compositors are not required to send it anymore and must send wl_output.done instead. Many compositors will assign names to their outputs, show them to the user, allow them to be configured by name, etc. The client may wish to know this name as well to offer the user similar behaviors. The naming convention is compositor defined, but limited to alphanumeric characters and dashes (-). Each name is unique among all wl_output globals, but if a wl_output global is destroyed the same name may be reused later. The names will also remain consistent across sessions with the same hardware and software configuration. Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do not assume that the name is a reflection of an underlying DRM connector, X11 connection, etc. The name event is sent after creating an xdg_output (see xdg_output_manager.get_xdg_output). This event is only sent once per xdg_output, and the name does not change over the lifetime of the wl_output global. Many compositors can produce human-readable descriptions of their outputs. The client may wish to know this description as well, to communicate the user for various purposes. The description is a UTF-8 string with no convention defined for its contents. Examples might include 'Foocorp 11" Display' or 'Virtual X11 output via :1'. The description event is sent after creating an xdg_output (see xdg_output_manager.get_xdg_output) and whenever the description changes. The description is optional, and may not be sent at all. For objects of version 2 and lower, this event is only sent once per xdg_output, and the description does not change over the lifetime of the wl_output global. wayvnc-0.4.1/src/000077500000000000000000000000001415671750500136255ustar00rootroot00000000000000wayvnc-0.4.1/src/buffer.c000066400000000000000000000211321415671750500152410ustar00rootroot00000000000000/* * Copyright (c) 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include "linux-dmabuf-unstable-v1.h" #include "shm.h" #include "sys/queue.h" #include "buffer.h" #include "pixels.h" #include "config.h" #ifdef ENABLE_SCREENCOPY_DMABUF #include #endif extern struct wl_shm* wl_shm; extern struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf; extern struct gbm_device* gbm_device; enum wv_buffer_type wv_buffer_get_available_types(void) { enum wv_buffer_type type = 0; if (wl_shm) type |= WV_BUFFER_SHM; #ifdef ENABLE_SCREENCOPY_DMABUF if (zwp_linux_dmabuf && gbm_device) type |= WV_BUFFER_DMABUF; #endif return type; } struct wv_buffer* wv_buffer_create_shm(int width, int height, int stride, uint32_t fourcc) { assert(wl_shm); enum wl_shm_format wl_fmt = fourcc_to_wl_shm(fourcc); struct wv_buffer* self = calloc(1, sizeof(*self)); if (!self) return NULL; self->type = WV_BUFFER_SHM; self->width = width; self->height = height; self->stride = stride; self->format = fourcc; self->size = height * stride; int fd = shm_alloc_fd(self->size); if (fd < 0) goto failure; self->pixels = mmap(NULL, self->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (!self->pixels) goto mmap_failure; struct wl_shm_pool* pool = wl_shm_create_pool(wl_shm, fd, self->size); if (!pool) goto pool_failure; self->wl_buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, wl_fmt); wl_shm_pool_destroy(pool); if (!self->wl_buffer) goto shm_failure; pixman_region_init(&self->damage); close(fd); return self; shm_failure: pool_failure: munmap(self->pixels, self->size); mmap_failure: close(fd); failure: free(self); return NULL; } #ifdef ENABLE_SCREENCOPY_DMABUF static struct wv_buffer* wv_buffer_create_dmabuf(int width, int height, uint32_t fourcc) { assert(zwp_linux_dmabuf); assert(gbm_device); struct wv_buffer* self = calloc(1, sizeof(*self)); if (!self) return NULL; self->type = WV_BUFFER_DMABUF; self->width = width; self->height = height; self->format = fourcc; self->bo = gbm_bo_create(gbm_device, width, height, fourcc, GBM_BO_USE_RENDERING); if (!self->bo) goto bo_failure; struct zwp_linux_buffer_params_v1* params; params = zwp_linux_dmabuf_v1_create_params(zwp_linux_dmabuf); if (!params) goto params_failure; uint32_t offset = gbm_bo_get_offset(self->bo, 0); uint32_t stride = gbm_bo_get_stride(self->bo); uint64_t mod = gbm_bo_get_modifier(self->bo); int fd = gbm_bo_get_fd(self->bo); if (fd < 0) goto fd_failure; zwp_linux_buffer_params_v1_add(params, fd, 0, offset, stride, mod >> 32, mod & 0xffffffff); self->wl_buffer = zwp_linux_buffer_params_v1_create_immed(params, width, height, fourcc, /* flags */ 0); zwp_linux_buffer_params_v1_destroy(params); close(fd); if (!self->wl_buffer) goto buffer_failure; return self; buffer_failure: fd_failure: zwp_linux_buffer_params_v1_destroy(params); params_failure: gbm_bo_destroy(self->bo); bo_failure: free(self); return NULL; } #endif struct wv_buffer* wv_buffer_create(enum wv_buffer_type type, int width, int height, int stride, uint32_t fourcc) { switch (type) { case WV_BUFFER_SHM: return wv_buffer_create_shm(width, height, stride, fourcc); #ifdef ENABLE_SCREENCOPY_DMABUF case WV_BUFFER_DMABUF: return wv_buffer_create_dmabuf(width, height, fourcc); #endif case WV_BUFFER_UNSPEC:; } abort(); return NULL; } static void wv_buffer_destroy_shm(struct wv_buffer* self) { wl_buffer_destroy(self->wl_buffer); munmap(self->pixels, self->size); free(self); } #ifdef ENABLE_SCREENCOPY_DMABUF static void wv_buffer_destroy_dmabuf(struct wv_buffer* self) { wl_buffer_destroy(self->wl_buffer); gbm_bo_destroy(self->bo); free(self); } #endif void wv_buffer_destroy(struct wv_buffer* self) { pixman_region_fini(&self->damage); wv_buffer_unmap(self); switch (self->type) { case WV_BUFFER_SHM: wv_buffer_destroy_shm(self); return; #ifdef ENABLE_SCREENCOPY_DMABUF case WV_BUFFER_DMABUF: wv_buffer_destroy_dmabuf(self); return; #endif case WV_BUFFER_UNSPEC:; } abort(); } #ifdef ENABLE_SCREENCOPY_DMABUF static int wv_buffer_map_dmabuf(struct wv_buffer* self) { if (self->bo_map_handle) return 0; uint32_t stride = 0; self->pixels = gbm_bo_map(self->bo, 0, 0, self->width, self->height, GBM_BO_TRANSFER_READ, &stride, &self->bo_map_handle); self->stride = stride; if (self->pixels) return 0; self->bo_map_handle = NULL; return -1; } #endif int wv_buffer_map(struct wv_buffer* self) { switch (self->type) { case WV_BUFFER_SHM: return 0; #ifdef ENABLE_SCREENCOPY_DMABUF case WV_BUFFER_DMABUF: return wv_buffer_map_dmabuf(self); #endif case WV_BUFFER_UNSPEC:; } abort(); } #ifdef ENABLE_SCREENCOPY_DMABUF static void wv_buffer_unmap_dmabuf(struct wv_buffer* self) { if (self->bo_map_handle) gbm_bo_unmap(self->bo, self->bo_map_handle); self->bo_map_handle = NULL; } #endif void wv_buffer_unmap(struct wv_buffer* self) { switch (self->type) { case WV_BUFFER_SHM: return; #ifdef ENABLE_SCREENCOPY_DMABUF case WV_BUFFER_DMABUF: return wv_buffer_unmap_dmabuf(self); #endif case WV_BUFFER_UNSPEC:; } abort(); } void wv_buffer_damage_rect(struct wv_buffer* self, int x, int y, int width, int height) { pixman_region_union_rect(&self->damage, &self->damage, x, y, width, height); } void wv_buffer_damage_whole(struct wv_buffer* self) { wv_buffer_damage_rect(self, 0, 0, self->width, self->height); } void wv_buffer_damage_clear(struct wv_buffer* self) { pixman_region_clear(&self->damage); } struct wv_buffer_pool* wv_buffer_pool_create(enum wv_buffer_type type, int width, int height, int stride, uint32_t format) { struct wv_buffer_pool* self = calloc(1, sizeof(*self)); if (!self) return NULL; TAILQ_INIT(&self->queue); self->type = type; self->width = width; self->height = height; self->stride = stride; self->format = format; return self; } static void wv_buffer_pool_clear(struct wv_buffer_pool* pool) { while (!TAILQ_EMPTY(&pool->queue)) { struct wv_buffer* buffer = TAILQ_FIRST(&pool->queue); TAILQ_REMOVE(&pool->queue, buffer, link); wv_buffer_destroy(buffer); } } void wv_buffer_pool_destroy(struct wv_buffer_pool* pool) { wv_buffer_pool_clear(pool); free(pool); } void wv_buffer_pool_resize(struct wv_buffer_pool* pool, enum wv_buffer_type type, int width, int height, int stride, uint32_t format) { if (pool->type != type || pool->width != width || pool->height != height || pool->stride != stride || pool->format != format) { wv_buffer_pool_clear(pool); } pool->type = type; pool->width = width; pool->height = height; pool->stride = stride; pool->format = format; } static bool wv_buffer_pool_match_buffer(struct wv_buffer_pool* pool, struct wv_buffer* buffer) { if (pool->type != buffer->type) return false; switch (pool->type) { case WV_BUFFER_SHM: if (pool->stride != buffer->stride) return false; /* fall-through */ #ifdef ENABLE_SCREENCOPY_DMABUF case WV_BUFFER_DMABUF: #endif if (pool->width != buffer->width || pool->height != buffer->height || pool->format != buffer->format) return false; return true; case WV_BUFFER_UNSPEC: abort(); } return false; } struct wv_buffer* wv_buffer_pool_acquire(struct wv_buffer_pool* pool) { struct wv_buffer* buffer = TAILQ_FIRST(&pool->queue); if (buffer) { assert(wv_buffer_pool_match_buffer(pool, buffer)); TAILQ_REMOVE(&pool->queue, buffer, link); return buffer; } return wv_buffer_create(pool->type, pool->width, pool->height, pool->stride, pool->format); } void wv_buffer_pool_release(struct wv_buffer_pool* pool, struct wv_buffer* buffer) { wv_buffer_damage_clear(buffer); wv_buffer_unmap(buffer); if (wv_buffer_pool_match_buffer(pool, buffer)) { TAILQ_INSERT_TAIL(&pool->queue, buffer, link); } else { wv_buffer_destroy(buffer); } } wayvnc-0.4.1/src/cfg.c000066400000000000000000000063751415671750500145430ustar00rootroot00000000000000/* * Copyright (c) 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "cfg.h" #define XSTR(s) STR(s) #define STR(s) #s static char* cfg__get_default_path(void) { static char result[256]; char* xdg_config_home = getenv("XDG_CONFIG_HOME"); if (xdg_config_home) { snprintf(result, sizeof(result), "%s/wayvnc/config", xdg_config_home); result[sizeof(result) - 1] = '\0'; return result; } char* home = getenv("HOME"); if (!home) return NULL; snprintf(result, sizeof(result), "%s/.config/wayvnc/config", home); result[sizeof(result) - 1] = '\0'; return result; } static char* cfg__trim_left(char* str) { while (isspace(*str)) ++str; return str; } static char* cfg__trim_right(char* str) { char* end = str + strlen(str) - 1; while (str < end && isspace(*end)) *end-- = '\0'; return str; } static inline char* cfg__trim(char* str) { return cfg__trim_right(cfg__trim_left(str)); } static int cfg__load_key_value(struct cfg* self, const char* key, const char* value) { #define LOAD_bool(v) (strcmp(v, "false") != 0) #define LOAD_string(v) strdup(v) #define LOAD_uint(v) strtoul(v, NULL, 0) #define X(type, name) \ if (strcmp(XSTR(name), key) == 0) { \ self->name = LOAD_ ## type(value); \ return 0; \ } X_CFG_LIST #undef X #undef LOAD_uint #undef LOAD_string #undef LOAD_bool return -1; } static int cfg__load_line(struct cfg* self, char* line) { line = cfg__trim(line); if (line[0] == '\0' || line[0] == '#') return 0; char* delim = strchr(line, '='); if (!delim) return -1; *delim = '\0'; char* key = cfg__trim_right(line); char* value = cfg__trim_left(delim + 1); return cfg__load_key_value(self, key, value); } int cfg_load(struct cfg* self, const char* requested_path) { const char* path = requested_path ? requested_path : cfg__get_default_path(); if (!path) return -1; FILE* stream = fopen(path, "r"); if (!stream) return -1; char* line = NULL; size_t len = 0; int lineno = 0; while (getline(&line, &len, stream) > 0) { ++lineno; if (cfg__load_line(self, line) < 0) goto failure; } free(line); fclose(stream); return 0; failure: cfg_destroy(self); free(line); fclose(stream); return lineno; } void cfg_destroy(struct cfg* self) { #define DESTROY_bool(...) #define DESTROY_uint(...) #define DESTROY_string(p) free(p) #define X(type, name) DESTROY_ ## type(self->name); X_CFG_LIST #undef X #undef DESTROY_string #undef DESTROY_uint #undef DESTROY_bool } wayvnc-0.4.1/src/damage-refinery.c000066400000000000000000000075551415671750500170440ustar00rootroot00000000000000/* * Copyright (c) 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "damage-refinery.h" #include "buffer.h" #include "util.h" #include "murmurhash.h" #define HASH_SEED 0 int damage_refinery_init(struct damage_refinery* self, uint32_t width, uint32_t height) { self->width = width; self->height = height; uint32_t twidth = UDIV_UP(width, 32); uint32_t theight = UDIV_UP(height, 32); self->hashes = calloc(twidth * theight, sizeof(*self->hashes)); if (!self->hashes) return -1; return 0; } void damage_refinery_destroy(struct damage_refinery* self) { free(self->hashes); } static uint32_t damage_hash_tile(struct damage_refinery* self, uint32_t tx, uint32_t ty, const struct wv_buffer* buffer) { // TODO: Support different pixel sizes uint32_t* pixels = buffer->pixels; int pixel_stride = buffer->stride / 4; if (buffer->y_inverted) { pixels += (buffer->height - 1) * pixel_stride; pixel_stride *= -1; } int x_start = tx * 32; int x_stop = MIN((tx + 1) * 32, self->width); int y_start = ty * 32; int y_stop = MIN((ty + 1) * 32, self->height); uint32_t hash = 0; for (int y = y_start; y < y_stop; ++y) hash = murmurhash((void*)&(pixels[x_start + y * pixel_stride]), 4 * (x_stop - x_start), hash); return hash; } static uint32_t* damage_tile_hash_ptr(struct damage_refinery* self, uint32_t tx, uint32_t ty) { uint32_t twidth = UDIV_UP(self->width, 32); return &self->hashes[tx + ty * twidth]; } static void damage_refine_tile(struct damage_refinery* self, struct pixman_region16* refined, uint32_t tx, uint32_t ty, const struct wv_buffer* buffer) { uint32_t hash = damage_hash_tile(self, tx, ty, buffer); uint32_t* old_hash_ptr = damage_tile_hash_ptr(self, tx, ty); int is_damaged = hash != *old_hash_ptr; *old_hash_ptr = hash; if (is_damaged) pixman_region_union_rect(refined, refined, tx * 32, ty * 32, 32, 32); } static void tile_region_from_region(struct pixman_region16* dst, struct pixman_region16* src) { int n_rects = 0; struct pixman_box16* rects = pixman_region_rectangles(src, &n_rects); for (int i = 0; i < n_rects; ++i) { int x1 = rects[i].x1 / 32; int y1 = rects[i].y1 / 32; int x2 = UDIV_UP(rects[i].x2, 32); int y2 = UDIV_UP(rects[i].y2, 32); pixman_region_union_rect(dst, dst, x1, y1, x2 - x1, y2 - y1); } } void damage_refine(struct damage_refinery* self, struct pixman_region16* refined, struct pixman_region16* hint, const struct wv_buffer* buffer) { assert(self->width == (uint32_t)buffer->width && self->height == (uint32_t)buffer->height); struct pixman_region16 tile_region; pixman_region_init(&tile_region); tile_region_from_region(&tile_region, hint); int n_rects = 0; struct pixman_box16* rects = pixman_region_rectangles(&tile_region, &n_rects); for (int i = 0; i < n_rects; ++i) for (int ty = rects[i].y1; ty < rects[i].y2; ++ty) for (int tx = rects[i].x1; tx < rects[i].x2; ++tx) damage_refine_tile(self, refined, tx, ty, buffer); pixman_region_fini(&tile_region); pixman_region_intersect_rect(refined, refined, 0, 0, self->width, self->height); } wayvnc-0.4.1/src/data-control.c000066400000000000000000000167521415671750500163730ustar00rootroot00000000000000/* * Copyright (c) 2020 Scott Moreau * Copyright (c) 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "logging.h" #include "data-control.h" struct receive_context { struct data_control* data_control; struct zwlr_data_control_offer_v1* offer; int fd; FILE* mem_fp; size_t mem_size; char* mem_data; }; static void destroy_receive_context(void* raw_ctx) { struct receive_context* ctx = raw_ctx; int fd = ctx->fd; if (ctx->mem_fp) fclose(ctx->mem_fp); free(ctx->mem_data); zwlr_data_control_offer_v1_destroy(ctx->offer); close(fd); free(ctx); } static void on_receive(void* handler) { struct receive_context* ctx = aml_get_userdata(handler); int fd = aml_get_fd(handler); assert(ctx->fd == fd); char buf[4096]; ssize_t ret = read(fd, &buf, sizeof(buf)); if (ret > 0) { fwrite(&buf, 1, ret, ctx->mem_fp); return; } fclose(ctx->mem_fp); ctx->mem_fp = NULL; if (ctx->mem_size) nvnc_send_cut_text(ctx->data_control->server, ctx->mem_data, ctx->mem_size); aml_stop(aml_get_default(), handler); } static void receive_data(void* data, struct zwlr_data_control_offer_v1* offer) { struct data_control* self = data; int pipe_fd[2]; if (pipe(pipe_fd) == -1) { log_error("pipe() failed: %m\n"); return; } struct receive_context* ctx = calloc(1, sizeof(*ctx)); if (!ctx) { log_error("OOM\n"); close(pipe_fd[0]); close(pipe_fd[1]); return; } zwlr_data_control_offer_v1_receive(offer, self->mime_type, pipe_fd[1]); wl_display_flush(self->wl_display); close(pipe_fd[1]); ctx->fd = pipe_fd[0]; ctx->data_control = self; ctx->offer = offer; ctx->mem_fp = open_memstream(&ctx->mem_data, &ctx->mem_size); if (!ctx->mem_fp) { close(ctx->fd); free(ctx); log_error("open_memstream() failed: %m\n"); return; } struct aml_handler* handler = aml_handler_new(ctx->fd, on_receive, ctx, destroy_receive_context); if (!handler) { close(ctx->fd); free(ctx); return; } aml_start(aml_get_default(), handler); aml_unref(handler); } static void data_control_offer(void* data, struct zwlr_data_control_offer_v1* zwlr_data_control_offer_v1, const char* mime_type) { struct data_control* self = data; if (self->offer) return; if (strcmp(mime_type, self->mime_type) != 0) { return; } self->offer = zwlr_data_control_offer_v1; } struct zwlr_data_control_offer_v1_listener data_control_offer_listener = { data_control_offer }; static void data_control_device_offer(void* data, struct zwlr_data_control_device_v1* zwlr_data_control_device_v1, struct zwlr_data_control_offer_v1* id) { if (!id) return; zwlr_data_control_offer_v1_add_listener(id, &data_control_offer_listener, data); } static void data_control_device_selection(void* data, struct zwlr_data_control_device_v1* zwlr_data_control_device_v1, struct zwlr_data_control_offer_v1* id) { struct data_control* self = data; if (id && self->offer == id) { receive_data(data, id); self->offer = NULL; } } static void data_control_device_finished(void* data, struct zwlr_data_control_device_v1* zwlr_data_control_device_v1) { zwlr_data_control_device_v1_destroy(zwlr_data_control_device_v1); } static void data_control_device_primary_selection(void* data, struct zwlr_data_control_device_v1* zwlr_data_control_device_v1, struct zwlr_data_control_offer_v1* id) { struct data_control* self = data; if (id && self->offer == id) { receive_data(data, id); self->offer = NULL; return; } } static struct zwlr_data_control_device_v1_listener data_control_device_listener = { .data_offer = data_control_device_offer, .selection = data_control_device_selection, .finished = data_control_device_finished, .primary_selection = data_control_device_primary_selection }; static void data_control_source_send(void* data, struct zwlr_data_control_source_v1* zwlr_data_control_source_v1, const char* mime_type, int32_t fd) { struct data_control* self = data; char* d = self->cb_data; size_t len = self->cb_len; int ret; assert(d); ret = write(fd, d, len); if (ret < (int)len) log_error("write from clipboard incomplete\n"); close(fd); } static void data_control_source_cancelled(void* data, struct zwlr_data_control_source_v1* zwlr_data_control_source_v1) { struct data_control* self = data; if (self->selection == zwlr_data_control_source_v1) { self->selection = NULL; } if (self->primary_selection == zwlr_data_control_source_v1) { self->primary_selection = NULL; } zwlr_data_control_source_v1_destroy(zwlr_data_control_source_v1); } struct zwlr_data_control_source_v1_listener data_control_source_listener = { .send = data_control_source_send, .cancelled = data_control_source_cancelled }; static struct zwlr_data_control_source_v1* set_selection(struct data_control* self, bool primary) { struct zwlr_data_control_source_v1* selection; selection = zwlr_data_control_manager_v1_create_data_source(self->manager); if (selection == NULL) { log_error("zwlr_data_control_manager_v1_create_data_source() failed\n"); free(self->cb_data); self->cb_data = NULL; return NULL; } zwlr_data_control_source_v1_add_listener(selection, &data_control_source_listener, self); zwlr_data_control_source_v1_offer(selection, self->mime_type); if (primary) zwlr_data_control_device_v1_set_primary_selection(self->device, selection); else zwlr_data_control_device_v1_set_selection(self->device, selection); return selection; } void data_control_init(struct data_control* self, struct wl_display* wl_display, struct nvnc* server, struct wl_seat* seat) { self->wl_display = wl_display; self->server = server; self->device = zwlr_data_control_manager_v1_get_data_device(self->manager, seat); zwlr_data_control_device_v1_add_listener(self->device, &data_control_device_listener, self); self->selection = NULL; self->primary_selection = NULL; self->cb_data = NULL; self->cb_len = 0; self->mime_type = "text/plain;charset=utf-8"; } void data_control_destroy(struct data_control* self) { if (self->selection) { zwlr_data_control_source_v1_destroy(self->selection); self->selection = NULL; } if (self->primary_selection) { zwlr_data_control_source_v1_destroy(self->primary_selection); self->primary_selection = NULL; } zwlr_data_control_device_v1_destroy(self->device); free(self->cb_data); } void data_control_to_clipboard(struct data_control* self, const char* text, size_t len) { if (!len) { log_error("%s called with 0 length\n", __func__); return; } free(self->cb_data); self->cb_data = malloc(len); if (!self->cb_data) { log_error("OOM: %m\n"); return; } memcpy(self->cb_data, text, len); self->cb_len = len; // Set copy/paste buffer self->selection = set_selection(self, false); // Set highlight/middle_click buffer self->primary_selection = set_selection(self, true); } wayvnc-0.4.1/src/intset.c000066400000000000000000000042021415671750500152750ustar00rootroot00000000000000/* * Copyright (c) 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "intset.h" #include #include #include #define DEFAULT_CAPACITY 256 int intset_init(struct intset* self, size_t cap) { if (cap == 0) cap = DEFAULT_CAPACITY; memset(self, 0, sizeof(*self)); self->storage = malloc(cap * sizeof(*self->storage)); if (!self->storage) return -1; self->cap = cap; return 0; } void intset_destroy(struct intset* self) { free(self->storage); memset(self, 0, sizeof(*self)); } static int intset__grow(struct intset* self) { size_t new_cap = self->cap * 2; int32_t* new_storage = realloc(self->storage, new_cap); if (!new_storage) return -1; self->storage = new_storage; self->cap = new_cap; return 0; } int intset_set(struct intset* self, int32_t value) { if (intset_is_set(self, value)) return 0; if (self->len >= self->cap && intset__grow(self) < 0) return -1; self->storage[self->len++] = value; return 0; } static ssize_t intset__find_index(const struct intset* self, int32_t value) { for (size_t i = 0; i < self->len; ++i) if (self->storage[i] == value) return i; return -1; } void intset_clear(struct intset* self, int32_t value) { ssize_t index = intset__find_index(self, value); if (index < 0) return; self->storage[index] = self->storage[--self->len]; } bool intset_is_set(const struct intset* self, int32_t value) { return intset__find_index(self, value) >= 0; } wayvnc-0.4.1/src/keyboard.c000066400000000000000000000267541415671750500156070ustar00rootroot00000000000000/* * Copyright (c) 2019 - 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. * * Acknowledgements: Reading Josef Gajdusek's wvnc code helped me understand * how to use the xkbcommon API to interface with the wayland virtual keyboard * interface. */ #include #include #include #include #include #include #include #include #include #include "virtual-keyboard-unstable-v1.h" #include "keyboard.h" #include "shm.h" #include "logging.h" #include "intset.h" #define MAYBE_UNUSED __attribute__((unused)) struct table_entry { xkb_keysym_t symbol; xkb_keycode_t code; int level; }; struct kb_mods { xkb_mod_mask_t depressed, latched, locked; }; static void append_entry(struct keyboard* self, xkb_keysym_t symbol, xkb_keycode_t code, int level) { if (self->lookup_table_size <= self->lookup_table_length) { size_t new_size = self->lookup_table_size * 2; struct table_entry* table = realloc(self->lookup_table, new_size * sizeof(*table)); if (!table) return; // TODO: Report this self->lookup_table_size = new_size; self->lookup_table = table; } struct table_entry* entry = &self->lookup_table[self->lookup_table_length++]; entry->symbol = symbol; entry->code = code; entry->level = level; } static void key_iter(struct xkb_keymap* map, xkb_keycode_t code, void* userdata) { struct keyboard* self = userdata; size_t n_levels = xkb_keymap_num_levels_for_key(map, code, 0); for (size_t level = 0; level < n_levels; level++) { const xkb_keysym_t* symbols; size_t n_syms = xkb_keymap_key_get_syms_by_level(map, code, 0, level, &symbols); for (size_t sym_idx = 0; sym_idx < n_syms; sym_idx++) append_entry(self, symbols[sym_idx], code, level); } } static int compare_symbols(const void* a, const void* b) { const struct table_entry* x = a; const struct table_entry* y = b; if (x->symbol == y->symbol) return x->level < y->level ? -1 : x->level > y->level; return x->symbol < y->symbol ? -1 : x->symbol > y->symbol; } static int compare_symbols2(const void* a, const void* b) { const struct table_entry* x = a; const struct table_entry* y = b; return x->symbol < y->symbol ? -1 : x->symbol > y->symbol; } static int create_lookup_table(struct keyboard* self) { self->lookup_table_length = 0; self->lookup_table_size = 128; self->lookup_table = malloc(self->lookup_table_size * sizeof(*self->lookup_table)); if (!self->lookup_table) return -1; xkb_keymap_key_for_each(self->keymap, key_iter, self); qsort(self->lookup_table, self->lookup_table_length, sizeof(*self->lookup_table), compare_symbols); return 0; } static char* get_symbol_name(xkb_keysym_t sym, char* dst, size_t size) { if (xkb_keysym_get_name(sym, dst, size) >= 0) return dst; snprintf(dst, size, "UNKNOWN (%x)", sym); return dst; } static void keyboard__dump_entry(const struct keyboard* self, const struct table_entry* entry) { char sym_name[256]; get_symbol_name(entry->symbol, sym_name, sizeof(sym_name)); const char* code_name MAYBE_UNUSED = xkb_keymap_key_get_name(self->keymap, entry->code); bool is_pressed MAYBE_UNUSED = intset_is_set(&self->key_state, entry->code); log_debug("symbol=%s level=%d code=%s %s\n", sym_name, entry->level, code_name, is_pressed ? "pressed" : "released"); } void keyboard_dump_lookup_table(const struct keyboard* self) { for (size_t i = 0; i < self->lookup_table_length; i++) keyboard__dump_entry(self, &self->lookup_table[i]); } int keyboard_init(struct keyboard* self, const struct xkb_rule_names* rule_names) { self->context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if (!self->context) return -1; if (intset_init(&self->key_state, 0) < 0) goto key_state_failure; self->keymap = xkb_keymap_new_from_names(self->context, rule_names, 0); if (!self->keymap) goto keymap_failure; if (xkb_keymap_num_layouts(self->keymap) > 1) log_warning("Multiple keyboard layouts have been specified, but only one is supported.\n"); self->state = xkb_state_new(self->keymap); if (!self->state) goto state_failure; if (create_lookup_table(self) < 0) goto table_failure; // keyboard_dump_lookup_table(self); char* keymap_string = xkb_keymap_get_as_string(self->keymap, XKB_KEYMAP_FORMAT_TEXT_V1); if (!keymap_string) goto keymap_string_failure; size_t keymap_size = strlen(keymap_string) + 1; int keymap_fd = shm_alloc_fd(keymap_size); if (keymap_fd < 0) goto fd_failure; size_t written = 0; while (written < keymap_size) { ssize_t ret = write(keymap_fd, keymap_string + written, keymap_size - written); if (ret == -1 && errno == EINTR) continue; if (ret == -1) goto write_failure; written += ret; } free(keymap_string); zwp_virtual_keyboard_v1_keymap(self->virtual_keyboard, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keymap_fd, keymap_size); close(keymap_fd); return 0; write_failure: close(keymap_fd); fd_failure: free(keymap_string); keymap_string_failure: free(self->lookup_table); table_failure: xkb_state_unref(self->state); state_failure: xkb_keymap_unref(self->keymap); keymap_failure: intset_destroy(&self->key_state); key_state_failure: xkb_context_unref(self->context); return -1; } void keyboard_destroy(struct keyboard* self) { free(self->lookup_table); xkb_state_unref(self->state); xkb_keymap_unref(self->keymap); intset_destroy(&self->key_state); xkb_context_unref(self->context); } struct table_entry* keyboard_find_symbol(const struct keyboard* self, xkb_keysym_t symbol) { struct table_entry cmp = { .symbol = symbol }; struct table_entry* entry = bsearch(&cmp, self->lookup_table, self->lookup_table_length, sizeof(*self->lookup_table), compare_symbols2); if (!entry) return NULL; while (entry != self->lookup_table && (entry - 1)->symbol == symbol) --entry; return entry; } static void keyboard_send_mods(struct keyboard* self) { xkb_mod_mask_t depressed, latched, locked, group; depressed = xkb_state_serialize_mods(self->state, XKB_STATE_MODS_DEPRESSED); latched = xkb_state_serialize_mods(self->state, XKB_STATE_MODS_LATCHED); locked = xkb_state_serialize_mods(self->state, XKB_STATE_MODS_LOCKED); group = xkb_state_serialize_mods(self->state, XKB_STATE_MODS_EFFECTIVE); zwp_virtual_keyboard_v1_modifiers(self->virtual_keyboard, depressed, latched, locked, group); } static void keyboard_apply_mods(struct keyboard* self, xkb_keycode_t code, bool is_pressed) { enum xkb_state_component comp, compmask; comp = xkb_state_update_key(self->state, code, is_pressed ? XKB_KEY_DOWN : XKB_KEY_UP); compmask = XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED | XKB_STATE_MODS_LOCKED | XKB_STATE_MODS_EFFECTIVE; if (!(comp & compmask)) return; keyboard_send_mods(self); } static struct table_entry* match_level(struct keyboard* self, struct table_entry* entry) { xkb_keysym_t symbol = entry->symbol; while (true) { int level; level = xkb_state_key_get_level(self->state, entry->code, 0); if (entry->level == level) return entry; if (++entry >= &self->lookup_table[self->lookup_table_length] || entry->symbol != symbol) break; } return NULL; } static bool keyboard_symbol_is_mod(xkb_keysym_t symbol) { switch (symbol) { case XKB_KEY_Shift_L: case XKB_KEY_Shift_R: case XKB_KEY_Control_L: case XKB_KEY_Caps_Lock: case XKB_KEY_Shift_Lock: case XKB_KEY_Meta_L: case XKB_KEY_Meta_R: case XKB_KEY_Alt_L: case XKB_KEY_Alt_R: case XKB_KEY_Super_L: case XKB_KEY_Super_R: case XKB_KEY_Hyper_L: case XKB_KEY_Hyper_R: case XKB_KEY_ISO_Level5_Shift: case XKB_KEY_ISO_Level5_Lock: return true; } return false; } static void send_key(struct keyboard* self, xkb_keycode_t code, bool is_pressed) { zwp_virtual_keyboard_v1_key(self->virtual_keyboard, 0, code - 8, is_pressed ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED); } static void save_mods(struct keyboard* self, struct kb_mods* mods) { mods->depressed = xkb_state_serialize_mods(self->state, XKB_STATE_MODS_DEPRESSED); mods->latched = xkb_state_serialize_mods(self->state, XKB_STATE_MODS_LATCHED); mods->locked = xkb_state_serialize_mods(self->state, XKB_STATE_MODS_LOCKED); } static void restore_mods(struct keyboard* self, struct kb_mods* mods) { xkb_state_update_mask(self->state, mods->depressed, mods->latched, mods->locked, XKB_STATE_MODS_DEPRESSED, XKB_STATE_MODS_LATCHED, XKB_STATE_MODS_LOCKED); } static void send_key_with_level(struct keyboard* self, xkb_keycode_t code, bool is_pressed, int level) { struct kb_mods save; save_mods(self, &save); xkb_mod_mask_t mods = 0; xkb_keymap_key_get_mods_for_level(self->keymap, code, 0, level, &mods, 1); xkb_state_update_mask(self->state, mods, 0, 0, XKB_STATE_MODS_DEPRESSED, XKB_STATE_MODS_LATCHED, XKB_STATE_MODS_LOCKED); keyboard_send_mods(self); log_debug("send key with level: old mods: %x, new mods: %x\n", save.latched | save.locked | save.depressed, mods); send_key(self, code, is_pressed); restore_mods(self, &save); keyboard_send_mods(self); } static bool update_key_state(struct keyboard* self, xkb_keycode_t code, bool is_pressed) { bool was_pressed = intset_is_set(&self->key_state, code); if (was_pressed == is_pressed) return false; if (is_pressed) intset_set(&self->key_state, code); else intset_clear(&self->key_state, code); return true; } void keyboard_feed(struct keyboard* self, xkb_keysym_t symbol, bool is_pressed) { struct table_entry* entry = keyboard_find_symbol(self, symbol); if (!entry) { char name[256]; log_error("Failed to look up keyboard symbol: %s\n", get_symbol_name(symbol, name, sizeof(name))); return; } bool level_is_match = true; if (!keyboard_symbol_is_mod(symbol)) { struct table_entry* level_entry = match_level(self, entry); if (level_entry) entry = level_entry; else level_is_match = false; } #ifndef NDEBUG keyboard__dump_entry(self, entry); #endif if (!update_key_state(self, entry->code, is_pressed)) return; keyboard_apply_mods(self, entry->code, is_pressed); if (level_is_match) send_key(self, entry->code, is_pressed); else send_key_with_level(self, entry->code, is_pressed, entry->level); } void keyboard_feed_code(struct keyboard* self, xkb_keycode_t code, bool is_pressed) { if (update_key_state(self, code, is_pressed)) { keyboard_apply_mods(self, code, is_pressed); send_key(self, code, is_pressed); } } wayvnc-0.4.1/src/main.c000066400000000000000000000634721415671750500147310ustar00rootroot00000000000000/* * Copyright (c) 2019 - 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wlr-screencopy-unstable-v1.h" #include "wlr-virtual-pointer-unstable-v1.h" #include "virtual-keyboard-unstable-v1.h" #include "xdg-output-unstable-v1.h" #include "linux-dmabuf-unstable-v1.h" #include "screencopy.h" #include "data-control.h" #include "strlcpy.h" #include "logging.h" #include "output.h" #include "pointer.h" #include "keyboard.h" #include "seat.h" #include "cfg.h" #include "pixman-renderer.h" #include "transform-util.h" #include "damage-refinery.h" #include "usdt.h" #ifdef ENABLE_PAM #include "pam_auth.h" #endif #ifdef ENABLE_SCREENCOPY_DMABUF #include #include #endif #define DEFAULT_ADDRESS "127.0.0.1" #define DEFAULT_PORT 5900 #define MAYBE_UNUSED __attribute__((unused)) struct wayvnc { bool do_exit; struct wl_display* display; struct wl_registry* registry; struct wl_list outputs; struct wl_list seats; struct cfg cfg; struct zxdg_output_manager_v1* xdg_output_manager; struct zwp_virtual_keyboard_manager_v1* keyboard_manager; struct zwlr_virtual_pointer_manager_v1* pointer_manager; const struct output* selected_output; const struct seat* selected_seat; struct screencopy screencopy; struct pointer pointer_backend; struct keyboard keyboard_backend; struct data_control data_control; struct aml_handler* wayland_handler; struct aml_signal* signal_handler; struct nvnc* nvnc; struct nvnc_display* nvnc_display; struct nvnc_fb* buffer; struct damage_refinery damage_refinery; struct pixman_region16 current_damage; const char* kb_layout; const char* kb_variant; uint32_t damage_area_sum; uint32_t n_frames_captured; uint32_t n_frames_rendered; }; void wayvnc_exit(struct wayvnc* self); void on_capture_done(struct screencopy* sc); static void on_render(struct nvnc_display* display, struct nvnc_fb* fb); #if defined(GIT_VERSION) static const char wayvnc_version[] = GIT_VERSION; #elif defined(PROJECT_VERSION) static const char wayvnc_version[] = PROJECT_VERSION; #else static const char wayvnc_version[] = "UNKNOWN"; #endif struct wl_shm* wl_shm = NULL; struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf = NULL; struct gbm_device* gbm_device = NULL; static void registry_add(void* data, struct wl_registry* registry, uint32_t id, const char* interface, uint32_t version) { struct wayvnc* self = data; if (strcmp(interface, wl_output_interface.name) == 0) { struct wl_output* wl_output = wl_registry_bind(registry, id, &wl_output_interface, 3); if (!wl_output) return; struct output* output = output_new(wl_output, id); if (!output) return; wl_list_insert(&self->outputs, &output->link); return; } if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { self->xdg_output_manager = wl_registry_bind(registry, id, &zxdg_output_manager_v1_interface, 3); return; } if (strcmp(interface, wl_shm_interface.name) == 0) { wl_shm = wl_registry_bind(registry, id, &wl_shm_interface, 1); return; } if (strcmp(interface, zwlr_screencopy_manager_v1_interface.name) == 0) { self->screencopy.manager = wl_registry_bind(registry, id, &zwlr_screencopy_manager_v1_interface, MIN(3, version)); return; } if (strcmp(interface, zwlr_virtual_pointer_manager_v1_interface.name) == 0) { self->pointer_manager = wl_registry_bind(registry, id, &zwlr_virtual_pointer_manager_v1_interface, MIN(2, version)); return; }; if (strcmp(interface, wl_seat_interface.name) == 0) { struct wl_seat* wl_seat = wl_registry_bind(registry, id, &wl_seat_interface, 7); if (!wl_seat) return; struct seat* seat = seat_new(wl_seat, id); if (!seat) { wl_seat_destroy(wl_seat); return; } wl_list_insert(&self->seats, &seat->link); return; } if (strcmp(interface, zwp_virtual_keyboard_manager_v1_interface.name) == 0) { self->keyboard_manager = wl_registry_bind(registry, id, &zwp_virtual_keyboard_manager_v1_interface, 1); return; } if (strcmp(interface, zwp_linux_dmabuf_v1_interface.name) == 0) { zwp_linux_dmabuf = wl_registry_bind(registry, id, &zwp_linux_dmabuf_v1_interface, 3); return; } if (strcmp(interface, zwlr_data_control_manager_v1_interface.name) == 0) { self->data_control.manager = wl_registry_bind(registry, id, &zwlr_data_control_manager_v1_interface, 2); return; } } static void registry_remove(void* data, struct wl_registry* registry, uint32_t id) { struct wayvnc* self = data; struct output* out = output_find_by_id(&self->outputs, id); if (out) { wl_list_remove(&out->link); output_destroy(out); if (out == self->selected_output) { log_error("Selected output went away. Exiting...\n"); wayvnc_exit(self); } return; } struct seat* seat = seat_find_by_id(&self->seats, id); if (seat) { wl_list_remove(&seat->link); seat_destroy(seat); if (seat == self->selected_seat) { log_error("Selected seat went away. Exiting...\n"); wayvnc_exit(self); } return; } } #ifdef ENABLE_SCREENCOPY_DMABUF static int find_render_node(char *node, size_t maxlen) { bool r = -1; drmDevice *devices[64]; int n = drmGetDevices2(0, devices, sizeof(devices) / sizeof(devices[0])); for (int i = 0; i < n; ++i) { drmDevice *dev = devices[i]; if (!(dev->available_nodes & (1 << DRM_NODE_RENDER))) continue; strlcpy(node, dev->nodes[DRM_NODE_RENDER], maxlen); r = 0; break; } drmFreeDevices(devices, n); return r; } static int init_render_node(int* fd) { char render_node[256]; if (find_render_node(render_node, sizeof(render_node)) < 0) return -1; *fd = open(render_node, O_RDWR); if (*fd < 0) return -1; gbm_device = gbm_create_device(*fd); if (!gbm_device) { close(*fd); return -1; } return 0; } #endif void wayvnc_destroy(struct wayvnc* self) { cfg_destroy(&self->cfg); output_list_destroy(&self->outputs); seat_list_destroy(&self->seats); zxdg_output_manager_v1_destroy(self->xdg_output_manager); wl_shm_destroy(wl_shm); zwp_virtual_keyboard_v1_destroy(self->keyboard_backend.virtual_keyboard); zwp_virtual_keyboard_manager_v1_destroy(self->keyboard_manager); keyboard_destroy(&self->keyboard_backend); zwlr_virtual_pointer_manager_v1_destroy(self->pointer_manager); pointer_destroy(&self->pointer_backend); if (self->screencopy.manager) zwlr_screencopy_manager_v1_destroy(self->screencopy.manager); if (self->data_control.manager) zwlr_data_control_manager_v1_destroy(self->data_control.manager); wl_registry_destroy(self->registry); wl_display_disconnect(self->display); } static void init_xdg_outputs(struct wayvnc* self) { struct output* output; wl_list_for_each(output, &self->outputs, link) { struct zxdg_output_v1* xdg_output = zxdg_output_manager_v1_get_xdg_output( self->xdg_output_manager, output->wl_output); output_set_xdg_output(output, xdg_output); } } static int init_wayland(struct wayvnc* self) { static const struct wl_registry_listener registry_listener = { .global = registry_add, .global_remove = registry_remove, }; self->display = wl_display_connect(NULL); if (!self->display) return -1; wl_list_init(&self->outputs); wl_list_init(&self->seats); self->registry = wl_display_get_registry(self->display); if (!self->registry) goto failure; wl_registry_add_listener(self->registry, ®istry_listener, self); wl_display_dispatch(self->display); wl_display_roundtrip(self->display); init_xdg_outputs(self); if (!self->pointer_manager) { log_error("Virtual Pointer protocol not supported by compositor.\n"); goto failure; } if (!self->keyboard_manager) { log_error("Virtual Keyboard protocol not supported by compositor.\n"); goto failure; } wl_display_dispatch(self->display); wl_display_roundtrip(self->display); if (!self->screencopy.manager) { log_error("Compositor doesn't support screencopy! Exiting.\n"); goto failure; } self->screencopy.on_done = on_capture_done; self->screencopy.userdata = self; return 0; failure: wl_display_disconnect(self->display); return -1; } void on_wayland_event(void* obj) { struct wayvnc* self = aml_get_userdata(obj); int rc MAYBE_UNUSED = wl_display_prepare_read(self->display); assert(rc == 0); if (wl_display_read_events(self->display) < 0) { if (errno == EPIPE || errno == ECONNRESET) { log_error("Compositor has gone away. Exiting...\n"); wayvnc_exit(self); } else { log_error("Failed to read wayland events: %m\n"); } } if (wl_display_dispatch_pending(self->display) < 0) log_error("Failed to dispatch pending\n"); } void wayvnc_exit(struct wayvnc* self) { self->do_exit = true; } void on_signal(void* obj) { struct wayvnc* self = aml_get_userdata(obj); wayvnc_exit(self); } int init_main_loop(struct wayvnc* self) { struct aml* loop = aml_get_default(); struct aml_handler* wl_handler; wl_handler = aml_handler_new(wl_display_get_fd(self->display), on_wayland_event, self, NULL); if (!wl_handler) return -1; int rc = aml_start(loop, wl_handler); aml_unref(wl_handler); if (rc < 0) return -1; struct aml_signal* sig; sig = aml_signal_new(SIGINT, on_signal, self, NULL); if (!sig) return -1; rc = aml_start(loop, sig); aml_unref(sig); if (rc < 0) return -1; return 0; } static void on_pointer_event(struct nvnc_client* client, uint16_t x, uint16_t y, enum nvnc_button_mask button_mask) { // TODO: Have a seat per client struct nvnc* nvnc = nvnc_client_get_server(client); struct wayvnc* wayvnc = nvnc_get_userdata(nvnc); uint32_t xfx = 0, xfy = 0; output_transform_coord(wayvnc->selected_output, x, y, &xfx, &xfy); pointer_set(&wayvnc->pointer_backend, xfx, xfy, button_mask); } static void on_key_event(struct nvnc_client* client, uint32_t symbol, bool is_pressed) { struct nvnc* nvnc = nvnc_client_get_server(client); struct wayvnc* wayvnc = nvnc_get_userdata(nvnc); keyboard_feed(&wayvnc->keyboard_backend, symbol, is_pressed); } static void on_key_code_event(struct nvnc_client* client, uint32_t code, bool is_pressed) { struct nvnc* nvnc = nvnc_client_get_server(client); struct wayvnc* wayvnc = nvnc_get_userdata(nvnc); keyboard_feed_code(&wayvnc->keyboard_backend, code + 8, is_pressed); } static void on_client_cut_text(struct nvnc* server, const char* text, uint32_t len) { struct wayvnc* wayvnc = nvnc_get_userdata(server); data_control_to_clipboard(&wayvnc->data_control, text, len); } bool on_auth(const char* username, const char* password, void* ud) { struct wayvnc* self = ud; #ifdef ENABLE_PAM if (self->cfg.enable_pam) return pam_auth(username, password); #endif if (strcmp(username, self->cfg.username) != 0) return false; if (strcmp(password, self->cfg.password) != 0) return false; return true; } int init_nvnc(struct wayvnc* self, const char* addr, uint16_t port) { self->nvnc = nvnc_open(addr, port); if (!self->nvnc) { log_error("Failed to bind to address\n"); return -1; } self->nvnc_display = nvnc_display_new(0, 0); if (!self->nvnc_display) goto failure; nvnc_add_display(self->nvnc, self->nvnc_display); nvnc_set_userdata(self->nvnc, self); nvnc_set_name(self->nvnc, "WayVNC"); nvnc_display_set_render_fn(self->nvnc_display, on_render); if (self->cfg.enable_auth && nvnc_enable_auth(self->nvnc, self->cfg.private_key_file, self->cfg.certificate_file, on_auth, self) < 0) { log_error("Failed to enable authentication\n"); goto failure; } if (self->pointer_manager) nvnc_set_pointer_fn(self->nvnc, on_pointer_event); if (self->keyboard_backend.virtual_keyboard) { nvnc_set_key_fn(self->nvnc, on_key_event); nvnc_set_key_code_fn(self->nvnc, on_key_code_event); } nvnc_set_cut_text_receive_fn(self->nvnc, on_client_cut_text); return 0; failure: nvnc_close(self->nvnc); return -1; } int wayvnc_start_capture(struct wayvnc* self) { int rc = screencopy_start(&self->screencopy); if (rc < 0) { log_error("Failed to start capture. Exiting...\n"); wayvnc_exit(self); } return rc; } int wayvnc_start_capture_immediate(struct wayvnc* self) { int rc = screencopy_start_immediate(&self->screencopy); if (rc < 0) { log_error("Failed to start capture. Exiting...\n"); wayvnc_exit(self); } return rc; } static void on_render(struct nvnc_display* display, struct nvnc_fb* fb) { struct nvnc* nvnc = nvnc_display_get_server(display); struct wayvnc* self = nvnc_get_userdata(nvnc); if (!self->screencopy.back) return; self->n_frames_rendered++; DTRACE_PROBE(wayvnc, render_start); enum wl_output_transform transform = self->selected_output->transform; wv_pixman_render(fb, self->screencopy.back, transform, &self->current_damage); pixman_region_clear(&self->current_damage); DTRACE_PROBE(wayvnc, render_end); } static void wayvnc_damage_region(struct wayvnc* self, struct pixman_region16* damage) { pixman_region_union(&self->current_damage, &self->current_damage, damage); nvnc_display_damage_region(self->nvnc_display, damage); } // TODO: Handle transform change too void on_output_dimension_change(struct output* output) { struct wayvnc* self = output->userdata; assert(self->selected_output == output); // The buffer must have been set as a result of a screencopy frame if (!self->buffer) return; log_debug("Output dimensions changed. Restarting frame capturer...\n"); screencopy_stop(&self->screencopy); wayvnc_start_capture_immediate(self); } static uint32_t calculate_region_area(struct pixman_region16* region) { uint32_t area = 0; int n_rects = 0; struct pixman_box16* rects = pixman_region_rectangles(region, &n_rects); for (int i = 0; i < n_rects; ++i) { int width = rects[i].x2 - rects[i].x1; int height = rects[i].y2 - rects[i].y1; area += width * height; } return area; } void wayvnc_process_frame(struct wayvnc* self) { uint32_t width = output_get_transformed_width(self->selected_output); uint32_t height = output_get_transformed_height(self->selected_output); uint32_t format = self->screencopy.back->format; if ((int)self->selected_output->width != self->screencopy.back->width || (int)self->selected_output->height != self->screencopy.back->height) { log_debug("Frame dimensions don't match output. Recapturing frame...\n"); wayvnc_start_capture_immediate(self); return; } if (self->buffer && (width != nvnc_fb_get_width(self->buffer) || height != nvnc_fb_get_height(self->buffer))) { log_debug("Frame dimensions changed. Resizing...\n"); nvnc_fb_unref(self->buffer); self->buffer = NULL; damage_refinery_destroy(&self->damage_refinery); } // TODO: Maybe the buffer needs to be changed inside on_render? if (!self->buffer) { self->buffer = nvnc_fb_new(width, height, format); nvnc_display_set_buffer(self->nvnc_display, self->buffer); log_debug("Set nvnc buffer: %p\n", self->buffer); damage_refinery_init(&self->damage_refinery, self->screencopy.back->width, self->screencopy.back->height); } self->n_frames_captured++; self->damage_area_sum += calculate_region_area(&self->screencopy.back->damage); DTRACE_PROBE(wayvnc, refine_damage_start); struct pixman_region16 txdamage, refined; pixman_region_init(&txdamage); pixman_region_init(&refined); damage_refine(&self->damage_refinery, &refined, &self->screencopy.back->damage, self->screencopy.back); wv_region_transform(&txdamage, &refined, self->selected_output->transform, self->selected_output->width, self->selected_output->height); // damage_dump(stdout, &txdamage, width, height, 32); wayvnc_damage_region(self, &txdamage); pixman_region_fini(&refined); pixman_region_fini(&txdamage); DTRACE_PROBE(wayvnc, refine_damage_end); wayvnc_start_capture(self); } void on_capture_done(struct screencopy* sc) { struct wayvnc* self = sc->userdata; switch (sc->status) { case SCREENCOPY_STOPPED: break; case SCREENCOPY_IN_PROGRESS: break; case SCREENCOPY_FATAL: log_error("Fatal error while capturing. Exiting...\n"); wayvnc_exit(self); break; case SCREENCOPY_FAILED: wayvnc_start_capture_immediate(self); break; case SCREENCOPY_DONE: wayvnc_process_frame(self); break; } } int wayvnc_usage(FILE* stream, int rc) { static const char* usage = "Usage: wayvnc [options] [address [port]]\n" "\n" " -C,--config= Select a config file.\n" " -o,--output= Select output to capture.\n" " -k,--keyboard=[-] Select keyboard layout with an\n" " optional variant.\n" " -s,--seat= Select seat by name.\n" " -r,--render-cursor Enable overlay cursor rendering.\n" " -f,--max-fps= Set the rate limit (default 30).\n" " -p,--show-performance Show performance counters.\n" " -V,--version Show version info.\n" " -h,--help Get help (this text).\n" "\n"; fprintf(stream, "%s", usage); return rc; } int check_cfg_sanity(struct cfg* cfg) { if (cfg->enable_auth) { int rc = 0; if (!nvnc_has_auth()) { log_error("Authentication can't be enabled because it was not selected during build\n"); return -1; } if (!cfg->certificate_file) { log_error("Authentication enabled, but missing certificate_file\n"); rc = -1; } if (!cfg->private_key_file) { log_error("Authentication enabled, but missing private_key_file\n"); rc = -1; } if (!cfg->username && !cfg->enable_pam) { log_error("Authentication enabled, but missing username\n"); rc = -1; } if (!cfg->password && !cfg->enable_pam) { log_error("Authentication enabled, but missing password\n"); rc = -1; } return rc; } return 0; } static void on_perf_tick(void* obj) { struct wayvnc* self = aml_get_userdata(obj); double total_area = self->selected_output->width * self->selected_output->height; double area_avg = (double)self->damage_area_sum / (double)self->n_frames_captured; double relative_area_avg = 100.0 * area_avg / total_area; printf("Frames captured: %"PRIu32", rendered: %"PRIu32", average reported frame damage: %.1f %%\n", self->n_frames_captured, self->n_frames_rendered, relative_area_avg); self->n_frames_captured = 0; self->n_frames_rendered = 0; self->damage_area_sum = 0; } static void start_performance_ticker(struct wayvnc* self) { struct aml_ticker* ticker = aml_ticker_new(1000, on_perf_tick, self, NULL); if (!ticker) return; aml_start(aml_get_default(), ticker); aml_unref(ticker); } void parse_keyboard_option(struct wayvnc* self, char* arg) { // Find optional variant, separated by - char* index = strchr(arg, '-'); if (index != NULL) { self->kb_variant = index + 1; // layout needs to be 0-terminated, replace the - by 0 *index = 0; } self->kb_layout = arg; } int show_version(void) { printf("wayvnc: %s\n", wayvnc_version); printf("neatvnc: %s\n", nvnc_version); printf("aml: %s\n", aml_version); return 0; } int main(int argc, char* argv[]) { struct wayvnc self = { 0 }; const char* cfg_file = NULL; const char* address = NULL; int port = 0; const char* output_name = NULL; const char* seat_name = NULL; bool overlay_cursor = false; bool show_performance = false; int max_rate = 30; static const char* shortopts = "C:o:k:s:rf:hpV"; int drm_fd MAYBE_UNUSED = -1; static const struct option longopts[] = { { "config", required_argument, NULL, 'C' }, { "output", required_argument, NULL, 'o' }, { "keyboard", required_argument, NULL, 'k' }, { "seat", required_argument, NULL, 's' }, { "render-cursor", no_argument, NULL, 'r' }, { "max-fps", required_argument, NULL, 'f' }, { "help", no_argument, NULL, 'h' }, { "show-performance", no_argument, NULL, 'p' }, { "version", no_argument, NULL, 'V' }, { NULL, 0, NULL, 0 } }; while (1) { int c = getopt_long(argc, argv, shortopts, longopts, NULL); if (c < 0) break; switch (c) { case 'C': cfg_file = optarg; break; case 'o': output_name = optarg; break; case 'k': parse_keyboard_option(&self, optarg); break; case 's': seat_name = optarg; break; case 'r': overlay_cursor = true; break; case 'f': max_rate = atoi(optarg); break; case 'p': show_performance = true; break; case 'V': return show_version(); case 'h': return wayvnc_usage(stdout, 0); default: return wayvnc_usage(stderr, 1); } } int n_args = argc - optind; if (n_args >= 1) address = argv[optind]; if (n_args >= 2) port = atoi(argv[optind + 1]); errno = 0; int cfg_rc = cfg_load(&self.cfg, cfg_file); if (cfg_rc != 0 && (cfg_file || errno != ENOENT)) { if (cfg_rc > 0) { log_error("Failed to load config. Error on line %d\n", cfg_rc); } else { log_error("Failed to load config. %m\n"); } return 1; } if (check_cfg_sanity(&self.cfg) < 0) return 1; if (cfg_rc == 0) { if (!address) address = self.cfg.address; if (!port) port = self.cfg.port; } if (!address) address = DEFAULT_ADDRESS; if (!port) port = DEFAULT_PORT; if (init_wayland(&self) < 0) { log_error("Failed to initialise wayland\n"); return 1; } struct output* out; if (output_name) { out = output_find_by_name(&self.outputs, output_name); if (!out) { log_error("No such output\n"); goto failure; } } else { out = output_first(&self.outputs); if (!out) { log_error("No output found\n"); goto failure; } } struct seat* seat; if (seat_name) { seat = seat_find_by_name(&self.seats, seat_name); if (!seat) { log_error("No such seat\n"); goto failure; } } else { seat = seat_first(&self.seats); if (!seat) { log_error("No seat found\n"); goto failure; } } self.selected_output = out; self.selected_seat = seat; self.screencopy.wl_output = out->wl_output; self.screencopy.rate_limit = max_rate; self.keyboard_backend.virtual_keyboard = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard( self.keyboard_manager, self.selected_seat->wl_seat); struct xkb_rule_names rule_names = { .rules = self.cfg.xkb_rules, .layout = self.kb_layout ? self.kb_layout : self.cfg.xkb_layout, .model = self.cfg.xkb_model ? self.cfg.xkb_model : "pc105", .variant = self.kb_variant ? self.kb_variant : self.cfg.xkb_variant, .options = self.cfg.xkb_options, }; keyboard_init(&self.keyboard_backend, &rule_names); self.pointer_backend.vnc = self.nvnc; self.pointer_backend.output = self.selected_output; int pointer_manager_version = zwlr_virtual_pointer_manager_v1_get_version(self.pointer_manager); self.pointer_backend.pointer = pointer_manager_version >= 2 ? zwlr_virtual_pointer_manager_v1_create_virtual_pointer_with_output( self.pointer_manager, self.selected_seat->wl_seat, out->wl_output) : zwlr_virtual_pointer_manager_v1_create_virtual_pointer( self.pointer_manager, self.selected_seat->wl_seat); pointer_init(&self.pointer_backend); out->on_dimension_change = on_output_dimension_change; out->userdata = &self; #ifdef ENABLE_SCREENCOPY_DMABUF if (init_render_node(&drm_fd) < 0) { log_error("Failed to initialise DRM render node. No GPU acceleration will be available.\n"); } #endif struct aml* aml = aml_new(); if (!aml) goto main_loop_failure; aml_set_default(aml); if (init_main_loop(&self) < 0) goto main_loop_failure; if (init_nvnc(&self, address, port) < 0) goto nvnc_failure; if (self.screencopy.manager) screencopy_init(&self.screencopy); if (!self.screencopy.manager) { log_error("screencopy is not supported by compositor\n"); goto capture_failure; } if (self.data_control.manager) data_control_init(&self.data_control, self.display, self.nvnc, self.selected_seat->wl_seat); pixman_region_init(&self.current_damage); self.screencopy.overlay_cursor = overlay_cursor; if (wayvnc_start_capture(&self) < 0) goto capture_failure; if (show_performance) start_performance_ticker(&self); wl_display_dispatch(self.display); while (!self.do_exit) { wl_display_flush(self.display); aml_poll(aml, -1); aml_dispatch(aml); } screencopy_stop(&self.screencopy); if (self.buffer) { damage_refinery_destroy(&self.damage_refinery); nvnc_fb_unref(self.buffer); } pixman_region_fini(&self.current_damage); nvnc_display_unref(self.nvnc_display); nvnc_close(self.nvnc); if (zwp_linux_dmabuf) zwp_linux_dmabuf_v1_destroy(zwp_linux_dmabuf); if (self.screencopy.manager) screencopy_destroy(&self.screencopy); if (self.data_control.manager) data_control_destroy(&self.data_control); #ifdef ENABLE_SCREENCOPY_DMABUF if (gbm_device) { gbm_device_destroy(gbm_device); close(drm_fd); } #endif wayvnc_destroy(&self); aml_unref(aml); return 0; capture_failure: nvnc_display_unref(self.nvnc_display); nvnc_close(self.nvnc); nvnc_failure: aml_unref(aml); main_loop_failure: failure: #ifdef ENABLE_SCREENCOPY_DMABUF if (gbm_device) gbm_device_destroy(gbm_device); if (drm_fd >= 0) close(drm_fd); #endif wayvnc_destroy(&self); return 1; } wayvnc-0.4.1/src/murmurhash.c000066400000000000000000000047051415671750500161720ustar00rootroot00000000000000/* * The MIT License (MIT) * * Copyright (c) 2014 Joseph Werle * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include "murmurhash.h" uint32_t murmurhash (const char *key, uint32_t len, uint32_t seed) { uint32_t c1 = 0xcc9e2d51; uint32_t c2 = 0x1b873593; uint32_t r1 = 15; uint32_t r2 = 13; uint32_t m = 5; uint32_t n = 0xe6546b64; uint32_t h = 0; uint32_t k = 0; uint8_t *d = (uint8_t *) key; // 32 bit extract from `key' const uint32_t *chunks = NULL; const uint8_t *tail = NULL; // tail - last 8 bytes int i = 0; int l = len / 4; // chunk length h = seed; chunks = (const uint32_t *) (d + l * 4); // body tail = (const uint8_t *) (d + l * 4); // last 8 byte chunk of `key' // for each 4 byte chunk of `key' for (i = -l; i != 0; ++i) { // next 4 byte chunk of `key' k = chunks[i]; // encode next 4 byte chunk of `key' k *= c1; k = (k << r1) | (k >> (32 - r1)); k *= c2; // append to hash h ^= k; h = (h << r2) | (h >> (32 - r2)); h = h * m + n; } k = 0; // remainder switch (len & 3) { // `len % 4' case 3: k ^= (tail[2] << 16); case 2: k ^= (tail[1] << 8); case 1: k ^= tail[0]; k *= c1; k = (k << r1) | (k >> (32 - r1)); k *= c2; h ^= k; } h ^= len; h ^= (h >> 16); h *= 0x85ebca6b; h ^= (h >> 13); h *= 0xc2b2ae35; h ^= (h >> 16); return h; } wayvnc-0.4.1/src/output.c000066400000000000000000000162101415671750500153310ustar00rootroot00000000000000/* * Copyright (c) 2019 - 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "output.h" #include "strlcpy.h" #include "logging.h" #include "xdg-output-unstable-v1.h" #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) void output_transform_coord(const struct output* self, uint32_t src_x, uint32_t src_y, uint32_t* dst_x, uint32_t* dst_y) { switch (self->transform) { case WL_OUTPUT_TRANSFORM_NORMAL: *dst_x = src_x; *dst_y = src_y; break; case WL_OUTPUT_TRANSFORM_90: *dst_x = src_y; *dst_y = self->height - src_x; break; case WL_OUTPUT_TRANSFORM_180: *dst_x = self->width - src_x; *dst_y = self->height - src_y; break; case WL_OUTPUT_TRANSFORM_270: *dst_x = self->width - src_y; *dst_y = src_x; break; case WL_OUTPUT_TRANSFORM_FLIPPED: *dst_x = self->width - src_x; *dst_y = src_y; break; case WL_OUTPUT_TRANSFORM_FLIPPED_90: *dst_x = src_y; *dst_y = src_x; break; case WL_OUTPUT_TRANSFORM_FLIPPED_180: *dst_x = src_x; *dst_y = self->height - src_y; break; case WL_OUTPUT_TRANSFORM_FLIPPED_270: *dst_x = self->width - src_y; *dst_y = self->height - src_x; break; } } void output_transform_box_coord(const struct output* self, uint32_t src_x0, uint32_t src_y0, uint32_t src_x1, uint32_t src_y1, uint32_t* dst_x0, uint32_t* dst_y0, uint32_t* dst_x1, uint32_t* dst_y1) { uint32_t x0 = 0, y0 = 0, x1 = 0, y1 = 0; output_transform_coord(self, src_x0, src_y0, &x0, &y0); output_transform_coord(self, src_x1, src_y1, &x1, &y1); *dst_x0 = MIN(x0, x1); *dst_x1 = MAX(x0, x1); *dst_y0 = MIN(y0, y1); *dst_y1 = MAX(y0, y1); } static bool is_transform_90_degrees(enum wl_output_transform transform) { switch (transform) { case WL_OUTPUT_TRANSFORM_90: case WL_OUTPUT_TRANSFORM_270: case WL_OUTPUT_TRANSFORM_FLIPPED_90: case WL_OUTPUT_TRANSFORM_FLIPPED_270: return true; default: break; } return false; } uint32_t output_get_transformed_width(const struct output* self) { return is_transform_90_degrees(self->transform) ? self->height : self->width; } uint32_t output_get_transformed_height(const struct output* self) { return is_transform_90_degrees(self->transform) ? self->width : self->height; } static void output_handle_geometry(void* data, struct wl_output* wl_output, int32_t x, int32_t y, int32_t phys_width, int32_t phys_height, int32_t subpixel, const char* make, const char* model, int32_t transform) { struct output* output = data; if (transform != (int32_t)output->transform) output->is_transform_changed = true; output->x = x; output->y = y; output->transform = transform; strlcpy(output->make, make, sizeof(output->make)); strlcpy(output->model, model, sizeof(output->model)); } static void output_handle_mode(void* data, struct wl_output* wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { struct output* output = data; if (!(flags & WL_OUTPUT_MODE_CURRENT)) return; if (width != (int32_t)output->width || height != (int32_t)output->height) output->is_dimension_changed = true; output->width = width; output->height = height; } static void output_handle_done(void* data, struct wl_output* wl_output) { struct output* output = data; if (output->is_dimension_changed && output->on_dimension_change) output->on_dimension_change(output); if (output->is_transform_changed && output->on_transform_change) output->on_transform_change(output); output->is_dimension_changed = false; output->is_transform_changed = false; } static void output_handle_scale(void* data, struct wl_output* wl_output, int32_t factor) { } static const struct wl_output_listener output_listener = { .geometry = output_handle_geometry, .mode = output_handle_mode, .done = output_handle_done, .scale = output_handle_scale, }; void output_destroy(struct output* output) { zxdg_output_v1_destroy(output->xdg_output); wl_output_destroy(output->wl_output); free(output); } void output_list_destroy(struct wl_list* list) { struct output* output; struct output* tmp; wl_list_for_each_safe(output, tmp, list, link) { wl_list_remove(&output->link); output_destroy(output); } } struct output* output_new(struct wl_output* wl_output, uint32_t id) { struct output* output = calloc(1, sizeof(*output)); if (!output) { log_error("OOM\n"); return NULL; } output->wl_output = wl_output; output->id = id; wl_output_add_listener(output->wl_output, &output_listener, output); return output; } void output_logical_position(void* data, struct zxdg_output_v1* xdg_output, int32_t x, int32_t y) { } void output_logical_size(void* data, struct zxdg_output_v1* xdg_output, int32_t width, int32_t height) { } void output_name(void* data, struct zxdg_output_v1* xdg_output, const char* name) { struct output* self = data; strlcpy(self->name, name, sizeof(self->name)); } void output_description(void* data, struct zxdg_output_v1* xdg_output, const char* description) { struct output* self = data; strlcpy(self->description, description, sizeof(self->description)); } static const struct zxdg_output_v1_listener xdg_output_listener = { .logical_position = output_logical_position, .logical_size = output_logical_size, .done = NULL, /* Deprecated */ .name = output_name, .description = output_description, }; void output_set_xdg_output(struct output* self, struct zxdg_output_v1* xdg_output) { self->xdg_output = xdg_output; zxdg_output_v1_add_listener(self->xdg_output, &xdg_output_listener, self); } struct output* output_find_by_id(struct wl_list* list, uint32_t id) { struct output* output; wl_list_for_each(output, list, link) if (output->id == id) return output; return NULL; } struct output* output_find_by_name(struct wl_list* list, const char* name) { struct output* output; wl_list_for_each(output, list, link) if (strcmp(output->name, name) == 0) return output; return NULL; } struct output* output_first(struct wl_list* list) { struct output* output; wl_list_for_each(output, list, link) return output; return output; } wayvnc-0.4.1/src/pam_auth.c000066400000000000000000000045441415671750500155760ustar00rootroot00000000000000/* * Copyright (c) 2020 Nicholas Sica * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "pam_auth.h" #include #include #include #include "logging.h" struct credentials { const char* user; const char* password; }; static int pam_return_pwd(int num_msg, const struct pam_message** msgm, struct pam_response** response, void* appdata_ptr) { struct credentials* cred = appdata_ptr; struct pam_response* resp = calloc(sizeof(*response), num_msg); for (int i = 0; i < num_msg; i++) { resp[i].resp_retcode = PAM_SUCCESS; switch(msgm[i]->msg_style) { case PAM_PROMPT_ECHO_OFF: resp[i].resp = strdup(cred->password); break; default: goto error; } } *response = resp; return PAM_SUCCESS; error: for (int i = 0; i < num_msg; i++) { free(resp[i].resp); } free(resp); return PAM_CONV_ERR; } bool pam_auth(const char* username, const char* password) { struct credentials cred = { username, password }; struct pam_conv conv = { &pam_return_pwd, &cred }; const char* service = "wayvnc"; pam_handle_t* pamh; int result = pam_start(service, username, &conv, &pamh); if (result != PAM_SUCCESS) { log_error("ERROR: PAM start failed: %s\n", pam_strerror(pamh, result)); return false; } result = pam_authenticate(pamh, PAM_SILENT|PAM_DISALLOW_NULL_AUTHTOK); if (result != PAM_SUCCESS) { log_error("PAM authenticate failed: %s\n", pam_strerror(pamh, result)); goto error; } result = pam_acct_mgmt(pamh, 0); if (result != PAM_SUCCESS) { log_error("PAM account management failed: %s\n", pam_strerror(pamh, result)); goto error; } error: pam_end(pamh, result); return result == PAM_SUCCESS; } wayvnc-0.4.1/src/pixels.c000066400000000000000000000055001415671750500152750ustar00rootroot00000000000000/* * Copyright (c) 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "pixels.h" #include #include #include #include #include enum wl_shm_format fourcc_to_wl_shm(uint32_t in) { assert(!(in & DRM_FORMAT_BIG_ENDIAN)); switch (in) { case DRM_FORMAT_ARGB8888: return WL_SHM_FORMAT_ARGB8888; case DRM_FORMAT_XRGB8888: return WL_SHM_FORMAT_XRGB8888; } return in; } uint32_t fourcc_from_wl_shm(enum wl_shm_format in) { switch (in) { case WL_SHM_FORMAT_ARGB8888: return DRM_FORMAT_ARGB8888; case WL_SHM_FORMAT_XRGB8888: return DRM_FORMAT_XRGB8888; default:; } return in; } bool fourcc_to_pixman_fmt(pixman_format_code_t* dst, uint32_t src) { assert(!(src & DRM_FORMAT_BIG_ENDIAN)); #define LOWER_R r #define LOWER_G g #define LOWER_B b #define LOWER_A a #define LOWER_X x #define LOWER_ #define LOWER(x) LOWER_##x #define CONCAT_(a, b) a ## b #define CONCAT(a, b) CONCAT_(a, b) #define FMT_DRM(x, y, z, v, a, b, c, d) DRM_FORMAT_##x##y##z##v##a##b##c##d #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define FMT_PIXMAN(x, y, z, v, a, b, c, d) \ CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(\ PIXMAN_, LOWER(x)), a), LOWER(y)), b), LOWER(z)), c), LOWER(v)), d) #else #define FMT_PIXMAN(x, y, z, v, a, b, c, d) \ CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(\ PIXMAN_, LOWER(v)), d), LOWER(z)), c), LOWER(y)), b), LOWER(x)), a) #endif switch (src) { #define X(...) \ case FMT_DRM(__VA_ARGS__): *dst = FMT_PIXMAN(__VA_ARGS__); break /* 32 bits */ X(A,R,G,B,8,8,8,8); X(A,B,G,R,8,8,8,8); X(X,R,G,B,8,8,8,8); X(X,B,G,R,8,8,8,8); X(R,G,B,A,8,8,8,8); X(B,G,R,A,8,8,8,8); X(R,G,B,X,8,8,8,8); X(B,G,R,X,8,8,8,8); /* 24 bits */ X(R,G,B,,8,8,8,); X(B,G,R,,8,8,8,); /* 16 bits */ X(R,G,B,,5,6,5,); X(B,G,R,,5,6,5,); /* These are incompatible on big endian */ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ X(A,R,G,B,1,5,5,5); X(A,B,G,R,1,5,5,5); X(X,R,G,B,1,5,5,5); X(X,B,G,R,1,5,5,5); X(A,R,G,B,4,4,4,4); X(A,B,G,R,4,4,4,4); X(X,R,G,B,4,4,4,4); X(X,B,G,R,4,4,4,4); #endif #undef X default: return false; } return true; } wayvnc-0.4.1/src/pixman-renderer.c000066400000000000000000000046161415671750500171000ustar00rootroot00000000000000/* * Copyright (c) 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "buffer.h" #include "pixels.h" #include "transform-util.h" void wv_pixman_render(struct nvnc_fb* dst, const struct wv_buffer* src, enum wl_output_transform transform, struct pixman_region16* damage) { uint32_t* dst_pixels = nvnc_fb_get_addr(dst); uint32_t dst_width = nvnc_fb_get_width(dst); uint32_t dst_height = nvnc_fb_get_height(dst); bool ok __attribute__((unused)); // TODO: Check that both buffers have the same dimensions after applying // transform pixman_format_code_t dst_fmt = 0; ok = fourcc_to_pixman_fmt(&dst_fmt, nvnc_fb_get_fourcc_format(dst)); assert(ok); pixman_image_t* dstimg = pixman_image_create_bits_no_clear( dst_fmt, dst_width, dst_height, dst_pixels, 4 * dst_width); intptr_t src_offset = src->y_inverted ? src->stride * (src->height - 1) : 0; void* src_pixels = (void*)((intptr_t)src->pixels + src_offset); int src_stride = src->y_inverted ? -src->stride : src->stride; pixman_format_code_t src_fmt = 0; ok = fourcc_to_pixman_fmt(&src_fmt, src->format); assert(ok); pixman_image_t* srcimg = pixman_image_create_bits_no_clear( src_fmt, src->width, src->height, src_pixels, src_stride); pixman_transform_t pxform; wv_pixman_transform_from_wl_output_transform(&pxform, transform, src->width, src->height); pixman_image_set_transform(srcimg, &pxform); pixman_image_set_clip_region(dstimg, damage); pixman_image_composite(PIXMAN_OP_OVER, srcimg, NULL, dstimg, 0, 0, 0, 0, 0, 0, dst_width, dst_height); pixman_image_unref(srcimg); pixman_image_unref(dstimg); } wayvnc-0.4.1/src/pointer.c000066400000000000000000000060101415671750500154460ustar00rootroot00000000000000/* * Copyright (c) 2019 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "pointer.h" #include "wlr-virtual-pointer-unstable-v1.h" #include "time-util.h" int pointer_init(struct pointer* self) { zwlr_virtual_pointer_v1_axis_source(self->pointer, WL_POINTER_AXIS_SOURCE_WHEEL); return 0; } void pointer_destroy(struct pointer* self) { zwlr_virtual_pointer_v1_destroy(self->pointer); } static void pointer_set_button_mask(struct pointer* self, uint32_t t, enum nvnc_button_mask mask) { enum nvnc_button_mask diff = self->current_mask ^ mask; if (diff & NVNC_BUTTON_LEFT) zwlr_virtual_pointer_v1_button(self->pointer, t, BTN_LEFT, !!(mask & NVNC_BUTTON_LEFT)); if (diff & NVNC_BUTTON_MIDDLE) zwlr_virtual_pointer_v1_button(self->pointer, t, BTN_MIDDLE, !!(mask & NVNC_BUTTON_MIDDLE)); if (diff & NVNC_BUTTON_RIGHT) zwlr_virtual_pointer_v1_button(self->pointer, t, BTN_RIGHT, !!(mask & NVNC_BUTTON_RIGHT)); int axis = WL_POINTER_AXIS_VERTICAL_SCROLL; /* I arrived at the magical value of 15 by connecting a mouse with a * scroll wheel and viewing the output of wev. */ if ((diff & NVNC_SCROLL_UP) && !(mask & NVNC_SCROLL_UP)) zwlr_virtual_pointer_v1_axis_discrete(self->pointer, t, axis, wl_fixed_from_int(-15), -1); if ((diff & NVNC_SCROLL_DOWN) && !(mask & NVNC_SCROLL_DOWN)) zwlr_virtual_pointer_v1_axis_discrete(self->pointer, t, axis, wl_fixed_from_int(15), 1); self->current_mask = mask; } void pointer_set(struct pointer* self, uint32_t x, uint32_t y, enum nvnc_button_mask button_mask) { uint32_t t = gettime_ms(); if (x != self->current_x || y != self->current_y) zwlr_virtual_pointer_v1_motion_absolute(self->pointer, t, x, y, self->output->width, self->output->height); self->current_x = x; self->current_y = y; if (button_mask != self->current_mask) pointer_set_button_mask(self, t, button_mask); zwlr_virtual_pointer_v1_frame(self->pointer); } wayvnc-0.4.1/src/screencopy.c000066400000000000000000000164771415671750500161620ustar00rootroot00000000000000/* * Copyright (c) 2019 - 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include "wlr-screencopy-unstable-v1.h" #include "buffer.h" #include "shm.h" #include "screencopy.h" #include "smooth.h" #include "time-util.h" #include "usdt.h" #include "pixels.h" #include "config.h" #define DELAY_SMOOTHER_TIME_CONSTANT 0.5 // s static void screencopy__stop(struct screencopy* self) { aml_stop(aml_get_default(), self->timer); self->status = SCREENCOPY_STOPPED; if (self->frame) { zwlr_screencopy_frame_v1_destroy(self->frame); self->frame = NULL; } } void screencopy_stop(struct screencopy* self) { if (self->front) wv_buffer_pool_release(self->pool, self->front); self->front = NULL; return screencopy__stop(self); } static void screencopy_linux_dmabuf(void* data, struct zwlr_screencopy_frame_v1* frame, uint32_t format, uint32_t width, uint32_t height) { #ifdef ENABLE_SCREENCOPY_DMABUF struct screencopy* self = data; if (!(wv_buffer_get_available_types() & WV_BUFFER_DMABUF)) return; self->have_linux_dmabuf = true; self->dmabuf_width = width; self->dmabuf_height = height; self->fourcc = format; #endif } static void screencopy_buffer_done(void* data, struct zwlr_screencopy_frame_v1* frame) { struct screencopy* self = data; uint32_t width, height, stride, fourcc; enum wv_buffer_type type = WV_BUFFER_UNSPEC; #ifdef ENABLE_SCREENCOPY_DMABUF if (self->have_linux_dmabuf) { width = self->dmabuf_width; height = self->dmabuf_height; stride = 0; fourcc = self->fourcc; type = WV_BUFFER_DMABUF; } else #endif { width = self->wl_shm_width; height = self->wl_shm_height; stride = self->wl_shm_stride; fourcc = fourcc_from_wl_shm(self->wl_shm_format); type = WV_BUFFER_SHM; } wv_buffer_pool_resize(self->pool, type, width, height, stride, fourcc); struct wv_buffer* buffer = wv_buffer_pool_acquire(self->pool); if (!buffer) { screencopy__stop(self); self->status = SCREENCOPY_FATAL; self->on_done(self); return; } assert(!self->front); self->front = buffer; if (self->is_immediate_copy) zwlr_screencopy_frame_v1_copy(self->frame, buffer->wl_buffer); else zwlr_screencopy_frame_v1_copy_with_damage(self->frame, buffer->wl_buffer); } static void screencopy_buffer(void* data, struct zwlr_screencopy_frame_v1* frame, enum wl_shm_format format, uint32_t width, uint32_t height, uint32_t stride) { struct screencopy* self = data; self->wl_shm_format = format; self->wl_shm_width = width; self->wl_shm_height = height; self->wl_shm_stride = stride; int version = zwlr_screencopy_manager_v1_get_version(self->manager); if (version < 3) { self->have_linux_dmabuf = false; screencopy_buffer_done(data, frame); return; } } static void screencopy_flags(void* data, struct zwlr_screencopy_frame_v1* frame, uint32_t flags) { (void)frame; struct screencopy* self = data; self->front->y_inverted = !!(flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT); } static void screencopy_ready(void* data, struct zwlr_screencopy_frame_v1* frame, uint32_t sec_hi, uint32_t sec_lo, uint32_t nsec) { (void)sec_hi; (void)sec_lo; (void)nsec; struct screencopy* self = data; DTRACE_PROBE1(wayvnc, screencopy_ready, self); screencopy__stop(self); self->last_time = gettime_us(); double delay = (self->last_time - self->start_time) * 1.0e-6; self->delay = smooth(&self->delay_smoother, delay); if (self->is_immediate_copy) wv_buffer_damage_whole(self->front); if (self->back) wv_buffer_pool_release(self->pool, self->back); self->back = self->front; self->front = NULL; wv_buffer_map(self->back); self->status = SCREENCOPY_DONE; self->on_done(self); } static void screencopy_failed(void* data, struct zwlr_screencopy_frame_v1* frame) { struct screencopy* self = data; DTRACE_PROBE1(wayvnc, screencopy_failed, self); screencopy__stop(self); if (self->front) wv_buffer_pool_release(self->pool, self->front); self->front = NULL; self->status = SCREENCOPY_FAILED; self->on_done(self); } static void screencopy_damage(void* data, struct zwlr_screencopy_frame_v1* frame, uint32_t x, uint32_t y, uint32_t width, uint32_t height) { struct screencopy* self = data; DTRACE_PROBE1(wayvnc, screencopy_damage, self); wv_buffer_damage_rect(self->front, x, y, width, height); } static int screencopy__start_capture(struct screencopy* self) { DTRACE_PROBE1(wayvnc, screencopy_start, self); static const struct zwlr_screencopy_frame_v1_listener frame_listener = { .buffer = screencopy_buffer, .linux_dmabuf = screencopy_linux_dmabuf, .buffer_done = screencopy_buffer_done, .flags = screencopy_flags, .ready = screencopy_ready, .failed = screencopy_failed, .damage = screencopy_damage, }; self->start_time = gettime_us(); self->frame = zwlr_screencopy_manager_v1_capture_output(self->manager, self->overlay_cursor, self->wl_output); if (!self->frame) return -1; zwlr_screencopy_frame_v1_add_listener(self->frame, &frame_listener, self); return 0; } static void screencopy__poll(void* obj) { struct screencopy* self = aml_get_userdata(obj); screencopy__start_capture(self); } static int screencopy__start(struct screencopy* self, bool is_immediate_copy) { if (self->status == SCREENCOPY_IN_PROGRESS) return -1; self->is_immediate_copy = is_immediate_copy; uint64_t now = gettime_us(); double dt = (now - self->last_time) * 1.0e-6; int32_t time_left = (1.0 / self->rate_limit - dt - self->delay) * 1.0e3; self->status = SCREENCOPY_IN_PROGRESS; if (time_left > 0) { aml_set_duration(self->timer, time_left); return aml_start(aml_get_default(), self->timer); } return screencopy__start_capture(self); } int screencopy_start(struct screencopy* self) { return screencopy__start(self, false); } int screencopy_start_immediate(struct screencopy* self) { return screencopy__start(self, true); } void screencopy_init(struct screencopy* self) { self->pool = wv_buffer_pool_create(0, 0, 0, 0, 0); assert(self->pool); self->timer = aml_timer_new(0, screencopy__poll, self, NULL); assert(self->timer); self->delay_smoother.time_constant = DELAY_SMOOTHER_TIME_CONSTANT; } void screencopy_destroy(struct screencopy* self) { aml_stop(aml_get_default(), self->timer); aml_unref(self->timer); if (self->back) wv_buffer_pool_release(self->pool, self->back); if (self->front) wv_buffer_pool_release(self->pool, self->front); wv_buffer_pool_destroy(self->pool); } wayvnc-0.4.1/src/seat.c000066400000000000000000000046541415671750500147360ustar00rootroot00000000000000/* * Copyright (c) 2019 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "seat.h" #include "strlcpy.h" static void seat_capabilities(void* data, struct wl_seat* wl_seat, uint32_t capabilities) { struct seat* self = data; self->capabilities = capabilities; } static void seat_name(void* data, struct wl_seat* wl_seat, const char* name) { struct seat* self = data; strlcpy(self->name, name, sizeof(self->name)); } static const struct wl_seat_listener seat_listener = { .capabilities = seat_capabilities, .name = seat_name, }; struct seat* seat_new(struct wl_seat* wl_seat, uint32_t id) { struct seat* self = calloc(1, sizeof(*self)); if (!self) return NULL; self->wl_seat = wl_seat; self->id = id; wl_seat_add_listener(wl_seat, &seat_listener, self); return self; } void seat_destroy(struct seat* self) { wl_seat_destroy(self->wl_seat); free(self); } void seat_list_destroy(struct wl_list* list) { struct seat* seat; struct seat* tmp; wl_list_for_each_safe(seat, tmp, list, link) { wl_list_remove(&seat->link); seat_destroy(seat); } } struct seat* seat_find_by_name(struct wl_list* list, const char* name) { struct seat* seat; wl_list_for_each(seat, list, link) if (strcmp(seat->name, name) == 0) return seat; return NULL; } struct seat* seat_find_by_id(struct wl_list* list, uint32_t id) { struct seat* seat; wl_list_for_each(seat, list, link) if (seat->id == id) return seat; return NULL; } struct seat* seat_first(struct wl_list* list) { struct seat* seat; wl_list_for_each(seat, list, link) return seat; return NULL; } wayvnc-0.4.1/src/shm.c000066400000000000000000000042271415671750500145650ustar00rootroot00000000000000/* * Copyright (c) 2019 - 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "config.h" // Linux with glibc < 2.27 has no wrapper #if defined(HAVE_MEMFD) && !defined(HAVE_MEMFD_CREATE) #include static inline int memfd_create(const char *name, unsigned int flags) { return syscall(SYS_memfd_create, name, flags); } #endif #if !defined(HAVE_MEMFD) && !defined(__FreeBSD__) static void randname(char *buf) { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); long r = ts.tv_nsec; for (int i = 0; i < 6; ++i) { buf[i] = 'A'+(r&15)+(r&16)*2; r >>= 5; } } #endif static int create_shm_file(void) { #ifdef HAVE_MEMFD return memfd_create("wayvnc-shm", 0); #elif defined(__FreeBSD__) // memfd_create added in FreeBSD 13, but SHM_ANON has been supported for ages return shm_open(SHM_ANON, O_RDWR | O_CREAT | O_EXCL, 0600); #else int retries = 100; do { char name[] = "/wl_shm-XXXXXX"; randname(name + sizeof(name) - 7); --retries; int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); if (fd >= 0) { shm_unlink(name); return fd; } } while (retries > 0 && errno == EEXIST); return -1; #endif } int shm_alloc_fd(size_t size) { int fd = create_shm_file(); if (fd < 0) return -1; int ret; do { ret = ftruncate(fd, size); } while (ret < 0 && errno == EINTR); if (ret < 0) { close(fd); return -1; } return fd; } wayvnc-0.4.1/src/smooth.c000066400000000000000000000022201415671750500152760ustar00rootroot00000000000000/* * Copyright (c) 2019 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "smooth.h" #include "time-util.h" #include double smooth(struct smooth* self, double input) { uint64_t now = gettime_us(); double dt = (now - self->last_time) * 1.0e-6; self->last_time = now; double factor = 1.0 - exp(-dt / self->time_constant); double result = factor * input + (1.0 - factor) * self->last_result; self->last_result = result; return result; } wayvnc-0.4.1/src/strlcpy.c000066400000000000000000000031001415671750500154630ustar00rootroot00000000000000/* $OpenBSD: strlcpy.c,v 1.13 2015/08/31 02:53:57 guenther Exp $ */ /* * Copyright (c) 1998, 2015 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include /* * Copy string src to buffer dst of size dsize. At most dsize-1 * chars will be copied. Always NUL terminates (unless dsize == 0). * Returns strlen(src); if retval >= dsize, truncation occurred. */ size_t strlcpy(char *dst, const char *src, size_t dsize) { const char *osrc = src; size_t nleft = dsize; /* Copy as many bytes as will fit. */ if (nleft != 0) { while (--nleft != 0) { if ((*dst++ = *src++) == '\0') break; } } /* Not enough room in dst, add NUL and traverse rest of src. */ if (nleft == 0) { if (dsize != 0) *dst = '\0'; /* NUL-terminate dst */ while (*src++) ; } return(src - osrc - 1); /* count does not include NUL */ } wayvnc-0.4.1/src/transform-util.c000066400000000000000000000152361415671750500167660ustar00rootroot00000000000000/* * Copyright (c) 2020 Andri Yngvason * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. * * For code borrowed from wlroots: * Copyright (c) 2017, 2018 Drew DeVault * Copyright (c) 2014 Jari Vetoniemi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include /* Note: This function yields the inverse pixman transform of the * wl_output_transform. */ void wv_pixman_transform_from_wl_output_transform(pixman_transform_t* dst, enum wl_output_transform src, int width, int height) { #define F1 pixman_fixed_1 switch (src) { case WL_OUTPUT_TRANSFORM_NORMAL: { pixman_transform_t t = {{ { F1, 0, 0 }, { 0, F1, 0 }, { 0, 0, F1 }, }}; *dst = t; } return; case WL_OUTPUT_TRANSFORM_90: { pixman_transform_t t = {{ { 0, F1, 0 }, { -F1, 0, height * F1 }, { 0, 0, F1 }, }}; *dst = t; } return; case WL_OUTPUT_TRANSFORM_180: { pixman_transform_t t = {{ { -F1, 0, width * F1 }, { 0, -F1, height * F1 }, { 0, 0, F1 }, }}; *dst = t; } return; case WL_OUTPUT_TRANSFORM_270: { pixman_transform_t t = {{ { 0, -F1, width * F1 }, { F1, 0, 0 }, { 0, 0, F1 }, }}; *dst = t; } return; case WL_OUTPUT_TRANSFORM_FLIPPED: { pixman_transform_t t = {{ { -F1, 0, width * F1 }, { 0, F1, 0 }, { 0, 0, F1 }, }}; *dst = t; } return; case WL_OUTPUT_TRANSFORM_FLIPPED_90: { pixman_transform_t t = {{ { 0, F1, 0 }, { F1, 0, 0 }, { 0, 0, F1 }, }}; *dst = t; } return; case WL_OUTPUT_TRANSFORM_FLIPPED_180: { pixman_transform_t t = {{ { F1, 0, 0 }, { 0, -F1, height * F1 }, { 0, 0, F1 }, }}; *dst = t; } return; case WL_OUTPUT_TRANSFORM_FLIPPED_270: { pixman_transform_t t = {{ { 0, -F1, width * F1 }, { -F1, 0, height * F1 }, { 0, 0, F1 }, }}; *dst = t; } return; } #undef F1 abort(); } /* Borrowed these from wlroots */ void wv_region_transform(struct pixman_region16* dst, struct pixman_region16* src, enum wl_output_transform transform, int width, int height) { if (transform == WL_OUTPUT_TRANSFORM_NORMAL) { pixman_region_copy(dst, src); return; } int nrects = 0; pixman_box16_t* src_rects = pixman_region_rectangles(src, &nrects); pixman_box16_t* dst_rects = malloc(nrects * sizeof(*dst_rects)); if (dst_rects == NULL) { return; } for (int i = 0; i < nrects; ++i) { switch (transform) { case WL_OUTPUT_TRANSFORM_NORMAL: dst_rects[i].x1 = src_rects[i].x1; dst_rects[i].y1 = src_rects[i].y1; dst_rects[i].x2 = src_rects[i].x2; dst_rects[i].y2 = src_rects[i].y2; break; case WL_OUTPUT_TRANSFORM_90: dst_rects[i].x1 = height - src_rects[i].y2; dst_rects[i].y1 = src_rects[i].x1; dst_rects[i].x2 = height - src_rects[i].y1; dst_rects[i].y2 = src_rects[i].x2; break; case WL_OUTPUT_TRANSFORM_180: dst_rects[i].x1 = width - src_rects[i].x2; dst_rects[i].y1 = height - src_rects[i].y2; dst_rects[i].x2 = width - src_rects[i].x1; dst_rects[i].y2 = height - src_rects[i].y1; break; case WL_OUTPUT_TRANSFORM_270: dst_rects[i].x1 = src_rects[i].y1; dst_rects[i].y1 = width - src_rects[i].x2; dst_rects[i].x2 = src_rects[i].y2; dst_rects[i].y2 = width - src_rects[i].x1; break; case WL_OUTPUT_TRANSFORM_FLIPPED: dst_rects[i].x1 = width - src_rects[i].x2; dst_rects[i].y1 = src_rects[i].y1; dst_rects[i].x2 = width - src_rects[i].x1; dst_rects[i].y2 = src_rects[i].y2; break; case WL_OUTPUT_TRANSFORM_FLIPPED_90: dst_rects[i].x1 = src_rects[i].y1; dst_rects[i].y1 = src_rects[i].x1; dst_rects[i].x2 = src_rects[i].y2; dst_rects[i].y2 = src_rects[i].x2; break; case WL_OUTPUT_TRANSFORM_FLIPPED_180: dst_rects[i].x1 = src_rects[i].x1; dst_rects[i].y1 = height - src_rects[i].y2; dst_rects[i].x2 = src_rects[i].x2; dst_rects[i].y2 = height - src_rects[i].y1; break; case WL_OUTPUT_TRANSFORM_FLIPPED_270: dst_rects[i].x1 = height - src_rects[i].y2; dst_rects[i].y1 = width - src_rects[i].x2; dst_rects[i].x2 = height - src_rects[i].y1; dst_rects[i].y2 = width - src_rects[i].x1; break; } } pixman_region_fini(dst); pixman_region_init_rects(dst, dst_rects, nrects); free(dst_rects); } enum wl_output_transform wv_output_transform_invert(enum wl_output_transform tr) { if ((tr & WL_OUTPUT_TRANSFORM_90) && !(tr & WL_OUTPUT_TRANSFORM_FLIPPED)) { tr ^= WL_OUTPUT_TRANSFORM_180; } return tr; } enum wl_output_transform wv_output_transform_compose( enum wl_output_transform tr_a, enum wl_output_transform tr_b) { uint32_t flipped = (tr_a ^ tr_b) & WL_OUTPUT_TRANSFORM_FLIPPED; uint32_t rotation_mask = WL_OUTPUT_TRANSFORM_90 | WL_OUTPUT_TRANSFORM_180; uint32_t rotated; if (tr_b & WL_OUTPUT_TRANSFORM_FLIPPED) { // When a rotation of k degrees is followed by a flip, the // equivalent transform is a flip followed by a rotation of // -k degrees. rotated = (tr_b - tr_a) & rotation_mask; } else { rotated = (tr_a + tr_b) & rotation_mask; } return flipped | rotated; } wayvnc-0.4.1/util/000077500000000000000000000000001415671750500140135ustar00rootroot00000000000000wayvnc-0.4.1/util/latency_report.py000077500000000000000000000041721415671750500174260ustar00rootroot00000000000000#!/usr/bin/python import os import math stream = os.popen('perf script -F time,event') is_in_update_fb = False class StateTracker: def __init__(self, name, src, enter, leave): self.is_active = False self.name = name self.src = src self.enter = enter self.leave = leave self.n = 0 self.dt_sum = 0.0 self.dt_square_sum = 0.0 self.dt_max = 0.0 self.dt_min = math.inf def add_dt(self, dt): self.n += 1 self.dt_sum += dt self.dt_square_sum += dt ** 2 self.dt_max = max(self.dt_max, dt) self.dt_min = min(self.dt_min, dt) def apply(self, src, event, t): if self.is_active: if (src, event) == (self.src, self.leave): self.is_active = False self.add_dt(t - self.t0) else: if (src, event) == (self.src, self.enter): self.is_active = True self.t0 = t def avg(self): return self.dt_sum / self.n def var(self): return self.dt_square_sum / self.n - self.avg() ** 2 def stddev(self): return math.sqrt(self.var()) def report(self): if self.n == 0: return print('{}:'.format(self.name)) print('\tMin, max: {:.1f} ms, {:.1f} ms'.format(self.dt_min * 1e3, self.dt_max * 1e3)) print('\tAverage, std.dev.: {:.1f} ms, {:.1f} ms'.format(self.avg() * 1e3, self.stddev() * 1e3)) trackers = [ StateTracker('Framebuffer update', 'sdt_neatvnc', 'update_fb_start', 'update_fb_done'), StateTracker('Framebuffer update (only sending)', 'sdt_neatvnc', 'send_fb_start', 'send_fb_done'), StateTracker('Screencopy', 'sdt_wayvnc', 'screencopy_start', 'screencopy_ready'), StateTracker('Refine damage', 'sdt_wayvnc', 'refine_damage_start', 'refine_damage_end'), StateTracker('Render', 'sdt_wayvnc', 'render_start', 'render_end'), ] for line in stream: [t, src, event, _] = line.replace(' ', '').split(':') t = float(t) for tracker in trackers: tracker.apply(src, event, t) for tracker in trackers: tracker.report() print() wayvnc-0.4.1/util/trace.sh000077500000000000000000000006761415671750500154610ustar00rootroot00000000000000#!/bin/bash set -e EVENTS="sdt_wayvnc:* sdt_neatvnc:*" delete_all_events() { for e in $EVENTS; do sudo perf probe -d "$e" || true done } add_all_events() { for e in $EVENTS; do sudo perf probe "$e" done } sudo perf buildid-cache -a build/wayvnc sudo perf buildid-cache -a build/subprojects/neatvnc/libneatvnc.so delete_all_events add_all_events trap "sudo chown $USER.$USER perf.data*" EXIT sudo perf record -aR -e ${EVENTS/ /,} wayvnc-0.4.1/util/valgrind.sh000077500000000000000000000001521415671750500161560ustar00rootroot00000000000000#!/bin/sh valgrind --leak-check=full \ --show-leak-kinds=all \ --suppressions=util/valgrind.supp \ $@ wayvnc-0.4.1/util/valgrind.supp000066400000000000000000000000741415671750500165330ustar00rootroot00000000000000{ Ignore dlopen bug. Memcheck:Leak ... fun:_dl_* ... } wayvnc-0.4.1/wayvnc.pam000066400000000000000000000001701415671750500150420ustar00rootroot00000000000000auth required pam_unix.so nodelay deny=3 unlock_time=600 account required pam_unix.so nodelay deny=3 unlock_time=600 wayvnc-0.4.1/wayvnc.scd000066400000000000000000000103731415671750500150440ustar00rootroot00000000000000wayvnc(1) # NAME wayvnc - A VNC server for wlroots based Wayland compositors. # SYNOPSIS *wayvnc* [options] [address [port]] # OPTIONS *-C, --config=* Select a config file. *-o, --output=* Select output to capture. *-k, --keyboard=[-variant]* Select keyboard layout. The variant can be appended if needed. *-s, --seat=* Select seat by name. *-r, --render-cursor* Enable overlay cursor rendering. *-f, --max-fps=* Set the rate limit (default 30). *-p, --show-performance* Show performance counters. *-V, --version* Show version info. *-h, --help* Get help. # DESCRIPTION This is a VNC server for wlroots based Wayland compositors. It attaches to a running Wayland session, creates virtual input devices and exposes a single display via the RFB protocol. The Wayland session may be a headless one, so it is also possible to run wayvnc without a physical display attached. # CONFIGURATION wayvnc searches for a config file in the location ~/$XDG_CONFIG_HOME/wayvnc/config or if $XDG_CONFIG_HOME is not set ~/.config/wayvnc/config ## SYNTAX The configuration file is composed of key-value pairs separated with an *equal* sign. Whitespace around either the key or the value is insignificant and is not considered to be part of the key or the value. ## KEYWORDS *address* The address to which the server shall bind, e.g. 0.0.0.0 or localhost. *certificate_file* The path to the certificate file for encryption. Only applicable when *enable_auth*=true. *enable_auth* Enable authentication and encryption. Setting this value to *true* requires also setting *certificate_file*, *private_key_file*, *username* and *password*. *password* Choose a password for authentication. *port* The port to which the server shall bind. Default is 5900. *private_key_file* The path to the private key file for encryption. Only applicable when *enable_auth*=true. *username* Choose a username for authentication. *xkb_layout* The keyboard layout to use for key code lookup. Default: _XKB_DEFAULT_LAYOUT_ or system default. *xkb_model* The keyboard model by which to interpret keycodes and LEDs. Default: "pc105" *xkb_options* A comma separated list of options, through which the user specifies non-layout related preferences such as which key is the Compose key. Default: _XKB_DEFAULT_OPTIONS_ or system default. *xkb_rules* The rules file describes how to interpret the values of the model, layout, variant and options fields. Default: _XKB_DEFAULT_RULES_ or system default. *xkb_variant* The keyboard variant to use for keycode lookup. Default: _XKB_DEFAULT_VARIANT_ or system default. ## EXAMPLE ``` address=0.0.0.0 enable_auth=true username=luser password=p455w0rd private_key_file=/path/to/key.pem certificate_file=/path/to/cert.pem ``` # ENVIRONMENT The following environment variables have an effect on wayvnc: _WAYLAND_DISPLAY_ Specifies the name of the Wayland display that the compositor to which wayvnc shall bind is running on. _XDG_CONFIG_HOME_ Specifies the location of configuration files. # FAQ *How can I run wayvnc in headless mode/over an SSH session?* Set the environment variables _WLR_BACKENDS_=headless and _WLR_LIBINPUT_NO_DEVICES_=1 before starting the compositor, then run wayvnc as normal. *How can I pass my mod-key from Sway to the remote desktop session?* Create an almost empty mode in your sway config. Example: ``` mode passthrough { bindsym $mod+Pause mode default } bindsym $mod+Pause mode passthrough ``` This makes it so that when you press $mod+Pause, all keybindings, except the one to switch back, are disabled. *Not all symbols show up when I'm typing. What can I do to fix this?* Try setting the keyboard layout in wayvnc to the one that most closely matches the keyboard layout that you're using on the client side. An exact layout isn't needed, just one that has all the symbols that you use. *How do I enable the Compose key?* Set "xkb_options=compose:menu" in the config file. Any key that is not otherwise used will work. There just needs to be some key for wayvnc to match against. # AUTHORS Maintained by Andri Yngvason . Up-to-date sources can be found at https://github.com/any1/wayvnc and bugs reports or patches can be submitted to GitHub's issue tracker.