├── .github └── pull_request_template.md ├── .gitignore ├── CONTRIBUTING.md ├── COPYING ├── FUNDING.yml ├── README.md ├── bench ├── meson.build ├── parallel-deflate-bench.c └── zrle-bench.c ├── doc ├── rfc6143.txt └── vnc-enc-tight.h ├── examples ├── draw.c ├── meson.build └── png-server.c ├── include ├── auth │ ├── apple-dh.h │ ├── auth.h │ ├── rsa-aes.h │ └── vencrypt.h ├── bandwidth.h ├── base64.h ├── common.h ├── crypto.h ├── crypto │ └── nettle │ │ └── common.h ├── cursor.h ├── damage-refinery.h ├── desktop-layout.h ├── display.h ├── enc │ ├── encoder.h │ ├── h264-encoder.h │ └── util.h ├── fb.h ├── likely.h ├── logging.h ├── neatvnc.h ├── parallel-deflate.h ├── pixels.h ├── rcbuf.h ├── resampler.h ├── rfb-proto.h ├── stream │ ├── common.h │ ├── http.h │ ├── stream.h │ ├── tcp.h │ └── websocket.h ├── sys │ └── queue.h ├── transform-util.h ├── type-macros.h ├── usdt.h ├── vec.h └── xxhash.h ├── meson.build ├── meson_options.txt ├── src ├── auth │ ├── apple-dh.c │ ├── common.c │ ├── rsa-aes.c │ └── vencrypt.c ├── bandwidth.c ├── base64.c ├── crypto │ ├── nettle │ │ ├── cipher.c │ │ ├── hash.c │ │ ├── key.c │ │ └── rsa.c │ └── random.c ├── cursor.c ├── damage-refinery.c ├── desktop-layout.c ├── display.c ├── enc │ ├── h264 │ │ ├── encoder.c │ │ ├── ffmpeg-impl.c │ │ ├── open-h264.c │ │ └── v4l2m2m-impl.c │ ├── interface.c │ ├── raw.c │ ├── tight.c │ ├── util.c │ └── zrle.c ├── fb.c ├── fb_pool.c ├── logging.c ├── parallel-deflate.c ├── pixels.c ├── pngfb.c ├── qnum-to-evdev.c ├── rcbuf.c ├── resampler.c ├── server.c ├── stream │ ├── common.c │ ├── gnutls.c │ ├── interface.c │ ├── rsa-aes.c │ ├── tcp.c │ └── ws │ │ ├── framing.c │ │ ├── handshake.c │ │ ├── http.c │ │ └── ws.c ├── transform-util.c └── vec.c ├── test-images ├── mandrill.png ├── manifest └── tv-test-card.png ├── test-zrle.c └── test ├── meson.build ├── test-base64.c └── test-pixels.c /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Please read CONTRIBUTING.md before making a pull request. 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .clang_complete 2 | .ycm_extra_conf.py 3 | .vimrc 4 | vgcore.* 5 | perf.data 6 | perf.data.old 7 | build 8 | experiments 9 | subprojects 10 | sandbox 11 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | See wayvnc's [CONTRIBUTING.md](https://github.com/any1/wayvnc/blob/master/CONTRIBUTING.md). 2 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 - 2020 Andri Yngvason 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose 4 | with or without fee is hereby granted, provided that the above copyright notice 5 | and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 9 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 11 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 12 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 13 | THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: any1 2 | patreon: andriyngvason 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Neat VNC 2 | 3 | ## Introduction 4 | This is a liberally licensed VNC server library that's intended to be fast and 5 | neat. 6 | 7 | ## Goals 8 | * Speed. 9 | * Clean interface. 10 | * Interoperability with the Freedesktop.org ecosystem. 11 | 12 | ## Building 13 | 14 | ### Runtime Dependencies 15 | * aml - https://github.com/any1/aml/ 16 | * ffmpeg (optional) 17 | * gbm (optional) 18 | * gnutls (optional) 19 | * libdrm (optional) 20 | * libturbojpeg (optional) 21 | * nettle (optional) 22 | * hogweed (optional) 23 | * gmp (optional) 24 | * pixman 25 | * zlib 26 | 27 | ### Build Dependencies 28 | * libdrm 29 | * meson 30 | * pkg-config 31 | 32 | To build just run: 33 | ``` 34 | meson build 35 | ninja -C build 36 | ``` 37 | -------------------------------------------------------------------------------- /bench/meson.build: -------------------------------------------------------------------------------- 1 | libpng = dependency('libpng', required: false) 2 | 3 | configure_file( 4 | output: 'config.h', 5 | configuration: config, 6 | ) 7 | 8 | if libpng.found() 9 | 10 | executable( 11 | 'zrle-bench', 12 | [ 13 | 'zrle-bench.c', 14 | '../src/enc/util.c', 15 | '../src/enc/zrle.c', 16 | '../src/enc/tight.c', 17 | '../src/enc/raw.c', 18 | '../src/enc/h264/open-h264.c', 19 | '../src/enc/h264/encoder.c', 20 | '../src/enc/h264/ffmpeg-impl.c', 21 | '../src/enc/h264/v4l2m2m-impl.c', 22 | '../src/enc/interface.c', 23 | '../src/pngfb.c', 24 | '../src/pixels.c', 25 | '../src/vec.c', 26 | '../src/rcbuf.c', 27 | '../src/fb.c', 28 | '../src/parallel-deflate.c', 29 | ], 30 | dependencies: [ 31 | neatvnc_dep, 32 | pixman, 33 | aml, 34 | libpng, 35 | libm, 36 | zlib, 37 | libturbojpeg, 38 | gbm, 39 | libdrm, 40 | libavutil, 41 | libavcodec, 42 | libavfilter, 43 | ], 44 | ) 45 | endif 46 | 47 | executable( 48 | 'parallel-deflate', 49 | [ 50 | 'parallel-deflate-bench.c', 51 | '../src/parallel-deflate.c', 52 | '../src/vec.c', 53 | ], 54 | dependencies: [ 55 | neatvnc_dep, 56 | aml, 57 | zlib, 58 | libm, 59 | ], 60 | ) 61 | -------------------------------------------------------------------------------- /bench/parallel-deflate-bench.c: -------------------------------------------------------------------------------- 1 | #include "parallel-deflate.h" 2 | #include "vec.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | static int level = 1; 16 | static int method = Z_DEFLATED; 17 | static int window_bits = 15; 18 | static int mem_level = 9; 19 | static int strategy = Z_DEFAULT_STRATEGY; 20 | 21 | struct stopwatch { 22 | uint64_t cpu; 23 | uint64_t real; 24 | }; 25 | 26 | static uint64_t gettime_us(clockid_t clock) 27 | { 28 | struct timespec ts = { 0 }; 29 | clock_gettime(clock, &ts); 30 | return ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000ULL; 31 | } 32 | 33 | static void stopwatch_start(struct stopwatch* self) 34 | { 35 | self->real = gettime_us(CLOCK_MONOTONIC); 36 | self->cpu = gettime_us(CLOCK_PROCESS_CPUTIME_ID); 37 | } 38 | 39 | static void stopwatch_stop(const struct stopwatch* self, const char* report) 40 | { 41 | uint64_t real_stop = gettime_us(CLOCK_MONOTONIC); 42 | uint64_t cpu_stop = gettime_us(CLOCK_PROCESS_CPUTIME_ID); 43 | uint64_t dt_real = real_stop - self->real; 44 | uint64_t dt_cpu = cpu_stop - self->cpu; 45 | double cpu_util = (double)dt_cpu / dt_real; 46 | printf("%s took %"PRIu64" µs with %.0f%% CPU utilisation\n", report, 47 | dt_real, round(cpu_util * 100.0)); 48 | } 49 | 50 | static int zlib_transform(int (*method)(z_stream*, int), struct vec* dst, 51 | const struct vec* src, z_stream* zs) 52 | { 53 | zs->next_in = src->data; 54 | zs->avail_in = src->len; 55 | 56 | do { 57 | if (dst->len == dst->cap && vec_reserve(dst, dst->cap * 2) < 0) 58 | return -1; 59 | 60 | zs->next_out = ((Bytef*)dst->data) + dst->len; 61 | zs->avail_out = dst->cap - dst->len; 62 | 63 | int r = method(zs, Z_SYNC_FLUSH); 64 | if (r == Z_STREAM_ERROR) 65 | return -1; 66 | 67 | dst->len = zs->next_out - (Bytef*)dst->data; 68 | } while (zs->avail_out == 0); 69 | 70 | assert(zs->avail_in == 0); 71 | 72 | return 0; 73 | } 74 | 75 | static int deflate_vec(struct vec* dst, const struct vec* src, z_stream* zs) 76 | { 77 | return zlib_transform(deflate, dst, src, zs); 78 | } 79 | 80 | static int inflate_vec(struct vec* dst, const struct vec* src, z_stream* zs) 81 | { 82 | return zlib_transform(inflate, dst, src, zs); 83 | } 84 | 85 | static int read_file(struct vec* dst, const char* path) 86 | { 87 | int fd = open(path, O_RDONLY); 88 | if (fd < 0) { 89 | perror("Failed open file"); 90 | return -1; 91 | } 92 | 93 | char buffer[4096]; 94 | 95 | for (;;) { 96 | int n_read = read(fd, buffer, sizeof(buffer)); 97 | if (n_read <= 0) 98 | break; 99 | 100 | vec_append(dst, buffer, n_read); 101 | } 102 | 103 | close(fd); 104 | return 0; 105 | } 106 | 107 | static void establish_baseline(const struct vec* raw_data) 108 | { 109 | struct vec compressed, decompressed; 110 | vec_init(&compressed, raw_data->len); 111 | vec_init(&decompressed, raw_data->len); 112 | 113 | z_stream deflate_zs = {}; 114 | int rc = deflateInit2(&deflate_zs, level, method, window_bits, 115 | mem_level, strategy); 116 | assert(rc == Z_OK); 117 | 118 | z_stream inflate_zs = {}; 119 | rc = inflateInit(&inflate_zs); 120 | assert(rc == Z_OK); 121 | 122 | struct stopwatch stopwatch; 123 | 124 | stopwatch_start(&stopwatch); 125 | deflate_vec(&compressed, raw_data, &deflate_zs); 126 | stopwatch_stop(&stopwatch, "Single threaded deflate"); 127 | 128 | stopwatch_start(&stopwatch); 129 | inflate_vec(&decompressed, &compressed, &inflate_zs); 130 | stopwatch_stop(&stopwatch, "Single threaded inflate"); 131 | 132 | assert(decompressed.len == raw_data->len); 133 | assert(memcmp(decompressed.data, raw_data->data, decompressed.len) == 0); 134 | 135 | printf("Single threaded compression: %.1f%%\n", 136 | 100.0 * (1.0 - (double)compressed.len / 137 | (double)raw_data->len)); 138 | 139 | inflateEnd(&inflate_zs); 140 | deflateEnd(&deflate_zs); 141 | 142 | vec_destroy(&decompressed); 143 | vec_destroy(&compressed); 144 | } 145 | 146 | static int run_benchmark(const char* file) 147 | { 148 | struct vec raw_data; 149 | struct vec deflate_result; 150 | 151 | vec_init(&raw_data, 4096); 152 | vec_init(&deflate_result, 4096); 153 | 154 | if (read_file(&raw_data, file) < 0) 155 | return 1; 156 | 157 | establish_baseline(&raw_data); 158 | 159 | struct parallel_deflate* pd = parallel_deflate_new(level, -window_bits, 160 | mem_level, strategy); 161 | assert(pd); 162 | 163 | struct stopwatch stopwatch; 164 | stopwatch_start(&stopwatch); 165 | 166 | parallel_deflate_feed(pd, &deflate_result, raw_data.data, raw_data.len); 167 | parallel_deflate_sync(pd, &deflate_result); 168 | 169 | stopwatch_stop(&stopwatch, "Parallel deflate"); 170 | 171 | const uint8_t* compressed_data = deflate_result.data; 172 | assert(deflate_result.len > 2 && compressed_data[0] == 0x78 173 | && compressed_data[1] == 1); 174 | 175 | z_stream inflate_zs = {}; 176 | int rc = inflateInit(&inflate_zs); 177 | assert(rc == Z_OK); 178 | 179 | struct vec decompressed; 180 | vec_init(&decompressed, raw_data.len); 181 | rc = inflate_vec(&decompressed, &deflate_result, &inflate_zs); 182 | 183 | assert(rc == 0); 184 | assert(decompressed.len == raw_data.len); 185 | assert(memcmp(decompressed.data, raw_data.data, decompressed.len) == 0); 186 | 187 | vec_clear(&deflate_result); 188 | parallel_deflate_feed(pd, &deflate_result, raw_data.data, raw_data.len); 189 | parallel_deflate_sync(pd, &deflate_result); 190 | vec_clear(&decompressed); 191 | rc = inflate_vec(&decompressed, &deflate_result, &inflate_zs); 192 | 193 | assert(rc == 0); 194 | assert(decompressed.len == raw_data.len); 195 | assert(memcmp(decompressed.data, raw_data.data, decompressed.len) == 0); 196 | 197 | vec_destroy(&decompressed); 198 | 199 | printf("Parallel compression: %.1f%%\n", 200 | 100.0 * (1.0 - (double)deflate_result.len / 201 | (double)raw_data.len)); 202 | 203 | parallel_deflate_destroy(pd); 204 | inflateEnd(&inflate_zs); 205 | vec_destroy(&raw_data); 206 | vec_destroy(&deflate_result); 207 | return 0; 208 | } 209 | 210 | int main(int argc, char* argv[]) 211 | { 212 | const char* file = argv[1]; 213 | if (!file) { 214 | fprintf(stderr, "Missing input file\n"); 215 | return 1; 216 | } 217 | 218 | struct aml* aml = aml_new(); 219 | aml_set_default(aml); 220 | 221 | aml_require_workers(aml, -1); 222 | 223 | int rc = run_benchmark(file); 224 | 225 | aml_unref(aml); 226 | return rc; 227 | } 228 | -------------------------------------------------------------------------------- /examples/meson.build: -------------------------------------------------------------------------------- 1 | libpng = dependency('libpng', required: false) 2 | 3 | executable( 4 | 'draw', 5 | [ 6 | 'draw.c', 7 | ], 8 | dependencies: [ 9 | neatvnc_dep, 10 | pixman, 11 | aml, 12 | libm, 13 | ] 14 | ) 15 | 16 | if libpng.found() 17 | executable( 18 | 'png-server', 19 | [ 20 | 'png-server.c', 21 | '../src/pngfb.c', 22 | ], 23 | dependencies: [ 24 | neatvnc_dep, 25 | pixman, 26 | aml, 27 | libpng, 28 | ] 29 | ) 30 | endif 31 | -------------------------------------------------------------------------------- /examples/png-server.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 - 2021 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | struct nvnc_fb* read_png_file(const char* filename); 26 | 27 | static void on_sigint() 28 | { 29 | aml_exit(aml_get_default()); 30 | } 31 | 32 | int main(int argc, char* argv[]) 33 | { 34 | const char* file = argv[1]; 35 | 36 | if (!file) { 37 | printf("Missing argument\n"); 38 | return 1; 39 | } 40 | 41 | struct nvnc_fb* fb = read_png_file(file); 42 | if (!fb) { 43 | printf("Failed to read png file\n"); 44 | return 1; 45 | } 46 | 47 | struct aml* aml = aml_new(); 48 | aml_set_default(aml); 49 | 50 | struct nvnc* server = nvnc_new(); 51 | assert(server); 52 | 53 | int rc = nvnc_listen_tcp(server, "127.0.0.1", 5900, NVNC_STREAM_NORMAL); 54 | assert(rc == 0); 55 | 56 | struct nvnc_display* display = nvnc_display_new(0, 0); 57 | assert(display); 58 | 59 | nvnc_add_display(server, display); 60 | nvnc_set_name(server, file); 61 | 62 | struct pixman_region16 damage; 63 | pixman_region_init_rect(&damage, 0, 0, nvnc_fb_get_width(fb), 64 | nvnc_fb_get_height(fb)); 65 | nvnc_display_feed_buffer(display, fb, &damage); 66 | pixman_region_fini(&damage); 67 | 68 | struct aml_signal* sig = aml_signal_new(SIGINT, on_sigint, NULL, NULL); 69 | aml_start(aml_get_default(), sig); 70 | aml_unref(sig); 71 | 72 | aml_run(aml); 73 | 74 | nvnc_del(server); 75 | nvnc_display_unref(display); 76 | nvnc_fb_unref(fb); 77 | aml_unref(aml); 78 | } 79 | -------------------------------------------------------------------------------- /include/auth/apple-dh.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 - 2024 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | struct nvnc_client; 20 | 21 | int apple_dh_send_public_key(struct nvnc_client* client); 22 | int apple_dh_handle_response(struct nvnc_client* client); 23 | 24 | -------------------------------------------------------------------------------- /include/auth/auth.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 - 2024 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | struct nvnc_client; 20 | 21 | int security_handshake_failed(struct nvnc_client* client, const char* username, 22 | const char* reason_string); 23 | int security_handshake_ok(struct nvnc_client* client, const char* username); 24 | -------------------------------------------------------------------------------- /include/auth/rsa-aes.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 - 2024 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | struct nvnc_client; 20 | 21 | int rsa_aes_send_public_key(struct nvnc_client* client); 22 | int rsa_aes_handle_message(struct nvnc_client* client); 23 | -------------------------------------------------------------------------------- /include/auth/vencrypt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 - 2024 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | struct nvnc_client; 20 | 21 | int vencrypt_send_version(struct nvnc_client* client); 22 | int vencrypt_handle_message(struct nvnc_client* client); 23 | 24 | -------------------------------------------------------------------------------- /include/bandwidth.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct bwe_sample { 4 | int bytes; 5 | int departure_time; 6 | int arrival_time; // round-trip arrival time 7 | }; 8 | 9 | struct bwe* bwe_create(int rtt_min); 10 | void bwe_destroy(struct bwe* self); 11 | 12 | void bwe_feed(struct bwe* self, const struct bwe_sample* sample); 13 | void bwe_update_rtt_min(struct bwe* self, int rtt_min); 14 | int bwe_get_estimate(const struct bwe* self); 15 | -------------------------------------------------------------------------------- /include/base64.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Andri Yngvason 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #pragma once 17 | 18 | #include 19 | #include 20 | 21 | #define BASE64_ENCODED_SIZE(x) ((((x) + 2) / 3) * 4 + 1) 22 | #define BASE64_DECODED_MAX_SIZE(x) ((((x) + 3) / 4) * 3) 23 | 24 | void base64_encode(char* dst, const uint8_t* src, size_t src_len); 25 | ssize_t base64_decode(uint8_t* dst, const char* src); 26 | -------------------------------------------------------------------------------- /include/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 - 2024 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "rfb-proto.h" 24 | #include "sys/queue.h" 25 | 26 | #include "stream/stream.h" 27 | #include "neatvnc.h" 28 | #include "config.h" 29 | 30 | #ifdef HAVE_CRYPTO 31 | #include "crypto.h" 32 | #endif 33 | 34 | #ifdef ENABLE_TLS 35 | #include 36 | #endif 37 | 38 | #define MAX_ENCODINGS 32 39 | #define MAX_OUTGOING_FRAMES 4 40 | #define MSG_BUFFER_SIZE 4096 41 | #define MAX_CUT_TEXT_SIZE 10000000 42 | #define MAX_CLIENT_UNSOLICITED_TEXT_SIZE 20971520 43 | #define MAX_SECURITY_TYPES 32 44 | 45 | enum nvnc_client_state { 46 | VNC_CLIENT_STATE_WAITING_FOR_VERSION = 0, 47 | VNC_CLIENT_STATE_WAITING_FOR_SECURITY, 48 | #ifdef ENABLE_TLS 49 | VNC_CLIENT_STATE_WAITING_FOR_VENCRYPT_VERSION, 50 | VNC_CLIENT_STATE_WAITING_FOR_VENCRYPT_SUBTYPE, 51 | VNC_CLIENT_STATE_WAITING_FOR_VENCRYPT_PLAIN_AUTH, 52 | #endif 53 | #ifdef HAVE_CRYPTO 54 | VNC_CLIENT_STATE_WAITING_FOR_APPLE_DH_RESPONSE, 55 | VNC_CLIENT_STATE_WAITING_FOR_RSA_AES_PUBLIC_KEY, 56 | VNC_CLIENT_STATE_WAITING_FOR_RSA_AES_CHALLENGE, 57 | VNC_CLIENT_STATE_WAITING_FOR_RSA_AES_CLIENT_HASH, 58 | VNC_CLIENT_STATE_WAITING_FOR_RSA_AES_CREDENTIALS, 59 | #endif 60 | VNC_CLIENT_STATE_WAITING_FOR_INIT, 61 | VNC_CLIENT_STATE_READY, 62 | }; 63 | 64 | struct nvnc; 65 | struct stream; 66 | struct aml_handler; 67 | struct aml_idle; 68 | struct nvnc_display; 69 | struct crypto_key; 70 | struct crypto_rsa_pub_key; 71 | struct crypto_rsa_priv_key; 72 | struct bwe; 73 | 74 | struct nvnc_common { 75 | void* userdata; 76 | nvnc_cleanup_fn cleanup_fn; 77 | }; 78 | 79 | struct cut_text { 80 | char* buffer; 81 | size_t length; 82 | size_t index; 83 | bool is_zlib; 84 | bool is_text_provide; 85 | }; 86 | 87 | struct nvnc_client { 88 | struct nvnc_common common; 89 | struct stream* net_stream; 90 | char username[256]; 91 | struct nvnc* server; 92 | enum nvnc_client_state state; 93 | struct rfb_pixel_format pixfmt; 94 | enum rfb_encodings encodings[MAX_ENCODINGS + 1]; 95 | size_t n_encodings; 96 | LIST_ENTRY(nvnc_client) link; 97 | struct pixman_region16 damage; 98 | int n_pending_requests; 99 | bool is_updating; 100 | nvnc_client_fn cleanup_fn; 101 | size_t buffer_index; 102 | size_t buffer_len; 103 | uint8_t msg_buffer[MSG_BUFFER_SIZE]; 104 | uint32_t known_width; 105 | uint32_t known_height; 106 | struct cut_text cut_text; 107 | uint32_t ext_clipboard_caps; 108 | uint32_t ext_clipboard_max_unsolicited_text_size; 109 | bool is_ext_notified; 110 | bool is_continuous_updates_notified; 111 | bool continuous_updates_enabled; 112 | struct { 113 | int x, y; 114 | unsigned int width, height; 115 | } continuous_updates; 116 | struct encoder* encoder; 117 | struct encoder* zrle_encoder; 118 | struct encoder* tight_encoder; 119 | uint32_t cursor_seq; 120 | int quality; 121 | bool formats_changed; 122 | enum nvnc_keyboard_led_state led_state; 123 | enum nvnc_keyboard_led_state pending_led_state; 124 | bool is_blocked_by_fence; 125 | bool must_block_after_next_message; 126 | struct { 127 | int n_pending_requests; 128 | enum rfb_fence_flags flags; 129 | uint8_t payload[64]; 130 | size_t length; 131 | } pending_fence; 132 | int32_t last_ping_time; 133 | int32_t min_rtt; 134 | struct bwe* bwe; 135 | int32_t inflight_bytes; 136 | bool has_ext_mouse_buttons; 137 | struct aml_idle* close_task; 138 | 139 | #ifdef HAVE_CRYPTO 140 | struct crypto_key* apple_dh_secret; 141 | 142 | struct { 143 | enum crypto_hash_type hash_type; 144 | enum crypto_cipher_type cipher_type; 145 | size_t challenge_len; 146 | uint8_t challenge[32]; 147 | struct crypto_rsa_pub_key* pub; 148 | } rsa; 149 | #endif 150 | }; 151 | 152 | LIST_HEAD(nvnc_client_list, nvnc_client); 153 | 154 | enum nvnc__socket_type { 155 | NVNC__SOCKET_TCP, 156 | NVNC__SOCKET_UNIX, 157 | NVNC__SOCKET_WEBSOCKET, 158 | NVNC__SOCKET_FROM_FD, 159 | }; 160 | 161 | struct nvnc__socket { 162 | struct nvnc* parent; 163 | enum nvnc_stream_type type; 164 | bool is_external; 165 | int fd; 166 | struct aml_handler* poll_handle; 167 | LIST_ENTRY(nvnc__socket) link; 168 | }; 169 | 170 | LIST_HEAD(nvnc__socket_list, nvnc__socket); 171 | 172 | struct nvnc { 173 | struct nvnc_common common; 174 | bool is_closing; 175 | struct nvnc__socket_list sockets; 176 | struct nvnc_client_list clients; 177 | char name[256]; 178 | void* userdata; 179 | nvnc_key_fn key_fn; 180 | nvnc_key_fn key_code_fn; 181 | nvnc_pointer_fn pointer_fn; 182 | nvnc_fb_req_fn fb_req_fn; 183 | nvnc_client_fn new_client_fn; 184 | nvnc_cut_text_fn cut_text_fn; 185 | struct cut_text ext_clipboard_provide_msg; 186 | nvnc_desktop_layout_fn desktop_layout_fn; 187 | struct nvnc_display* display; 188 | struct { 189 | struct nvnc_fb* buffer; 190 | uint32_t width, height; 191 | uint32_t hotspot_x, hotspot_y; 192 | } cursor; 193 | uint32_t cursor_seq; 194 | 195 | enum nvnc_auth_flags auth_flags; 196 | nvnc_auth_fn auth_fn; 197 | void* auth_ud; 198 | 199 | #ifdef ENABLE_TLS 200 | gnutls_certificate_credentials_t tls_creds; 201 | #endif 202 | 203 | #ifdef HAVE_CRYPTO 204 | struct crypto_rsa_pub_key* rsa_pub; 205 | struct crypto_rsa_priv_key* rsa_priv; 206 | #endif 207 | 208 | int n_security_types; 209 | enum rfb_security_type security_types[MAX_SECURITY_TYPES]; 210 | 211 | uint32_t n_damage_clients; 212 | }; 213 | 214 | void nvnc__damage_region(struct nvnc* self, 215 | const struct pixman_region16* damage); 216 | void close_after_write(void* userdata, enum stream_req_status status); 217 | void update_min_rtt(struct nvnc_client* client); 218 | -------------------------------------------------------------------------------- /include/crypto.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 - 2024 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | struct crypto_key; 24 | struct crypto_cipher; 25 | struct crypto_hash; 26 | struct crypto_rsa_pub_key; 27 | struct crypto_rsa_priv_key; 28 | struct vec; 29 | 30 | enum crypto_cipher_type { 31 | CRYPTO_CIPHER_INVALID = 0, 32 | CRYPTO_CIPHER_AES128_ECB, 33 | CRYPTO_CIPHER_AES_EAX, 34 | CRYPTO_CIPHER_AES256_EAX, 35 | }; 36 | 37 | enum crypto_hash_type { 38 | CRYPTO_HASH_INVALID = 0, 39 | CRYPTO_HASH_MD5, 40 | CRYPTO_HASH_SHA1, 41 | CRYPTO_HASH_SHA256, 42 | }; 43 | 44 | struct crypto_data_entry { 45 | uint8_t* data; 46 | size_t len; 47 | }; 48 | 49 | void crypto_dump_base16(const char* msg, const uint8_t* bytes, size_t len); 50 | void crypto_dump_base64(const char* msg, const uint8_t* bytes, size_t len); 51 | 52 | void crypto_random(uint8_t* dst, size_t len); 53 | 54 | // Key generation 55 | struct crypto_key* crypto_key_new(int g, const uint8_t *p, uint32_t p_len, 56 | const uint8_t* q, uint32_t q_len); 57 | void crypto_key_del(struct crypto_key* key); 58 | 59 | int crypto_key_g(const struct crypto_key* key); 60 | uint32_t crypto_key_p(const struct crypto_key* key, uint8_t* dst, 61 | uint32_t dst_size); 62 | uint32_t crypto_key_q(const struct crypto_key* key, uint8_t* dst, 63 | uint32_t dst_size); 64 | 65 | struct crypto_key* crypto_keygen(void); 66 | 67 | // Diffie-Hellman 68 | struct crypto_key* crypto_derive_public_key(const struct crypto_key* priv); 69 | struct crypto_key* crypto_derive_shared_secret( 70 | const struct crypto_key* own_secret, 71 | const struct crypto_key* remote_public_key); 72 | 73 | // Ciphers 74 | struct crypto_cipher* crypto_cipher_new(const uint8_t* enc_key, 75 | const uint8_t* dec_key, enum crypto_cipher_type type); 76 | void crypto_cipher_del(struct crypto_cipher* self); 77 | 78 | bool crypto_cipher_encrypt(struct crypto_cipher* self, struct vec* dst, 79 | uint8_t* mac, const uint8_t* src, size_t len, 80 | const uint8_t* ad, size_t ad_len); 81 | ssize_t crypto_cipher_decrypt(struct crypto_cipher* self, uint8_t* dst, 82 | uint8_t* mac, const uint8_t* src, size_t len, 83 | const uint8_t* ad, size_t ad_len); 84 | 85 | // Hashing 86 | struct crypto_hash* crypto_hash_new(enum crypto_hash_type type); 87 | void crypto_hash_del(struct crypto_hash* self); 88 | 89 | void crypto_hash_append(struct crypto_hash* self, const uint8_t* src, 90 | size_t len); 91 | void crypto_hash_digest(struct crypto_hash* self, uint8_t* dst, 92 | size_t len); 93 | 94 | void crypto_hash_one(uint8_t* dst, size_t dst_len, enum crypto_hash_type type, 95 | const uint8_t* src, size_t src_len); 96 | void crypto_hash_many(uint8_t* dst, size_t dst_len, enum crypto_hash_type type, 97 | const struct crypto_data_entry *src); 98 | 99 | // RSA 100 | struct crypto_rsa_pub_key* crypto_rsa_pub_key_new(void); 101 | void crypto_rsa_pub_key_del(struct crypto_rsa_pub_key*); 102 | 103 | // Returns length in bytes 104 | size_t crypto_rsa_pub_key_length(const struct crypto_rsa_pub_key* key); 105 | 106 | struct crypto_rsa_pub_key* crypto_rsa_pub_key_import(const uint8_t* modulus, 107 | const uint8_t* exponent, size_t size); 108 | 109 | void crypto_rsa_pub_key_modulus(const struct crypto_rsa_pub_key* key, 110 | uint8_t* dst, size_t dst_size); 111 | void crypto_rsa_pub_key_exponent(const struct crypto_rsa_pub_key* key, 112 | uint8_t* dst, size_t dst_size); 113 | 114 | bool crypto_rsa_priv_key_import_pkcs1_der(struct crypto_rsa_priv_key* priv, 115 | struct crypto_rsa_pub_key* pub, const uint8_t* key, 116 | size_t size); 117 | 118 | bool crypto_rsa_priv_key_load(struct crypto_rsa_priv_key* priv, 119 | struct crypto_rsa_pub_key* pub, const char* path); 120 | 121 | struct crypto_rsa_priv_key *crypto_rsa_priv_key_new(void); 122 | void crypto_rsa_priv_key_del(struct crypto_rsa_priv_key*); 123 | 124 | bool crypto_rsa_keygen(struct crypto_rsa_pub_key*, struct crypto_rsa_priv_key*); 125 | 126 | ssize_t crypto_rsa_encrypt(struct crypto_rsa_pub_key* pub, uint8_t* dst, 127 | size_t dst_size, const uint8_t* src, size_t src_size); 128 | ssize_t crypto_rsa_decrypt(struct crypto_rsa_priv_key* priv, uint8_t* dst, 129 | size_t dst_size, const uint8_t* src, size_t src_size); 130 | -------------------------------------------------------------------------------- /include/crypto/nettle/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static inline void crypto_import(mpz_t n, const uint8_t* src, size_t len) 9 | { 10 | int order = 1; 11 | int unit_size = 1; 12 | int endian = 1; 13 | int skip_bits = 0; 14 | 15 | mpz_import(n, len, order, unit_size, endian, skip_bits, src); 16 | } 17 | 18 | static inline size_t crypto_export(uint8_t* dst, size_t dst_size, const mpz_t n) 19 | { 20 | int order = 1; // msb first 21 | int unit_size = 1; // byte 22 | int endian = 1; // msb first 23 | int skip_bits = 0; 24 | 25 | size_t bitsize = mpz_sizeinbase(n, 2); 26 | size_t bytesize = (bitsize + 7) / 8; 27 | 28 | assert(bytesize <= dst_size); 29 | 30 | memset(dst, 0, dst_size); 31 | mpz_export(dst + dst_size - bytesize, &bytesize, order, unit_size, 32 | endian, skip_bits, n); 33 | 34 | return bytesize; 35 | } 36 | -------------------------------------------------------------------------------- /include/cursor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | 21 | struct vec; 22 | struct nvnc_fb; 23 | struct rfb_pixel_format; 24 | 25 | int cursor_encode(struct vec* dst, struct rfb_pixel_format* pixfmt, 26 | struct nvnc_fb* image, uint32_t width, uint32_t height, 27 | uint32_t x_hotspot, uint32_t y_hotspot); 28 | -------------------------------------------------------------------------------- /include/damage-refinery.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 - 2021 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | 21 | struct pixman_region16; 22 | struct nvnc_fb; 23 | struct XXH3_state_s; 24 | 25 | struct damage_refinery { 26 | struct XXH3_state_s* state; 27 | uint32_t* hashes; 28 | uint32_t width; 29 | uint32_t height; 30 | }; 31 | 32 | int damage_refinery_init(struct damage_refinery* self, uint32_t width, 33 | uint32_t height); 34 | int damage_refinery_resize(struct damage_refinery* self, uint32_t width, 35 | uint32_t height); 36 | void damage_refinery_destroy(struct damage_refinery* self); 37 | 38 | void damage_refine(struct damage_refinery* self, 39 | struct pixman_region16* refined, 40 | struct pixman_region16* hint, 41 | struct nvnc_fb* buffer); 42 | -------------------------------------------------------------------------------- /include/desktop-layout.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Philipp Zabel 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | 21 | struct nvnc_display; 22 | struct rfb_screen; 23 | 24 | struct nvnc_display_layout { 25 | struct nvnc_display* display; 26 | uint32_t id; 27 | uint16_t x_pos, y_pos; 28 | uint16_t width, height; 29 | }; 30 | 31 | struct nvnc_desktop_layout { 32 | uint16_t width, height; 33 | uint8_t n_display_layouts; 34 | struct nvnc_display_layout display_layouts[0]; 35 | }; 36 | 37 | void nvnc_display_layout_init( 38 | struct nvnc_display_layout* display, struct rfb_screen* screen); 39 | -------------------------------------------------------------------------------- /include/display.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "neatvnc.h" 20 | #include "damage-refinery.h" 21 | 22 | #include 23 | #include 24 | 25 | struct nvnc; 26 | struct nvnc_fb; 27 | struct resampler; 28 | 29 | struct nvnc_display { 30 | int ref; 31 | struct nvnc* server; 32 | uint16_t x_pos, y_pos; 33 | struct nvnc_fb* buffer; 34 | struct resampler* resampler; 35 | struct damage_refinery damage_refinery; 36 | }; 37 | -------------------------------------------------------------------------------- /include/enc/encoder.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 - 2022 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | #pragma once 17 | 18 | #include "rfb-proto.h" 19 | #include "rcbuf.h" 20 | 21 | #include 22 | #include 23 | 24 | struct encoder; 25 | struct nvnc_fb; 26 | struct pixman_region16; 27 | 28 | enum encoder_impl_flags { 29 | ENCODER_IMPL_FLAG_NONE = 0, 30 | ENCODER_IMPL_FLAG_IGNORES_DAMAGE = 1 << 0, 31 | }; 32 | 33 | struct encoder_impl { 34 | enum encoder_impl_flags flags; 35 | 36 | void (*destroy)(struct encoder*); 37 | 38 | void (*set_output_format)(struct encoder*, 39 | const struct rfb_pixel_format*); 40 | void (*set_quality)(struct encoder*, int quality); 41 | 42 | int (*encode)(struct encoder*, struct nvnc_fb* fb, 43 | struct pixman_region16* damage); 44 | 45 | void (*request_key_frame)(struct encoder*); 46 | }; 47 | 48 | struct encoded_frame { 49 | struct rcbuf buf; 50 | int n_rects; 51 | uint32_t width; 52 | uint32_t height; 53 | uint64_t pts; 54 | }; 55 | 56 | struct encoder { 57 | struct encoder_impl* impl; 58 | 59 | int ref; 60 | 61 | uint16_t x_pos; 62 | uint16_t y_pos; 63 | 64 | void (*on_done)(struct encoder*, struct encoded_frame* result); 65 | void* userdata; 66 | }; 67 | 68 | struct encoder* encoder_new(enum rfb_encodings type, uint16_t width, 69 | uint16_t height); 70 | void encoder_ref(struct encoder* self); 71 | void encoder_unref(struct encoder* self); 72 | 73 | void encoder_init(struct encoder* self, struct encoder_impl*); 74 | 75 | enum rfb_encodings encoder_get_type(const struct encoder* self); 76 | enum encoder_kind encoder_get_kind(const struct encoder* self); 77 | 78 | void encoder_set_output_format(struct encoder* self, 79 | const struct rfb_pixel_format*); 80 | void encoder_set_quality(struct encoder* self, int value); 81 | 82 | int encoder_encode(struct encoder* self, struct nvnc_fb* fb, 83 | struct pixman_region16* damage); 84 | 85 | void encoder_request_key_frame(struct encoder* self); 86 | 87 | void encoder_finish_frame(struct encoder* self, struct encoded_frame* result); 88 | 89 | static inline void encoded_frame_ref(struct encoded_frame* self) 90 | { 91 | rcbuf_ref(&self->buf); 92 | } 93 | 94 | static inline void encoded_frame_unref(struct encoded_frame* self) 95 | { 96 | rcbuf_unref(&self->buf); 97 | } 98 | -------------------------------------------------------------------------------- /include/enc/h264-encoder.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 - 2024 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | struct nvnc_fb; 23 | struct h264_encoder; 24 | 25 | typedef void (*h264_encoder_packet_handler_fn)(const void* payload, size_t size, 26 | uint64_t pts, void* userdata); 27 | 28 | struct h264_encoder_impl { 29 | struct h264_encoder* (*create)(uint32_t width, uint32_t height, 30 | uint32_t format, int quality); 31 | void (*destroy)(struct h264_encoder*); 32 | void (*feed)(struct h264_encoder*, struct nvnc_fb*); 33 | }; 34 | 35 | struct h264_encoder { 36 | struct h264_encoder_impl *impl; 37 | h264_encoder_packet_handler_fn on_packet_ready; 38 | void* userdata; 39 | bool next_frame_should_be_keyframe; 40 | }; 41 | 42 | struct h264_encoder* h264_encoder_create(uint32_t width, uint32_t height, 43 | uint32_t format, int quality); 44 | 45 | void h264_encoder_destroy(struct h264_encoder*); 46 | 47 | void h264_encoder_set_packet_handler_fn(struct h264_encoder*, 48 | h264_encoder_packet_handler_fn); 49 | void h264_encoder_set_userdata(struct h264_encoder*, void* userdata); 50 | 51 | void h264_encoder_feed(struct h264_encoder*, struct nvnc_fb*); 52 | 53 | void h264_encoder_request_keyframe(struct h264_encoder*); 54 | -------------------------------------------------------------------------------- /include/enc/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 - 2022 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "rfb-proto.h" 20 | 21 | #include 22 | 23 | struct vec; 24 | struct pixman_region16; 25 | 26 | int encode_rect_head(struct vec* dst, enum rfb_encodings encoding, 27 | uint32_t x, uint32_t y, uint32_t width, uint32_t height); 28 | uint32_t calc_bytes_per_cpixel(const struct rfb_pixel_format* fmt); 29 | uint32_t calculate_region_area(struct pixman_region16* region); 30 | 31 | struct encoded_frame* encoded_frame_new(void* payload, size_t size, int n_rects, 32 | uint16_t width, uint16_t height, uint64_t pts); 33 | -------------------------------------------------------------------------------- /include/fb.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 - 2022 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "neatvnc.h" 25 | #include "common.h" 26 | 27 | struct gbm_bo; 28 | 29 | struct nvnc_fb { 30 | struct nvnc_common common; 31 | enum nvnc_fb_type type; 32 | int ref; 33 | int hold_count; 34 | nvnc_fb_release_fn on_release; 35 | void* release_context; 36 | bool is_external; 37 | uint16_t width; 38 | uint16_t height; 39 | uint32_t fourcc_format; 40 | enum nvnc_transform transform; 41 | uint64_t pts; // in micro seconds 42 | 43 | /* main memory buffer attributes */ 44 | void* addr; 45 | int32_t stride; 46 | 47 | /* dmabuf attributes */ 48 | struct gbm_bo* bo; 49 | void* bo_map_handle; 50 | }; 51 | 52 | void nvnc_fb_hold(struct nvnc_fb* fb); 53 | void nvnc_fb_release(struct nvnc_fb* fb); 54 | int nvnc_fb_map(struct nvnc_fb* fb); 55 | void nvnc_fb_unmap(struct nvnc_fb* fb); 56 | -------------------------------------------------------------------------------- /include/likely.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | #define likely(x) __builtin_expect(!!(x), 1) 20 | #define unlikely(x) __builtin_expect(!!(x), 0) 21 | -------------------------------------------------------------------------------- /include/logging.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Copyright (c) 2022 Andri Yngvason 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 11 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 12 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 13 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 15 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 | * PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | void nvnc__log_init(void); 20 | -------------------------------------------------------------------------------- /include/parallel-deflate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | 21 | struct vec; 22 | struct parallel_deflate; 23 | 24 | struct parallel_deflate* parallel_deflate_new(int level, int window_bits, 25 | int mem_level, int strategy); 26 | void parallel_deflate_destroy(struct parallel_deflate* self); 27 | 28 | void parallel_deflate_feed(struct parallel_deflate* self, struct vec* out, 29 | const void* data, size_t len); 30 | void parallel_deflate_sync(struct parallel_deflate* self, struct vec* out); 31 | -------------------------------------------------------------------------------- /include/pixels.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 - 2024 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | struct rfb_pixel_format; 25 | struct rfb_set_colour_map_entries_msg; 26 | 27 | enum format_rating_flags { 28 | FORMAT_RATING_NEED_ALPHA = 1 << 0, 29 | FORMAT_RATING_PREFER_LINEAR = 1 << 1, 30 | }; 31 | 32 | void pixel_to_cpixel(uint8_t* restrict dst, 33 | const struct rfb_pixel_format* dst_fmt, 34 | const uint8_t* restrict src, 35 | const struct rfb_pixel_format* src_fmt, 36 | size_t bytes_per_cpixel, size_t len); 37 | 38 | int rfb_pixfmt_from_fourcc(struct rfb_pixel_format *dst, uint32_t src); 39 | uint32_t rfb_pixfmt_to_fourcc(const struct rfb_pixel_format* fmt); 40 | int rfb_pixfmt_depth(const struct rfb_pixel_format *fmt); 41 | 42 | int pixel_size_from_fourcc(uint32_t fourcc); 43 | 44 | bool fourcc_to_pixman_fmt(pixman_format_code_t* dst, uint32_t src); 45 | 46 | bool extract_alpha_mask(uint8_t* dst, const void* src, uint32_t format, 47 | size_t len); 48 | 49 | const char* drm_format_to_string(uint32_t fmt); 50 | const char* rfb_pixfmt_to_string(const struct rfb_pixel_format* fmt); 51 | void make_rgb332_pal8_map(struct rfb_set_colour_map_entries_msg* msg); 52 | 53 | double rate_pixel_format(uint32_t format, uint64_t modifier, 54 | enum format_rating_flags flags, int target_depth); 55 | -------------------------------------------------------------------------------- /include/rcbuf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | 21 | struct rcbuf { 22 | void* payload; 23 | size_t size; 24 | int ref; 25 | }; 26 | 27 | struct rcbuf* rcbuf_new(void* payload, size_t size); 28 | struct rcbuf* rcbuf_from_string(const char* str); 29 | struct rcbuf* rcbuf_from_mem(const void* addr, size_t size); 30 | 31 | void rcbuf_ref(struct rcbuf* self); 32 | void rcbuf_unref(struct rcbuf* self); 33 | -------------------------------------------------------------------------------- /include/resampler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | 21 | struct nvnc_fb; 22 | struct pixman_region16; 23 | 24 | struct resampler; 25 | 26 | typedef void (*resampler_fn)(struct nvnc_fb*, struct pixman_region16* damage, 27 | void* userdata); 28 | 29 | struct resampler* resampler_create(void); 30 | void resampler_destroy(struct resampler*); 31 | 32 | int resampler_feed(struct resampler*, struct nvnc_fb* fb, 33 | struct pixman_region16* damage, resampler_fn on_done, 34 | void* userdata); 35 | 36 | void resample_now(struct nvnc_fb* dst, struct nvnc_fb* src, 37 | struct pixman_region16* damage); 38 | -------------------------------------------------------------------------------- /include/stream/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 - 2023 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "stream.h" 20 | 21 | #include 22 | 23 | static inline void stream__poll_r(struct stream* self) 24 | { 25 | aml_set_event_mask(self->handler, AML_EVENT_READ); 26 | } 27 | 28 | static inline void stream__poll_w(struct stream* self) 29 | { 30 | aml_set_event_mask(self->handler, AML_EVENT_WRITE); 31 | } 32 | 33 | static inline void stream__poll_rw(struct stream* self) 34 | { 35 | aml_set_event_mask(self->handler, AML_EVENT_READ | AML_EVENT_WRITE); 36 | } 37 | 38 | void stream_req__finish(struct stream_req* req, enum stream_req_status status); 39 | void stream__remote_closed(struct stream* self); 40 | -------------------------------------------------------------------------------- /include/stream/http.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014-2016, Marel 2 | * Copyright (c) 2023, Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | #define HTTP_FIELD_INDEX_MAX 32 20 | 21 | #include 22 | 23 | struct http_kv { 24 | char* key; 25 | char* value; 26 | }; 27 | 28 | struct http_req { 29 | size_t header_length; 30 | size_t content_length; 31 | char* content_type; 32 | size_t field_index; 33 | struct http_kv field[HTTP_FIELD_INDEX_MAX]; 34 | }; 35 | 36 | int http_req_parse(struct http_req* req, const char* head); 37 | void http_req_free(struct http_req* req); 38 | -------------------------------------------------------------------------------- /include/stream/stream.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 - 2025 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "config.h" 20 | #include "sys/queue.h" 21 | #include "rcbuf.h" 22 | #include "vec.h" 23 | 24 | #ifdef HAVE_CRYPTO 25 | #include "crypto.h" 26 | #endif 27 | 28 | #include 29 | #include 30 | 31 | #define STREAM_ALLOC_SIZE 4096 32 | 33 | enum stream_state { 34 | STREAM_STATE_NORMAL = 0, 35 | STREAM_STATE_CLOSED, 36 | #ifdef ENABLE_TLS 37 | STREAM_STATE_TLS_HANDSHAKE, 38 | STREAM_STATE_TLS_READY, 39 | #endif 40 | }; 41 | 42 | enum stream_req_status { 43 | STREAM_REQ_DONE = 0, 44 | STREAM_REQ_FAILED, 45 | }; 46 | 47 | enum stream_event { 48 | STREAM_EVENT_READ, 49 | STREAM_EVENT_REMOTE_CLOSED, 50 | }; 51 | 52 | struct stream; 53 | struct crypto_cipher; 54 | 55 | typedef void (*stream_event_fn)(struct stream*, enum stream_event); 56 | typedef void (*stream_req_fn)(void*, enum stream_req_status); 57 | typedef struct rcbuf* (*stream_exec_fn)(struct stream*, void* userdata); 58 | 59 | struct stream_req { 60 | struct rcbuf* payload; 61 | stream_req_fn on_done; 62 | stream_exec_fn exec; 63 | void* userdata; 64 | TAILQ_ENTRY(stream_req) link; 65 | }; 66 | 67 | TAILQ_HEAD(stream_send_queue, stream_req); 68 | 69 | struct stream_impl { 70 | int (*close)(struct stream*); 71 | void (*destroy)(struct stream*); 72 | ssize_t (*read)(struct stream*, void* dst, size_t size); 73 | int (*send)(struct stream*, struct rcbuf* payload, 74 | stream_req_fn on_done, void* userdata); 75 | int (*send_first)(struct stream*, struct rcbuf* payload); 76 | void (*exec_and_send)(struct stream*, stream_exec_fn, void* userdata); 77 | }; 78 | 79 | struct stream { 80 | struct stream_impl *impl; 81 | int ref; 82 | 83 | enum stream_state state; 84 | 85 | int fd; 86 | struct aml_handler* handler; 87 | stream_event_fn on_event; 88 | void* userdata; 89 | 90 | struct stream_send_queue send_queue; 91 | 92 | uint32_t bytes_sent; 93 | uint32_t bytes_received; 94 | 95 | bool cork; 96 | 97 | struct crypto_cipher* cipher; 98 | struct vec tmp_buf; 99 | }; 100 | 101 | #ifdef ENABLE_WEBSOCKET 102 | struct stream* stream_ws_new(int fd, stream_event_fn on_event, void* userdata); 103 | #endif 104 | 105 | struct stream* stream_new(int fd, stream_event_fn on_event, void* userdata); 106 | void stream_init(struct stream* self); 107 | void stream_ref(struct stream* self); 108 | int stream_close(struct stream* self); 109 | void stream_destroy(struct stream* self); 110 | ssize_t stream_read(struct stream* self, void* dst, size_t size); 111 | int stream_write(struct stream* self, const void* payload, size_t len, 112 | stream_req_fn on_done, void* userdata); 113 | int stream_send(struct stream* self, struct rcbuf* payload, 114 | stream_req_fn on_done, void* userdata); 115 | int stream_send_first(struct stream* self, struct rcbuf* payload); 116 | 117 | // Queue a pure function to be executed when time comes to send it. 118 | void stream_exec_and_send(struct stream* self, stream_exec_fn, void* userdata); 119 | 120 | #ifdef ENABLE_TLS 121 | int stream_upgrade_to_tls(struct stream* self, void* context); 122 | #endif 123 | 124 | #ifdef HAVE_CRYPTO 125 | int stream_upgrade_to_rsa_eas(struct stream* base, 126 | enum crypto_cipher_type cipher_type, 127 | const uint8_t* enc_key, const uint8_t* dec_key); 128 | #endif 129 | -------------------------------------------------------------------------------- /include/stream/tcp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "stream.h" 20 | 21 | #include 22 | 23 | struct stream; 24 | 25 | int stream_tcp_init(struct stream* self, int fd, stream_event_fn on_event, 26 | void* userdata); 27 | int stream_tcp_close(struct stream* self); 28 | void stream_tcp_destroy(struct stream* self); 29 | ssize_t stream_tcp_read(struct stream* self, void* dst, size_t size); 30 | int stream_tcp_send(struct stream* self, struct rcbuf* payload, 31 | stream_req_fn on_done, void* userdata); 32 | int stream_tcp_send_first(struct stream* self, struct rcbuf* payload); 33 | void stream_tcp_exec_and_send(struct stream* self, 34 | stream_exec_fn exec_fn, void* userdata); 35 | -------------------------------------------------------------------------------- /include/stream/websocket.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #define WS_HEADER_MIN_SIZE 14 24 | 25 | enum ws_opcode { 26 | WS_OPCODE_CONT = 0, 27 | WS_OPCODE_TEXT, 28 | WS_OPCODE_BIN, 29 | WS_OPCODE_CLOSE = 8, 30 | WS_OPCODE_PING, 31 | WS_OPCODE_PONG, 32 | }; 33 | 34 | struct ws_frame_header { 35 | bool fin; 36 | enum ws_opcode opcode; 37 | bool mask; 38 | uint64_t payload_length; 39 | uint8_t masking_key[4]; 40 | size_t header_length; 41 | }; 42 | 43 | ssize_t ws_handshake(char* output, size_t output_maxlen, const char* input); 44 | 45 | const char *ws_opcode_name(enum ws_opcode op); 46 | 47 | bool ws_parse_frame_header(struct ws_frame_header* header, 48 | const uint8_t* payload, size_t length); 49 | void ws_apply_mask(const struct ws_frame_header* header, 50 | uint8_t* restrict payload); 51 | void ws_copy_payload(const struct ws_frame_header* header, 52 | uint8_t* restrict dst, const uint8_t* restrict src, size_t len); 53 | int ws_write_frame_header(uint8_t* dst, const struct ws_frame_header* header); 54 | -------------------------------------------------------------------------------- /include/transform-util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 - 2021 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "neatvnc.h" 20 | 21 | #include 22 | 23 | void nvnc_transform_to_pixman_transform(pixman_transform_t* dst, 24 | enum nvnc_transform src, int width, int height); 25 | 26 | void nvnc_transform_dimensions(enum nvnc_transform transform, uint32_t* width, 27 | uint32_t* height); 28 | 29 | void nvnc_transform_region(struct pixman_region16* dst, 30 | struct pixman_region16* src, enum nvnc_transform transform, 31 | int width, int height); 32 | -------------------------------------------------------------------------------- /include/type-macros.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | 21 | #define container_of(ptr, type, member) \ 22 | ({ \ 23 | const typeof(((type*)0)->member)* __mptr = (ptr); \ 24 | (type*)((char*)__mptr - offsetof(type, member)); \ 25 | }) 26 | -------------------------------------------------------------------------------- /include/usdt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "config.h" 20 | 21 | #ifdef HAVE_USDT 22 | #include 23 | #else 24 | #define DTRACE_PROBE(...) 25 | #define DTRACE_PROBE1(...) 26 | #define DTRACE_PROBE2(...) 27 | #define DTRACE_PROBE3(...) 28 | #define DTRACE_PROBE4(...) 29 | #define DTRACE_PROBE5(...) 30 | #define DTRACE_PROBE6(...) 31 | #endif 32 | -------------------------------------------------------------------------------- /include/vec.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | struct vec { 24 | void* data; 25 | size_t len; 26 | size_t cap; 27 | }; 28 | 29 | static inline void vec_clear(struct vec* vec) 30 | { 31 | vec->len = 0; 32 | } 33 | 34 | int vec_init(struct vec* vec, size_t cap); 35 | void vec_destroy(struct vec* vec); 36 | 37 | int vec_reserve(struct vec* vec, size_t size); 38 | 39 | void vec_bzero(struct vec* vec); 40 | 41 | int vec_assign(struct vec* vec, const void* data, size_t size); 42 | int vec_append(struct vec* vec, const void* data, size_t size); 43 | void* vec_append_zero(struct vec* vec, size_t size); 44 | 45 | static inline void vec_fast_append_8(struct vec* vec, uint8_t value) 46 | { 47 | assert(vec->len < vec->cap); 48 | ((uint8_t*)vec->data)[vec->len++] = value; 49 | } 50 | 51 | static inline void vec_fast_append_32(struct vec* vec, uint32_t value) 52 | { 53 | assert(vec->len + sizeof(value) <= vec->cap); 54 | assert(vec->len % sizeof(value) == 0); 55 | uint32_t* p = (uint32_t*)((uint8_t*)vec->data + vec->len); 56 | *p = value; 57 | vec->len += sizeof(value); 58 | } 59 | 60 | #define vec_for(elem, vec) \ 61 | for (elem = (vec)->data; \ 62 | ((ptrdiff_t)elem - (ptrdiff_t)(vec)->data) < (ptrdiff_t)(vec)->len;\ 63 | ++elem) 64 | 65 | #define vec_for_tail(elem, vec) \ 66 | for (elem = (vec)->data, ++elem; \ 67 | ((ptrdiff_t)elem - (ptrdiff_t)(vec)->data) < (ptrdiff_t)(vec)->len;\ 68 | ++elem) 69 | 70 | #define vec_for_ptr(elem, vec) \ 71 | __typeof__(elem)* ptr_; \ 72 | vec_for(ptr_, vec) \ 73 | if ((elem = *ptr_)) 74 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'neatvnc', 3 | 'c', 4 | version: '0.10-dev', 5 | license: 'ISC', 6 | default_options: [ 7 | 'c_std=gnu11', 8 | 'warning_level=2', 9 | ], 10 | ) 11 | 12 | buildtype = get_option('buildtype') 13 | host_system = host_machine.system() 14 | 15 | c_args = [ 16 | '-D_GNU_SOURCE', 17 | '-fvisibility=hidden', 18 | '-DAML_UNSTABLE_API=1', 19 | 20 | '-Wmissing-prototypes', 21 | '-Wno-unused-parameter', 22 | '-Wno-format-truncation', 23 | ] 24 | 25 | if buildtype != 'debug' and buildtype != 'debugoptimized' 26 | c_args += '-DNDEBUG' 27 | endif 28 | 29 | version = '"@0@"'.format(meson.project_version()) 30 | git = find_program('git', native: true, required: false) 31 | if git.found() 32 | git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'], check: false) 33 | git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'], check: false) 34 | if git_commit.returncode() == 0 and git_branch.returncode() == 0 35 | version = '"v@0@-@1@ (@2@)"'.format( 36 | meson.project_version(), 37 | git_commit.stdout().strip(), 38 | git_branch.stdout().strip(), 39 | ) 40 | endif 41 | endif 42 | add_project_arguments('-DPROJECT_VERSION=@0@'.format(version), language: 'c') 43 | 44 | libdrm_inc_path = get_option('drm_include_path') 45 | if libdrm_inc_path != '' 46 | libdrm_inc = declare_dependency( 47 | include_directories: include_directories(libdrm_inc_path) 48 | ) 49 | else 50 | libdrm_inc = dependency('libdrm').partial_dependency(compile_args: true) 51 | endif 52 | 53 | 54 | add_project_arguments(c_args, language: 'c') 55 | 56 | cc = meson.get_compiler('c') 57 | 58 | libm = cc.find_library('m', required: false) 59 | 60 | pixman = dependency('pixman-1') 61 | libturbojpeg = dependency('libturbojpeg', required: get_option('jpeg')) 62 | gnutls = dependency('gnutls', required: get_option('tls')) 63 | nettle = dependency('nettle', required: get_option('nettle')) 64 | hogweed = dependency('hogweed', required: get_option('nettle')) 65 | gmp = dependency('gmp', required: get_option('nettle')) 66 | zlib = dependency('zlib') 67 | gbm = dependency('gbm', required: get_option('gbm')) 68 | libdrm = dependency('libdrm', required: get_option('h264')) 69 | 70 | libavcodec = dependency('libavcodec', required: get_option('h264')) 71 | libavfilter = dependency('libavfilter', required: get_option('h264')) 72 | libavutil = dependency('libavutil', required: get_option('h264')) 73 | 74 | aml_version = ['>=1.0.0', '<2.0.0'] 75 | aml_project = subproject('aml', required: false, version: aml_version) 76 | if aml_project.found() 77 | aml = aml_project.get_variable('aml_dep') 78 | else 79 | aml = dependency('aml', version: aml_version) 80 | endif 81 | 82 | inc = include_directories('include') 83 | 84 | sources = [ 85 | 'src/auth/common.c', 86 | 'src/server.c', 87 | 'src/vec.c', 88 | 'src/enc/zrle.c', 89 | 'src/enc/raw.c', 90 | 'src/pixels.c', 91 | 'src/fb.c', 92 | 'src/fb_pool.c', 93 | 'src/rcbuf.c', 94 | 'src/stream/interface.c', 95 | 'src/stream/common.c', 96 | 'src/stream/tcp.c', 97 | 'src/desktop-layout.c', 98 | 'src/display.c', 99 | 'src/enc/tight.c', 100 | 'src/enc/util.c', 101 | 'src/qnum-to-evdev.c', 102 | 'src/resampler.c', 103 | 'src/transform-util.c', 104 | 'src/damage-refinery.c', 105 | 'src/enc/interface.c', 106 | 'src/cursor.c', 107 | 'src/logging.c', 108 | 'src/base64.c', 109 | 'src/bandwidth.c', 110 | 'src/parallel-deflate.c', 111 | ] 112 | 113 | dependencies = [ 114 | libm, 115 | pixman, 116 | aml, 117 | zlib, 118 | libdrm_inc, 119 | ] 120 | 121 | enable_websocket = false 122 | 123 | config = configuration_data() 124 | 125 | if libturbojpeg.found() 126 | dependencies += libturbojpeg 127 | config.set('HAVE_JPEG', true) 128 | endif 129 | 130 | if gnutls.found() 131 | sources += [ 132 | 'src/stream/gnutls.c', 133 | 'src/auth/vencrypt.c', 134 | ] 135 | dependencies += gnutls 136 | config.set('ENABLE_TLS', true) 137 | endif 138 | 139 | have_random = false 140 | 141 | if cc.has_function( 142 | 'getrandom', 143 | prefix: '#include \n#include ', 144 | args: [ '-D_GNU_SOURCE' ] 145 | ) 146 | config.set('HAVE_GETRANDOM', true) 147 | have_random = true 148 | endif 149 | 150 | if cc.has_function( 151 | 'arc4random_buf', 152 | prefix: '#include ' 153 | ) 154 | config.set('HAVE_ARC4RANDOM', true) 155 | have_random = true 156 | endif 157 | 158 | 159 | if nettle.found() and hogweed.found() and gmp.found() 160 | dependencies += [ nettle, hogweed, gmp ] 161 | enable_websocket = true 162 | config.set('HAVE_CRYPTO', true) 163 | sources += [ 164 | 'src/crypto/random.c', 165 | 'src/crypto/nettle/cipher.c', 166 | 'src/crypto/nettle/hash.c', 167 | 'src/crypto/nettle/key.c', 168 | 'src/crypto/nettle/rsa.c', 169 | 'src/stream/rsa-aes.c', 170 | 'src/auth/apple-dh.c', 171 | 'src/auth/rsa-aes.c', 172 | ] 173 | if not(have_random) 174 | error('No random generator available') 175 | endif 176 | endif 177 | 178 | if host_system == 'linux' and get_option('systemtap') and cc.has_header('sys/sdt.h') 179 | config.set('HAVE_USDT', true) 180 | endif 181 | 182 | if gbm.found() 183 | dependencies += gbm 184 | config.set('HAVE_GBM', true) 185 | endif 186 | 187 | have_ffmpeg = gbm.found() and libdrm.found() and libavcodec.found() and libavfilter.found() and libavutil.found() 188 | have_v4l2 = gbm.found() and libdrm.found() and cc.check_header('linux/videodev2.h') 189 | 190 | if have_ffmpeg 191 | sources += [ 'src/enc/h264/ffmpeg-impl.c' ] 192 | dependencies += [libdrm, libavcodec, libavfilter, libavutil] 193 | config.set('HAVE_FFMPEG', true) 194 | config.set('HAVE_LIBAVUTIL', true) 195 | endif 196 | 197 | if have_v4l2 198 | sources += [ 'src/enc/h264/v4l2m2m-impl.c' ] 199 | config.set('HAVE_V4L2', true) 200 | endif 201 | 202 | if have_ffmpeg or have_v4l2 203 | sources += [ 'src/enc/h264/encoder.c', 'src/enc/h264/open-h264.c' ] 204 | config.set('ENABLE_OPEN_H264', true) 205 | endif 206 | 207 | if enable_websocket 208 | sources += [ 209 | 'src/stream/ws/handshake.c', 210 | 'src/stream/ws/framing.c', 211 | 'src/stream/ws/http.c', 212 | 'src/stream/ws/ws.c', 213 | ] 214 | config.set('ENABLE_WEBSOCKET', true) 215 | endif 216 | 217 | if get_option('experimental') 218 | if buildtype == 'release' 219 | warning('Experimental features enabled in release build') 220 | endif 221 | config.set('ENABLE_EXPERIMENTAL', true) 222 | endif 223 | 224 | configure_file( 225 | output: 'config.h', 226 | configuration: config, 227 | ) 228 | 229 | neatvnc = library( 230 | 'neatvnc', 231 | sources, 232 | version: '0.0.0', 233 | dependencies: dependencies, 234 | include_directories: inc, 235 | install: true, 236 | ) 237 | 238 | neatvnc_dep = declare_dependency( 239 | include_directories: inc, 240 | link_with: neatvnc, 241 | ) 242 | 243 | if get_option('examples') 244 | subdir('examples') 245 | endif 246 | 247 | if get_option('benchmarks') 248 | subdir('bench') 249 | endif 250 | 251 | if get_option('tests') 252 | subdir('test') 253 | endif 254 | 255 | install_headers('include/neatvnc.h') 256 | 257 | pkgconfig = import('pkgconfig') 258 | pkgconfig.generate( 259 | libraries: neatvnc, 260 | version: meson.project_version(), 261 | filebase: meson.project_name(), 262 | name: meson.project_name(), 263 | description: 'A Neat VNC server library' 264 | ) 265 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('benchmarks', type: 'boolean', value: false, description: 'Build benchmarks') 2 | option('examples', type: 'boolean', value: false, description: 'Build examples') 3 | option('tests', type: 'boolean', value: false, description: 'Build unit tests') 4 | option('jpeg', type: 'feature', value: 'auto', description: 'Enable JPEG compression') 5 | option('tls', type: 'feature', value: 'auto', description: 'Enable encryption & authentication') 6 | option('nettle', type: 'feature', value: 'auto', description: 'Enable nettle low level encryption library') 7 | option('systemtap', type: 'boolean', value: false, description: 'Enable tracing using sdt') 8 | option('gbm', type: 'feature', value: 'auto', description: 'Enable GBM integration') 9 | option('h264', type: 'feature', value: 'auto', description: 'Enable open h264 encoding') 10 | option('experimental', type: 'boolean', value: false, description: 'Enable experimental features') 11 | option('drm_include_path', type: 'string', value: '', description: 'Use specified libdrm include directory') 12 | -------------------------------------------------------------------------------- /src/auth/apple-dh.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 - 2024 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "common.h" 18 | #include "stream/stream.h" 19 | #include "auth/auth.h" 20 | #include "auth/apple-dh.h" 21 | 22 | #define APPLE_DH_SERVER_KEY_LENGTH 256 23 | 24 | int apple_dh_send_public_key(struct nvnc_client* client) 25 | { 26 | client->apple_dh_secret = crypto_keygen(); 27 | assert(client->apple_dh_secret); 28 | 29 | struct crypto_key* pub = 30 | crypto_derive_public_key(client->apple_dh_secret); 31 | assert(pub); 32 | 33 | uint8_t mod[APPLE_DH_SERVER_KEY_LENGTH] = {}; 34 | int mod_len = crypto_key_p(pub, mod, sizeof(mod)); 35 | assert(mod_len == sizeof(mod)); 36 | 37 | uint8_t q[APPLE_DH_SERVER_KEY_LENGTH] = {}; 38 | int q_len = crypto_key_q(pub, q, sizeof(q)); 39 | assert(q_len == sizeof(q)); 40 | 41 | struct rfb_apple_dh_server_msg msg = { 42 | .generator = htons(crypto_key_g(client->apple_dh_secret)), 43 | .key_size = htons(q_len), 44 | }; 45 | 46 | stream_write(client->net_stream, &msg, sizeof(msg), NULL, NULL); 47 | stream_write(client->net_stream, mod, mod_len, NULL, NULL); 48 | stream_write(client->net_stream, q, q_len, NULL, NULL); 49 | 50 | crypto_key_del(pub); 51 | return 0; 52 | } 53 | 54 | int apple_dh_handle_response(struct nvnc_client* client) 55 | { 56 | struct nvnc* server = client->server; 57 | 58 | struct rfb_apple_dh_client_msg* msg = 59 | (void*)(client->msg_buffer + client->buffer_index); 60 | 61 | uint8_t p[APPLE_DH_SERVER_KEY_LENGTH]; 62 | int key_len = crypto_key_p(client->apple_dh_secret, p, sizeof(p)); 63 | assert(key_len == sizeof(p)); 64 | 65 | if (client->buffer_len - client->buffer_index < sizeof(*msg) + key_len) 66 | return 0; 67 | 68 | int g = crypto_key_g(client->apple_dh_secret); 69 | 70 | struct crypto_key* remote_key = crypto_key_new(g, p, key_len, 71 | msg->public_key, key_len); 72 | assert(remote_key); 73 | 74 | struct crypto_key* shared_secret = 75 | crypto_derive_shared_secret(client->apple_dh_secret, remote_key); 76 | assert(shared_secret); 77 | 78 | uint8_t shared_buf[APPLE_DH_SERVER_KEY_LENGTH]; 79 | crypto_key_q(shared_secret, shared_buf, sizeof(shared_buf)); 80 | crypto_key_del(shared_secret); 81 | 82 | uint8_t hash[16] = {}; 83 | crypto_hash_one(hash, sizeof(hash), CRYPTO_HASH_MD5, shared_buf, 84 | sizeof(shared_buf)); 85 | 86 | struct crypto_cipher* cipher; 87 | cipher = crypto_cipher_new(NULL, hash, CRYPTO_CIPHER_AES128_ECB); 88 | assert(cipher); 89 | 90 | char username[128] = {}; 91 | char* password = username + 64; 92 | 93 | crypto_cipher_decrypt(cipher, (uint8_t*)username, NULL, 94 | msg->encrypted_credentials, sizeof(username), NULL, 0); 95 | username[63] = '\0'; 96 | username[127] = '\0'; 97 | crypto_cipher_del(cipher); 98 | 99 | update_min_rtt(client); 100 | 101 | if (!server->auth_fn(username, password, server->auth_ud)) { 102 | security_handshake_failed(client, username, 103 | "Invalid username or password"); 104 | return -1; 105 | } 106 | 107 | security_handshake_ok(client, username); 108 | client->state = VNC_CLIENT_STATE_WAITING_FOR_INIT; 109 | 110 | return sizeof(*msg) + key_len; 111 | } 112 | -------------------------------------------------------------------------------- /src/auth/common.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 - 2024 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "auth/auth.h" 18 | #include "stream/stream.h" 19 | #include "rfb-proto.h" 20 | #include "common.h" 21 | #include "neatvnc.h" 22 | 23 | int security_handshake_failed(struct nvnc_client* client, const char* username, 24 | const char* reason_string) 25 | { 26 | if (username) 27 | nvnc_log(NVNC_LOG_INFO, "Security handshake failed for \"%s\": %s", 28 | username, reason_string); 29 | else 30 | nvnc_log(NVNC_LOG_INFO, "Security handshake failed: %s", 31 | reason_string); 32 | 33 | char buffer[256]; 34 | 35 | uint32_t* result = (uint32_t*)buffer; 36 | 37 | struct rfb_error_reason* reason = 38 | (struct rfb_error_reason*)(buffer + sizeof(*result)); 39 | 40 | *result = htonl(RFB_SECURITY_HANDSHAKE_FAILED); 41 | reason->length = htonl(strlen(reason_string)); 42 | strcpy(reason->message, reason_string); 43 | 44 | size_t len = sizeof(*result) + sizeof(*reason) + strlen(reason_string); 45 | stream_write(client->net_stream, buffer, len, close_after_write, 46 | client->net_stream); 47 | 48 | stream_ref(client->net_stream); 49 | 50 | nvnc_client_close(client); 51 | return 0; 52 | } 53 | 54 | int security_handshake_ok(struct nvnc_client* client, const char* username) 55 | { 56 | if (username) { 57 | nvnc_log(NVNC_LOG_INFO, "User \"%s\" authenticated", username); 58 | 59 | strncpy(client->username, username, sizeof(client->username)); 60 | client->username[sizeof(client->username) - 1] = '\0'; 61 | } 62 | 63 | uint32_t result = htonl(RFB_SECURITY_HANDSHAKE_OK); 64 | return stream_write(client->net_stream, &result, sizeof(result), NULL, 65 | NULL); 66 | } 67 | -------------------------------------------------------------------------------- /src/auth/vencrypt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 - 2024 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "common.h" 18 | #include "stream/stream.h" 19 | #include "auth/auth.h" 20 | #include "auth/vencrypt.h" 21 | 22 | #include 23 | 24 | static int send_byte(struct nvnc_client* client, uint8_t value) 25 | { 26 | return stream_write(client->net_stream, &value, 1, NULL, NULL); 27 | } 28 | 29 | static int send_byte_and_close(struct nvnc_client* client, uint8_t value) 30 | { 31 | stream_ref(client->net_stream); 32 | return stream_write(client->net_stream, &value, 1, close_after_write, 33 | client->net_stream); 34 | } 35 | 36 | int vencrypt_send_version(struct nvnc_client* client) 37 | { 38 | struct rfb_vencrypt_version_msg msg = { 39 | .major = 0, 40 | .minor = 2, 41 | }; 42 | 43 | return stream_write(client->net_stream, &msg, sizeof(msg), NULL, NULL); 44 | } 45 | 46 | static int on_vencrypt_version_message(struct nvnc_client* client) 47 | { 48 | struct rfb_vencrypt_version_msg* msg = 49 | (struct rfb_vencrypt_version_msg*)&client->msg_buffer[client->buffer_index]; 50 | 51 | if (client->buffer_len - client->buffer_index < sizeof(*msg)) 52 | return 0; 53 | 54 | if (msg->major != 0 || msg->minor != 2) { 55 | security_handshake_failed(client, NULL, 56 | "Unsupported VeNCrypt version"); 57 | return -1; 58 | } 59 | 60 | send_byte(client, 0); 61 | 62 | struct rfb_vencrypt_subtypes_msg result = { .n = 1, }; 63 | result.types[0] = htonl(RFB_VENCRYPT_X509_PLAIN); 64 | 65 | update_min_rtt(client); 66 | 67 | stream_write(client->net_stream, &result, sizeof(result), NULL, NULL); 68 | 69 | client->state = VNC_CLIENT_STATE_WAITING_FOR_VENCRYPT_SUBTYPE; 70 | 71 | return sizeof(*msg); 72 | } 73 | 74 | static int on_vencrypt_subtype_message(struct nvnc_client* client) 75 | { 76 | uint32_t* msg = (uint32_t*)&client->msg_buffer[client->buffer_index]; 77 | 78 | if (client->buffer_len - client->buffer_index < sizeof(*msg)) 79 | return 0; 80 | 81 | enum rfb_vencrypt_subtype subtype = ntohl(*msg); 82 | 83 | if (subtype != RFB_VENCRYPT_X509_PLAIN) { 84 | send_byte_and_close(client, 0); 85 | return sizeof(*msg); 86 | } 87 | 88 | update_min_rtt(client); 89 | 90 | send_byte(client, 1); 91 | 92 | if (stream_upgrade_to_tls(client->net_stream, client->server->tls_creds) < 0) { 93 | nvnc_client_close(client); 94 | return -1; 95 | } 96 | 97 | client->state = VNC_CLIENT_STATE_WAITING_FOR_VENCRYPT_PLAIN_AUTH; 98 | 99 | return sizeof(*msg); 100 | } 101 | 102 | static int on_vencrypt_plain_auth_message(struct nvnc_client* client) 103 | { 104 | struct nvnc* server = client->server; 105 | 106 | struct rfb_vencrypt_plain_auth_msg* msg = 107 | (void*)(client->msg_buffer + client->buffer_index); 108 | 109 | if (client->buffer_len - client->buffer_index < sizeof(*msg)) 110 | return 0; 111 | 112 | uint32_t ulen = ntohl(msg->username_len); 113 | uint32_t plen = ntohl(msg->password_len); 114 | 115 | if (client->buffer_len - client->buffer_index < sizeof(*msg) + ulen + plen) 116 | return 0; 117 | 118 | char username[256]; 119 | char password[256]; 120 | 121 | memcpy(username, msg->text, MIN(ulen, sizeof(username) - 1)); 122 | memcpy(password, msg->text + ulen, MIN(plen, sizeof(password) - 1)); 123 | 124 | username[MIN(ulen, sizeof(username) - 1)] = '\0'; 125 | password[MIN(plen, sizeof(password) - 1)] = '\0'; 126 | 127 | update_min_rtt(client); 128 | 129 | if (!server->auth_fn(username, password, server->auth_ud)) { 130 | security_handshake_failed(client, username, 131 | "Invalid username or password"); 132 | return -1; 133 | } 134 | 135 | security_handshake_ok(client, username); 136 | client->state = VNC_CLIENT_STATE_WAITING_FOR_INIT; 137 | 138 | return sizeof(*msg) + ulen + plen; 139 | } 140 | 141 | int vencrypt_handle_message(struct nvnc_client* client) 142 | { 143 | switch (client->state) { 144 | case VNC_CLIENT_STATE_WAITING_FOR_VENCRYPT_VERSION: 145 | return on_vencrypt_version_message(client); 146 | case VNC_CLIENT_STATE_WAITING_FOR_VENCRYPT_SUBTYPE: 147 | return on_vencrypt_subtype_message(client); 148 | case VNC_CLIENT_STATE_WAITING_FOR_VENCRYPT_PLAIN_AUTH: 149 | return on_vencrypt_plain_auth_message(client); 150 | default:; 151 | } 152 | nvnc_log(NVNC_LOG_PANIC, "Unhandled client state: %d", client->state); 153 | return 0; 154 | } 155 | -------------------------------------------------------------------------------- /src/bandwidth.c: -------------------------------------------------------------------------------- 1 | #include "bandwidth.h" 2 | 3 | #include 4 | #include 5 | 6 | #define SAMPLES_MAX 16 7 | 8 | // bandwidth estimator 9 | struct bwe { 10 | int rtt_min; 11 | int n_samples; 12 | int index; 13 | double estimate; 14 | struct bwe_sample samples[0]; 15 | }; 16 | 17 | struct bwe* bwe_create(int rtt_min) 18 | { 19 | struct bwe* self = calloc(1, sizeof(*self) + sizeof(*self->samples) * 20 | SAMPLES_MAX); 21 | if (!self) 22 | return NULL; 23 | 24 | return self; 25 | } 26 | 27 | void bwe_destroy(struct bwe* self) 28 | { 29 | free(self); 30 | } 31 | 32 | static inline const struct bwe_sample* get_sample(const struct bwe* self, int index) 33 | { 34 | int head = (self->index + index - self->n_samples + SAMPLES_MAX) 35 | % SAMPLES_MAX; 36 | return &self->samples[head]; 37 | } 38 | 39 | // Under non-congested circumstances, there will be some space between packages 40 | static double estimate_non_congested_bandwidth(const struct bwe* self) 41 | { 42 | int bytes_total = 0; 43 | int bw_delay_total = 0; 44 | 45 | for (int i = 0; i < self->n_samples; ++i) { 46 | const struct bwe_sample* s = get_sample(self, i); 47 | 48 | int rtt = s->arrival_time - s->departure_time; 49 | int bw_delay = rtt - self->rtt_min; 50 | 51 | bytes_total += s->bytes; 52 | bw_delay_total += bw_delay; 53 | } 54 | 55 | return (double)bytes_total / (bw_delay_total * 1e-6); 56 | } 57 | 58 | // Under congested circumstances, there will be no space between packages 59 | static double estimate_congested_bandwidth(const struct bwe* self) 60 | { 61 | if (self->n_samples == 0) 62 | return 0; 63 | 64 | const struct bwe_sample* s0 = get_sample(self, 0); 65 | const struct bwe_sample* s1 = get_sample(self, self->n_samples - 1); 66 | 67 | int bytes_total = 0; 68 | 69 | for (int i = 0; i < self->n_samples; ++i) { 70 | const struct bwe_sample* s = get_sample(self, i); 71 | bytes_total += s->bytes; 72 | } 73 | 74 | int rtt = s1->arrival_time - s0->departure_time; 75 | int bw_delay = rtt - self->rtt_min; 76 | 77 | return (double)bytes_total / (bw_delay * 1e-6); 78 | } 79 | 80 | static void update_estimate(struct bwe* self) 81 | { 82 | double non_congested = estimate_non_congested_bandwidth(self); 83 | double congested = estimate_congested_bandwidth(self); 84 | self->estimate = fmax(non_congested, congested); 85 | } 86 | 87 | void bwe_feed(struct bwe* self, const struct bwe_sample* sample) 88 | { 89 | self->samples[self->index] = *sample; 90 | self->index = (self->index + 1) % SAMPLES_MAX; 91 | 92 | if (self->n_samples < SAMPLES_MAX) 93 | self->n_samples++; 94 | 95 | update_estimate(self); 96 | } 97 | 98 | void bwe_update_rtt_min(struct bwe* self, int rtt_min) 99 | { 100 | self->rtt_min = rtt_min; 101 | } 102 | 103 | int bwe_get_estimate(const struct bwe* self) 104 | { 105 | return round(self->estimate); 106 | } 107 | -------------------------------------------------------------------------------- /src/base64.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023 Andri Yngvason 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 12 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include "base64.h" 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | static const char base64_enc_lut[] = 25 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 26 | 27 | static const uint8_t base64_validation_lut[256] = { 28 | ['A'] = 1, ['B'] = 1, ['C'] = 1, ['D'] = 1, 29 | ['E'] = 1, ['F'] = 1, ['G'] = 1, ['H'] = 1, 30 | ['I'] = 1, ['J'] = 1, ['K'] = 1, ['L'] = 1, 31 | ['M'] = 1, ['N'] = 1, ['O'] = 1, ['P'] = 1, 32 | ['Q'] = 1, ['R'] = 1, ['S'] = 1, ['T'] = 1, 33 | ['U'] = 1, ['V'] = 1, ['W'] = 1, ['X'] = 1, 34 | ['Y'] = 1, ['Z'] = 1, ['a'] = 1, ['b'] = 1, 35 | ['c'] = 1, ['d'] = 1, ['e'] = 1, ['f'] = 1, 36 | ['g'] = 1, ['h'] = 1, ['i'] = 1, ['j'] = 1, 37 | ['k'] = 1, ['l'] = 1, ['m'] = 1, ['n'] = 1, 38 | ['o'] = 1, ['p'] = 1, ['q'] = 1, ['r'] = 1, 39 | ['s'] = 1, ['t'] = 1, ['u'] = 1, ['v'] = 1, 40 | ['w'] = 1, ['x'] = 1, ['y'] = 1, ['z'] = 1, 41 | ['0'] = 1, ['1'] = 1, ['2'] = 1, ['3'] = 1, 42 | ['4'] = 1, ['5'] = 1, ['6'] = 1, ['7'] = 1, 43 | ['8'] = 1, ['9'] = 1, ['+'] = 1, ['/'] = 1, 44 | ['-'] = 1, ['_'] = 1, ['='] = 1, 45 | }; 46 | 47 | static const uint8_t base64_dec_lut[256] = { 48 | ['A'] = 0x00, ['B'] = 0x01, ['C'] = 0x02, ['D'] = 0x03, 49 | ['E'] = 0x04, ['F'] = 0x05, ['G'] = 0x06, ['H'] = 0x07, 50 | ['I'] = 0x08, ['J'] = 0x09, ['K'] = 0x0a, ['L'] = 0x0b, 51 | ['M'] = 0x0c, ['N'] = 0x0d, ['O'] = 0x0e, ['P'] = 0x0f, 52 | ['Q'] = 0x10, ['R'] = 0x11, ['S'] = 0x12, ['T'] = 0x13, 53 | ['U'] = 0x14, ['V'] = 0x15, ['W'] = 0x16, ['X'] = 0x17, 54 | ['Y'] = 0x18, ['Z'] = 0x19, ['a'] = 0x1a, ['b'] = 0x1b, 55 | ['c'] = 0x1c, ['d'] = 0x1d, ['e'] = 0x1e, ['f'] = 0x1f, 56 | ['g'] = 0x20, ['h'] = 0x21, ['i'] = 0x22, ['j'] = 0x23, 57 | ['k'] = 0x24, ['l'] = 0x25, ['m'] = 0x26, ['n'] = 0x27, 58 | ['o'] = 0x28, ['p'] = 0x29, ['q'] = 0x2a, ['r'] = 0x2b, 59 | ['s'] = 0x2c, ['t'] = 0x2d, ['u'] = 0x2e, ['v'] = 0x2f, 60 | ['w'] = 0x30, ['x'] = 0x31, ['y'] = 0x32, ['z'] = 0x33, 61 | ['0'] = 0x34, ['1'] = 0x35, ['2'] = 0x36, ['3'] = 0x37, 62 | ['4'] = 0x38, ['5'] = 0x39, ['6'] = 0x3a, ['7'] = 0x3b, 63 | ['8'] = 0x3c, ['9'] = 0x3d, ['+'] = 0x3e, ['/'] = 0x3f, 64 | ['-'] = 0x3e, ['_'] = 0x3f, 65 | }; 66 | 67 | void base64_encode(char* dst, const uint8_t* src, size_t src_len) 68 | { 69 | size_t i = 0; 70 | 71 | for (; i < src_len / 3; ++i) { 72 | uint32_t tmp = 0; 73 | tmp |= (uint32_t)src[i * 3 + 0] << 16; 74 | tmp |= (uint32_t)src[i * 3 + 1] << 8; 75 | tmp |= (uint32_t)src[i * 3 + 2]; 76 | 77 | dst[i * 4 + 0] = base64_enc_lut[tmp >> 18]; 78 | dst[i * 4 + 1] = base64_enc_lut[(tmp >> 12) & 0x3f]; 79 | dst[i * 4 + 2] = base64_enc_lut[(tmp >> 6) & 0x3f]; 80 | dst[i * 4 + 3] = base64_enc_lut[tmp & 0x3f]; 81 | } 82 | 83 | size_t rem = src_len % 3; 84 | if (rem == 0) { 85 | dst[i * 4] = '\0'; 86 | return; 87 | } 88 | 89 | uint32_t tmp = 0; 90 | for (size_t r = 0; r < rem; ++r) { 91 | size_t s = (2 - r) * 8; 92 | tmp |= (uint32_t)src[i * 3 + r] << s; 93 | } 94 | 95 | size_t di = 0; 96 | for (; di < rem + 1; ++di) { 97 | size_t s = (3 - di) * 6; 98 | dst[i * 4 + di] = base64_enc_lut[(tmp >> s) & 0x3f]; 99 | } 100 | 101 | for (; di < 4; ++di) { 102 | dst[i * 4 + di] = '='; 103 | } 104 | 105 | dst[i * 4 + di] = '\0'; 106 | 107 | } 108 | 109 | static bool base64_is_valid(const char* src) 110 | { 111 | for (int i = 0; src[i]; i++) 112 | if (!base64_validation_lut[(uint8_t)src[i]]) 113 | return false; 114 | return true; 115 | } 116 | 117 | ssize_t base64_decode(uint8_t* dst, const char* src) 118 | { 119 | if (!base64_is_valid(src)) 120 | return -1; 121 | 122 | size_t src_len = strcspn(src, "="); 123 | size_t i = 0; 124 | 125 | for (; i < src_len / 4; ++i) { 126 | uint32_t tmp = 0; 127 | tmp |= (uint32_t)base64_dec_lut[(uint8_t)src[i * 4 + 0]] << 18; 128 | tmp |= (uint32_t)base64_dec_lut[(uint8_t)src[i * 4 + 1]] << 12; 129 | tmp |= (uint32_t)base64_dec_lut[(uint8_t)src[i * 4 + 2]] << 6; 130 | tmp |= (uint32_t)base64_dec_lut[(uint8_t)src[i * 4 + 3]]; 131 | 132 | dst[i * 3 + 0] = tmp >> 16; 133 | dst[i * 3 + 1] = (tmp >> 8) & 0xff; 134 | dst[i * 3 + 2] = tmp & 0xff; 135 | } 136 | 137 | size_t rem = src_len % 4; 138 | if (rem == 0) 139 | return i * 3; 140 | 141 | size_t di = 0; 142 | 143 | uint32_t tmp = 0; 144 | for (size_t r = 0; r < rem; ++r) { 145 | size_t s = (3 - r) * 6; 146 | tmp |= (uint32_t)base64_dec_lut[(uint8_t)src[i * 4 + r]] << s; 147 | } 148 | 149 | for (; di < (rem * 3) / 4; ++di) { 150 | size_t s = (2 - di) * 8; 151 | dst[i * 3 + di] = (tmp >> s) & 0xff; 152 | } 153 | 154 | return i * 3 + di; 155 | } 156 | -------------------------------------------------------------------------------- /src/crypto/nettle/hash.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 - 2024 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "crypto.h" 18 | #include "crypto/nettle/common.h" 19 | #include "neatvnc.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | struct crypto_hash { 27 | union { 28 | struct md5_ctx md5; 29 | struct sha1_ctx sha1; 30 | struct sha256_ctx sha256; 31 | } ctx; 32 | 33 | void (*update)(void* ctx, size_t len, const uint8_t* src); 34 | void (*digest)(void* ctx, size_t len, uint8_t* dst); 35 | }; 36 | 37 | struct crypto_hash* crypto_hash_new(enum crypto_hash_type type) 38 | { 39 | struct crypto_hash* self = calloc(1, sizeof(*self)); 40 | if (!self) 41 | return NULL; 42 | 43 | switch (type) { 44 | case CRYPTO_HASH_INVALID: 45 | nvnc_log(NVNC_LOG_PANIC, "Invalid hash type"); 46 | break; 47 | case CRYPTO_HASH_MD5: 48 | md5_init(&self->ctx.md5); 49 | self->update = (void*)nettle_md5_update; 50 | self->digest = (void*)nettle_md5_digest; 51 | break; 52 | case CRYPTO_HASH_SHA1: 53 | sha1_init(&self->ctx.sha1); 54 | self->update = (void*)nettle_sha1_update; 55 | self->digest = (void*)nettle_sha1_digest; 56 | break; 57 | case CRYPTO_HASH_SHA256: 58 | sha256_init(&self->ctx.sha256); 59 | self->update = (void*)nettle_sha256_update; 60 | self->digest = (void*)nettle_sha256_digest; 61 | break; 62 | } 63 | 64 | return self; 65 | } 66 | 67 | void crypto_hash_del(struct crypto_hash* self) 68 | { 69 | free(self); 70 | } 71 | 72 | void crypto_hash_append(struct crypto_hash* self, const uint8_t* src, 73 | size_t len) 74 | { 75 | self->update(&self->ctx, len, src); 76 | } 77 | 78 | void crypto_hash_digest(struct crypto_hash* self, uint8_t* dst, size_t len) 79 | { 80 | self->digest(&self->ctx, len, dst); 81 | } 82 | 83 | void crypto_hash_one(uint8_t* dst, size_t dst_len, enum crypto_hash_type type, 84 | const uint8_t* src, size_t src_len) 85 | { 86 | struct crypto_hash *hash = crypto_hash_new(type); 87 | crypto_hash_append(hash, src, src_len); 88 | crypto_hash_digest(hash, dst, dst_len); 89 | crypto_hash_del(hash); 90 | } 91 | 92 | void crypto_hash_many(uint8_t* dst, size_t dst_len, enum crypto_hash_type type, 93 | const struct crypto_data_entry *src) 94 | { 95 | struct crypto_hash *hash = crypto_hash_new(type); 96 | for (int i = 0; src[i].data && src[i].len; ++i) 97 | crypto_hash_append(hash, src[i].data, src[i].len); 98 | crypto_hash_digest(hash, dst, dst_len); 99 | crypto_hash_del(hash); 100 | } 101 | 102 | -------------------------------------------------------------------------------- /src/crypto/nettle/key.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 - 2024 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "crypto.h" 18 | #include "crypto/nettle/common.h" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | struct crypto_key { 28 | int g; 29 | mpz_t p; 30 | mpz_t q; 31 | }; 32 | 33 | struct crypto_key *crypto_key_new(int g, const uint8_t* p, uint32_t p_len, 34 | const uint8_t* q, uint32_t q_len) 35 | { 36 | struct crypto_key* self = calloc(1, sizeof(*self)); 37 | if (!self) 38 | return NULL; 39 | 40 | self->g = g; 41 | 42 | mpz_init(self->p); 43 | crypto_import(self->p, p, p_len); 44 | 45 | mpz_init(self->q); 46 | crypto_import(self->q, q, q_len); 47 | 48 | return self; 49 | } 50 | 51 | void crypto_key_del(struct crypto_key* key) 52 | { 53 | if (!key) 54 | return; 55 | mpz_clear(key->q); 56 | mpz_clear(key->p); 57 | free(key); 58 | } 59 | 60 | int crypto_key_g(const struct crypto_key* key) 61 | { 62 | return key->g; 63 | } 64 | 65 | uint32_t crypto_key_p(const struct crypto_key* key, uint8_t* dst, 66 | uint32_t dst_size) 67 | { 68 | return crypto_export(dst, dst_size, key->p); 69 | } 70 | 71 | uint32_t crypto_key_q(const struct crypto_key* key, uint8_t* dst, 72 | uint32_t dst_size) 73 | { 74 | return crypto_export(dst, dst_size, key->q); 75 | } 76 | 77 | static void initialise_p(mpz_t p) 78 | { 79 | // RFC 3526, section 3 80 | static const char s[] = 81 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" 82 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" 83 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" 84 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" 85 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" 86 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" 87 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" 88 | "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" 89 | "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" 90 | "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" 91 | "15728E5A8AACAA68FFFFFFFFFFFFFFFF"; 92 | 93 | char buf[256]; 94 | size_t len = 0; 95 | struct base16_decode_ctx ctx; 96 | nettle_base16_decode_init(&ctx); 97 | nettle_base16_decode_update(&ctx, &len, (uint8_t*)buf, sizeof(s) - 1, s); 98 | nettle_base16_decode_final(&ctx); 99 | assert(len == sizeof(buf)); 100 | 101 | crypto_import(p, (const uint8_t*)buf, sizeof(buf)); 102 | } 103 | 104 | static void generate_random(mpz_t n) 105 | { 106 | uint8_t buf[256]; 107 | crypto_random(buf, sizeof(buf)); 108 | crypto_import(n, buf, sizeof(buf)); 109 | } 110 | 111 | struct crypto_key* crypto_keygen(void) 112 | { 113 | struct crypto_key* self = calloc(1, sizeof(*self)); 114 | if (!self) 115 | return NULL; 116 | 117 | self->g = 2; 118 | 119 | mpz_init(self->p); 120 | initialise_p(self->p); 121 | 122 | mpz_init(self->q); 123 | generate_random(self->q); 124 | 125 | return self; 126 | } 127 | 128 | struct crypto_key* crypto_derive_public_key(const struct crypto_key* priv) 129 | { 130 | struct crypto_key* pub = calloc(1, sizeof(*pub)); 131 | if (!pub) 132 | return NULL; 133 | 134 | pub->g = priv->g; 135 | mpz_set(pub->p, priv->p); 136 | mpz_init(pub->q); 137 | 138 | mpz_t g; 139 | mpz_init(g); 140 | mpz_set_ui(g, priv->g); 141 | 142 | mpz_powm_sec(pub->q, g, priv->q, priv->p); 143 | mpz_clear(g); 144 | 145 | return pub; 146 | } 147 | 148 | struct crypto_key* crypto_derive_shared_secret( 149 | const struct crypto_key* own_secret, 150 | const struct crypto_key* remote_public_key) 151 | { 152 | if (own_secret->g != remote_public_key->g) { 153 | return NULL; 154 | } 155 | 156 | if (mpz_cmp(own_secret->p, remote_public_key->p) != 0) { 157 | return NULL; 158 | } 159 | 160 | struct crypto_key* shared = calloc(1, sizeof(*shared)); 161 | if (!shared) 162 | return NULL; 163 | 164 | shared->g = own_secret->g; 165 | mpz_set(shared->p, own_secret->p); 166 | 167 | mpz_t g; 168 | mpz_init(g); 169 | mpz_set_ui(g, own_secret->g); 170 | 171 | mpz_powm_sec(shared->q, remote_public_key->q, own_secret->q, 172 | own_secret->p); 173 | mpz_clear(g); 174 | 175 | return shared; 176 | } 177 | 178 | -------------------------------------------------------------------------------- /src/crypto/nettle/rsa.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 - 2024 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "crypto.h" 18 | #include "crypto/nettle/common.h" 19 | #include "vec.h" 20 | #include "neatvnc.h" 21 | #include "base64.h" 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | struct crypto_rsa_pub_key { 28 | struct rsa_public_key key; 29 | }; 30 | 31 | struct crypto_rsa_priv_key { 32 | struct rsa_private_key key; 33 | }; 34 | 35 | struct crypto_rsa_pub_key *crypto_rsa_pub_key_new(void) 36 | { 37 | struct crypto_rsa_pub_key* self = calloc(1, sizeof(*self)); 38 | if (!self) 39 | return NULL; 40 | 41 | rsa_public_key_init(&self->key); 42 | return self; 43 | } 44 | 45 | void crypto_rsa_pub_key_del(struct crypto_rsa_pub_key* self) 46 | { 47 | if (!self) 48 | return; 49 | rsa_public_key_clear(&self->key); 50 | free(self); 51 | } 52 | 53 | struct crypto_rsa_pub_key* crypto_rsa_pub_key_import(const uint8_t* modulus, 54 | const uint8_t* exponent, size_t size) 55 | { 56 | struct crypto_rsa_pub_key* self = crypto_rsa_pub_key_new(); 57 | if (!self) 58 | return NULL; 59 | 60 | rsa_public_key_init(&self->key); 61 | mpz_init(self->key.n); 62 | crypto_import(self->key.n, modulus, size); 63 | mpz_init(self->key.e); 64 | crypto_import(self->key.e, exponent, size); 65 | rsa_public_key_prepare(&self->key); 66 | 67 | return self; 68 | } 69 | 70 | bool crypto_rsa_priv_key_import_pkcs1_der(struct crypto_rsa_priv_key* priv, 71 | struct crypto_rsa_pub_key* pub, const uint8_t* key, 72 | size_t size) 73 | { 74 | return rsa_keypair_from_der(&pub->key, &priv->key, 0, size, key); 75 | } 76 | 77 | bool crypto_rsa_priv_key_load(struct crypto_rsa_priv_key* priv, 78 | struct crypto_rsa_pub_key* pub, const char* path) 79 | { 80 | FILE* stream = fopen(path, "r"); 81 | if (!stream) { 82 | nvnc_log(NVNC_LOG_ERROR, "Could not open file: %m"); 83 | return false; 84 | } 85 | 86 | char* line = NULL; 87 | size_t n = 0; 88 | if (getline(&line, &n, stream) < 0) { 89 | nvnc_log(NVNC_LOG_ERROR, "RSA private key file is not PEM"); 90 | return false; 91 | } 92 | 93 | char head[128]; 94 | strncpy(head, line, sizeof(head)); 95 | head[sizeof(head) - 1] = '\0'; 96 | char* end = strchr(head, '\n'); 97 | if (end) 98 | *end = '\0'; 99 | 100 | nvnc_trace("Read PEM head: \"%s\"\n", head); 101 | 102 | struct vec base64_der; 103 | vec_init(&base64_der, 4096); 104 | 105 | while (getline(&line, &n, stream) >= 0) { 106 | if (strncmp(line, "-----END", 8) == 0) 107 | break; 108 | 109 | 110 | vec_append(&base64_der, line, strcspn(line, "\n")); 111 | } 112 | free(line); 113 | fclose(stream); 114 | 115 | vec_append_zero(&base64_der, 1); 116 | 117 | uint8_t* der = malloc(BASE64_DECODED_MAX_SIZE(base64_der.len)); 118 | assert(der); 119 | 120 | ssize_t der_len = base64_decode(der, base64_der.data); 121 | vec_destroy(&base64_der); 122 | if (der_len < 0) { 123 | free(der); 124 | return false; 125 | } 126 | 127 | bool ok = false; 128 | if (strcmp(head, "-----BEGIN RSA PRIVATE KEY-----") == 0) { 129 | ok = crypto_rsa_priv_key_import_pkcs1_der(priv, pub, der, der_len); 130 | } else { 131 | nvnc_log(NVNC_LOG_ERROR, "Unsupported RSA private key format"); 132 | } 133 | 134 | nvnc_trace("Private key is %d bits long", priv->key.size * 8); 135 | 136 | free(der); 137 | return ok; 138 | } 139 | 140 | void crypto_rsa_pub_key_modulus(const struct crypto_rsa_pub_key* key, 141 | uint8_t* dst, size_t dst_size) 142 | { 143 | crypto_export(dst, dst_size, key->key.n); 144 | } 145 | 146 | void crypto_rsa_pub_key_exponent(const struct crypto_rsa_pub_key* key, 147 | uint8_t* dst, size_t dst_size) 148 | { 149 | char* str = mpz_get_str(NULL, 16, key->key.e); 150 | free(str); 151 | 152 | crypto_export(dst, dst_size, key->key.e); 153 | } 154 | 155 | struct crypto_rsa_priv_key *crypto_rsa_priv_key_new(void) 156 | { 157 | struct crypto_rsa_priv_key* self = calloc(1, sizeof(*self)); 158 | if (!self) 159 | return NULL; 160 | 161 | rsa_private_key_init(&self->key); 162 | return self; 163 | } 164 | 165 | void crypto_rsa_priv_key_del(struct crypto_rsa_priv_key* self) 166 | { 167 | if (!self) 168 | return; 169 | rsa_private_key_clear(&self->key); 170 | free(self); 171 | } 172 | 173 | size_t crypto_rsa_pub_key_length(const struct crypto_rsa_pub_key* key) 174 | { 175 | return key->key.size; 176 | } 177 | 178 | static void generate_random_for_rsa(void* random_ctx, size_t len, uint8_t* dst) 179 | { 180 | crypto_random(dst, len); 181 | } 182 | 183 | bool crypto_rsa_keygen(struct crypto_rsa_pub_key* pub, 184 | struct crypto_rsa_priv_key* priv) 185 | { 186 | void* random_ctx = NULL; 187 | nettle_random_func* random_func = generate_random_for_rsa; 188 | void* progress_ctx = NULL; 189 | nettle_progress_func* progress = NULL; 190 | 191 | int rc = rsa_generate_keypair(&pub->key, &priv->key, random_ctx, 192 | random_func, progress_ctx, progress, 2048, 30); 193 | return rc != 0; 194 | } 195 | 196 | ssize_t crypto_rsa_encrypt(struct crypto_rsa_pub_key* pub, uint8_t* dst, 197 | size_t dst_size, const uint8_t* src, size_t src_size) 198 | { 199 | mpz_t ciphertext; 200 | mpz_init(ciphertext); 201 | int r = rsa_encrypt(&pub->key, NULL, generate_random_for_rsa, 202 | src_size, src, ciphertext); 203 | if (r == 0) { 204 | mpz_clear(ciphertext); 205 | return -1; 206 | } 207 | size_t len = crypto_export(dst, dst_size, ciphertext); 208 | mpz_clear(ciphertext); 209 | return len; 210 | } 211 | 212 | ssize_t crypto_rsa_decrypt(struct crypto_rsa_priv_key* priv, uint8_t* dst, 213 | size_t dst_size, const uint8_t* src, size_t src_size) 214 | { 215 | mpz_t ciphertext; 216 | mpz_init(ciphertext); 217 | crypto_import(ciphertext, src, src_size); 218 | int r = rsa_decrypt(&priv->key, &dst_size, dst, ciphertext); 219 | mpz_clear(ciphertext); 220 | return r != 0 ? (ssize_t)dst_size : -1; 221 | } 222 | -------------------------------------------------------------------------------- /src/crypto/random.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 - 2024 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "crypto.h" 18 | #include "config.h" 19 | 20 | #include 21 | 22 | #if defined(HAVE_GETRANDOM) 23 | #include 24 | #elif defined(HAVE_ARC4RANDOM) 25 | #include 26 | #endif 27 | 28 | 29 | void crypto_random(uint8_t* dst, size_t len) 30 | { 31 | #if defined(HAVE_GETRANDOM) 32 | getrandom(dst, len, 0); 33 | #elif defined(HAVE_ARC4RANDOM) 34 | arc4random_buf(dst, len); 35 | #else 36 | #error No random generator available 37 | #endif 38 | } 39 | -------------------------------------------------------------------------------- /src/cursor.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "cursor.h" 18 | 19 | #include "fb.h" 20 | #include "pixels.h" 21 | #include "rfb-proto.h" 22 | #include "vec.h" 23 | #include "enc/util.h" 24 | #include "resampler.h" 25 | #include "transform-util.h" 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #define UDIV_UP(a, b) (((a) + (b) - 1) / (b)) 32 | 33 | static struct nvnc_fb* apply_transform(struct nvnc_fb* fb) 34 | { 35 | if (fb->transform == NVNC_TRANSFORM_NORMAL) { 36 | nvnc_fb_ref(fb); 37 | return fb; 38 | } 39 | 40 | uint32_t width = fb->width; 41 | uint32_t height = fb->height; 42 | 43 | nvnc_transform_dimensions(fb->transform, &width, &height); 44 | struct nvnc_fb* dst = nvnc_fb_new(width, height, fb->fourcc_format, 45 | width); 46 | assert(dst); 47 | 48 | // TODO: Don't assume bpp 49 | memset(dst->addr, 0, width * height * 4); 50 | 51 | resample_now(dst, fb, NULL); 52 | 53 | return dst; 54 | } 55 | 56 | int cursor_encode(struct vec* dst, struct rfb_pixel_format* pixfmt, 57 | struct nvnc_fb* image, uint32_t width, uint32_t height, 58 | uint32_t hotspot_x, uint32_t hotspot_y) 59 | { 60 | int rc = -1; 61 | 62 | // Empty cursor 63 | if (!image) 64 | return encode_rect_head(dst, RFB_ENCODING_CURSOR, 0, 0, 0, 0); 65 | 66 | nvnc_transform_dimensions(image->transform, &width, &height); 67 | nvnc_transform_dimensions(image->transform, &hotspot_x, &hotspot_y); 68 | 69 | if (nvnc_fb_map(image) < 0) 70 | return -1; 71 | 72 | // This returns a new image that needs to be unreferenced later 73 | image = apply_transform(image); 74 | 75 | assert(width <= image->width); 76 | assert(height <= image->height); 77 | 78 | struct rfb_pixel_format srcfmt = { 0 }; 79 | rc = rfb_pixfmt_from_fourcc(&srcfmt, image->fourcc_format); 80 | if (rc < 0) 81 | goto failure; 82 | 83 | rc = encode_rect_head(dst, RFB_ENCODING_CURSOR, hotspot_x, hotspot_y, 84 | width, height); 85 | if (rc < 0) 86 | goto failure; 87 | 88 | int bpp = pixfmt->bits_per_pixel / 8; 89 | size_t size = width * height; 90 | 91 | rc = vec_reserve(dst, dst->len + size * bpp + UDIV_UP(size, 8)); 92 | if (rc < 0) 93 | goto failure; 94 | 95 | uint8_t* dstdata = dst->data; 96 | dstdata += dst->len; 97 | 98 | int32_t src_byte_stride = image->stride * (srcfmt.bits_per_pixel / 8); 99 | 100 | if((int32_t)width == image->stride) { 101 | pixel_to_cpixel(dstdata, pixfmt, image->addr, &srcfmt, bpp, size); 102 | } else { 103 | for (uint32_t y = 0; y < height; ++y) { 104 | pixel_to_cpixel(dstdata + y * bpp * width, pixfmt, 105 | (uint8_t*)image->addr + y * src_byte_stride, 106 | &srcfmt, bpp, width); 107 | } 108 | } 109 | 110 | dst->len += size * bpp; 111 | dstdata = dst->data; 112 | dstdata += dst->len; 113 | 114 | for (uint32_t y = 0; y < height; ++y) { 115 | if (!extract_alpha_mask(dstdata + y * UDIV_UP(width, 8), 116 | (uint32_t*)image->addr + y * image->stride, 117 | image->fourcc_format, width)) 118 | goto failure; 119 | 120 | dst->len += UDIV_UP(width, 8); 121 | } 122 | 123 | rc = 0; 124 | failure: 125 | nvnc_fb_unref(image); 126 | return rc; 127 | } 128 | -------------------------------------------------------------------------------- /src/damage-refinery.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 - 2021 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "fb.h" 25 | #include "pixels.h" 26 | #include "damage-refinery.h" 27 | 28 | #define XXH_STATIC_LINKING_ONLY 29 | #define XXH_IMPLEMENTATION 30 | #define XXH_VECTOR XXH_SCALAR 31 | #include "xxhash.h" 32 | 33 | #define UDIV_UP(a, b) (((a) + (b) - 1) / (b)) 34 | 35 | #define HASH_SEED 0 36 | 37 | int damage_refinery_init(struct damage_refinery* self, uint32_t width, 38 | uint32_t height) 39 | { 40 | self->width = width; 41 | self->height = height; 42 | 43 | uint32_t twidth = UDIV_UP(width, 32); 44 | uint32_t theight = UDIV_UP(height, 32); 45 | 46 | self->state = XXH3_createState(); 47 | if (!self->state) 48 | return -1; 49 | 50 | self->hashes = calloc(twidth * theight, sizeof(*self->hashes)); 51 | if (!self->hashes) { 52 | XXH3_freeState(self->state); 53 | return -1; 54 | } 55 | 56 | return 0; 57 | } 58 | 59 | int damage_refinery_resize(struct damage_refinery* self, uint32_t width, 60 | uint32_t height) 61 | { 62 | if (width == self->width && height == self->height) 63 | return 0; 64 | 65 | damage_refinery_destroy(self); 66 | return damage_refinery_init(self, width, height); 67 | } 68 | 69 | void damage_refinery_destroy(struct damage_refinery* self) 70 | { 71 | XXH3_freeState(self->state); 72 | free(self->hashes); 73 | } 74 | 75 | static uint32_t damage_hash_tile(struct damage_refinery* self, uint32_t tx, 76 | uint32_t ty, const struct nvnc_fb* buffer) 77 | { 78 | uint8_t* pixels = buffer->addr; 79 | int bpp = pixel_size_from_fourcc(buffer->fourcc_format); 80 | int byte_stride = buffer->stride * bpp; 81 | 82 | int x_start = tx * 32; 83 | int x_stop = MIN((tx + 1) * 32, self->width); 84 | int y_start = ty * 32; 85 | int y_stop = MIN((ty + 1) * 32, self->height); 86 | 87 | int32_t xoff = x_start * bpp; 88 | 89 | XXH3_64bits_reset(self->state); 90 | for (int y = y_start; y < y_stop; ++y) { 91 | XXH3_64bits_update(self->state, pixels + xoff + y * byte_stride, 92 | bpp * (x_stop - x_start)); 93 | } 94 | 95 | return XXH3_64bits_digest(self->state); 96 | } 97 | 98 | static uint32_t* damage_tile_hash_ptr(struct damage_refinery* self, 99 | uint32_t tx, uint32_t ty) 100 | { 101 | uint32_t twidth = UDIV_UP(self->width, 32); 102 | return &self->hashes[tx + ty * twidth]; 103 | } 104 | 105 | static void damage_refine_tile(struct damage_refinery* self, 106 | struct pixman_region16* refined, uint32_t tx, uint32_t ty, 107 | const struct nvnc_fb* buffer) 108 | { 109 | uint32_t hash = damage_hash_tile(self, tx, ty, buffer); 110 | uint32_t* old_hash_ptr = damage_tile_hash_ptr(self, tx, ty); 111 | int is_damaged = hash != *old_hash_ptr; 112 | *old_hash_ptr = hash; 113 | 114 | if (is_damaged) 115 | pixman_region_union_rect(refined, refined, tx * 32, ty * 32, 32, 116 | 32); 117 | } 118 | 119 | static void tile_region_from_region(struct pixman_region16* dst, 120 | struct pixman_region16* src) 121 | { 122 | int n_rects = 0; 123 | struct pixman_box16* rects = pixman_region_rectangles(src, &n_rects); 124 | 125 | for (int i = 0; i < n_rects; ++i) { 126 | int x1 = rects[i].x1 / 32; 127 | int y1 = rects[i].y1 / 32; 128 | int x2 = UDIV_UP(rects[i].x2, 32); 129 | int y2 = UDIV_UP(rects[i].y2, 32); 130 | 131 | pixman_region_union_rect(dst, dst, x1, y1, x2 - x1, y2 - y1); 132 | } 133 | } 134 | 135 | void damage_refine(struct damage_refinery* self, 136 | struct pixman_region16* refined, 137 | struct pixman_region16* hint, 138 | struct nvnc_fb* buffer) 139 | { 140 | assert(self->width == (uint32_t)buffer->width && 141 | self->height == (uint32_t)buffer->height); 142 | 143 | nvnc_fb_map(buffer); 144 | 145 | struct pixman_region16 tile_region; 146 | pixman_region_init(&tile_region); 147 | tile_region_from_region(&tile_region, hint); 148 | 149 | int n_rects = 0; 150 | struct pixman_box16* rects = pixman_region_rectangles(&tile_region, 151 | &n_rects); 152 | 153 | for (int i = 0; i < n_rects; ++i) 154 | for (int ty = rects[i].y1; ty < rects[i].y2; ++ty) 155 | for (int tx = rects[i].x1; tx < rects[i].x2; ++tx) 156 | damage_refine_tile(self, refined, tx, ty, buffer); 157 | 158 | pixman_region_fini(&tile_region); 159 | pixman_region_intersect_rect(refined, refined, 0, 0, self->width, 160 | self->height); 161 | } 162 | -------------------------------------------------------------------------------- /src/desktop-layout.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Philipp Zabel 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "desktop-layout.h" 18 | #include "neatvnc.h" 19 | #include "rfb-proto.h" 20 | 21 | #define EXPORT __attribute__((visibility("default"))) 22 | 23 | void nvnc_display_layout_init( 24 | struct nvnc_display_layout* display, struct rfb_screen* screen) 25 | { 26 | display->display = NULL; 27 | display->id = ntohl(screen->id); 28 | display->x_pos = ntohs(screen->x); 29 | display->y_pos = ntohs(screen->y); 30 | display->width = ntohs(screen->width); 31 | display->height = ntohs(screen->height); 32 | } 33 | 34 | EXPORT 35 | uint16_t nvnc_desktop_layout_get_width(const struct nvnc_desktop_layout* layout) 36 | { 37 | return layout->width; 38 | } 39 | 40 | EXPORT 41 | uint16_t nvnc_desktop_layout_get_height(const struct nvnc_desktop_layout* layout) 42 | { 43 | return layout->height; 44 | } 45 | 46 | EXPORT 47 | uint8_t nvnc_desktop_layout_get_display_count( 48 | const struct nvnc_desktop_layout* layout) 49 | { 50 | return layout->n_display_layouts; 51 | } 52 | 53 | EXPORT 54 | uint16_t nvnc_desktop_layout_get_display_x_pos( 55 | const struct nvnc_desktop_layout* layout, uint8_t display_index) 56 | { 57 | if (display_index >= layout->n_display_layouts) 58 | return 0; 59 | return layout->display_layouts[display_index].x_pos; 60 | } 61 | 62 | EXPORT 63 | uint16_t nvnc_desktop_layout_get_display_y_pos( 64 | const struct nvnc_desktop_layout* layout, uint8_t display_index) 65 | { 66 | if (display_index >= layout->n_display_layouts) 67 | return 0; 68 | return layout->display_layouts[display_index].y_pos; 69 | } 70 | 71 | EXPORT 72 | uint16_t nvnc_desktop_layout_get_display_width( 73 | const struct nvnc_desktop_layout* layout, uint8_t display_index) 74 | { 75 | if (display_index >= layout->n_display_layouts) 76 | return 0; 77 | return layout->display_layouts[display_index].width; 78 | } 79 | 80 | EXPORT 81 | uint16_t nvnc_desktop_layout_get_display_height( 82 | const struct nvnc_desktop_layout* layout, uint8_t display_index) 83 | { 84 | if (display_index >= layout->n_display_layouts) 85 | return 0; 86 | return layout->display_layouts[display_index].height; 87 | } 88 | 89 | EXPORT 90 | struct nvnc_display* nvnc_desktop_layout_get_display( 91 | const struct nvnc_desktop_layout* layout, uint8_t display_index) 92 | { 93 | if (display_index >= layout->n_display_layouts) 94 | return NULL; 95 | return layout->display_layouts[display_index].display; 96 | } 97 | -------------------------------------------------------------------------------- /src/display.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 - 2021 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "display.h" 18 | #include "neatvnc.h" 19 | #include "common.h" 20 | #include "fb.h" 21 | #include "resampler.h" 22 | #include "transform-util.h" 23 | #include "enc/encoder.h" 24 | #include "usdt.h" 25 | 26 | #include 27 | #include 28 | 29 | #define EXPORT __attribute__((visibility("default"))) 30 | 31 | static void nvnc_display__on_resampler_done(struct nvnc_fb* fb, 32 | struct pixman_region16* damage, void* userdata) 33 | { 34 | struct nvnc_display* self = userdata; 35 | 36 | DTRACE_PROBE2(neatvnc, nvnc_display__on_resampler_done, self, fb->pts); 37 | 38 | if (self->buffer) { 39 | nvnc_fb_release(self->buffer); 40 | nvnc_fb_unref(self->buffer); 41 | } 42 | 43 | self->buffer = fb; 44 | nvnc_fb_ref(fb); 45 | nvnc_fb_hold(fb); 46 | 47 | assert(self->server); 48 | 49 | // TODO: Shift according to display position 50 | nvnc__damage_region(self->server, damage); 51 | } 52 | 53 | EXPORT 54 | struct nvnc_display* nvnc_display_new(uint16_t x_pos, uint16_t y_pos) 55 | { 56 | struct nvnc_display* self = calloc(1, sizeof(*self)); 57 | if (!self) 58 | return NULL; 59 | 60 | self->resampler = resampler_create(); 61 | if (!self->resampler) 62 | goto resampler_failure; 63 | 64 | if (damage_refinery_init(&self->damage_refinery, 0, 0) < 0) 65 | goto refinery_failure; 66 | 67 | self->ref = 1; 68 | self->x_pos = x_pos; 69 | self->y_pos = y_pos; 70 | 71 | return self; 72 | 73 | refinery_failure: 74 | resampler_destroy(self->resampler); 75 | resampler_failure: 76 | free(self); 77 | 78 | return NULL; 79 | } 80 | 81 | static void nvnc__display_free(struct nvnc_display* self) 82 | { 83 | if (self->buffer) { 84 | nvnc_fb_release(self->buffer); 85 | nvnc_fb_unref(self->buffer); 86 | } 87 | damage_refinery_destroy(&self->damage_refinery); 88 | resampler_destroy(self->resampler); 89 | free(self); 90 | } 91 | 92 | EXPORT 93 | void nvnc_display_ref(struct nvnc_display* self) 94 | { 95 | self->ref++; 96 | } 97 | 98 | EXPORT 99 | void nvnc_display_unref(struct nvnc_display* self) 100 | { 101 | if (--self->ref == 0) 102 | nvnc__display_free(self); 103 | } 104 | 105 | EXPORT 106 | struct nvnc* nvnc_display_get_server(const struct nvnc_display* self) 107 | { 108 | return self->server; 109 | } 110 | 111 | EXPORT 112 | void nvnc_display_feed_buffer(struct nvnc_display* self, struct nvnc_fb* fb, 113 | struct pixman_region16* damage) 114 | { 115 | DTRACE_PROBE2(neatvnc, nvnc_display_feed_buffer, self, fb->pts); 116 | 117 | struct nvnc* server = self->server; 118 | assert(server); 119 | 120 | struct pixman_region16 refined_damage; 121 | pixman_region_init(&refined_damage); 122 | 123 | if (server->n_damage_clients != 0) { 124 | damage_refinery_resize(&self->damage_refinery, fb->width, 125 | fb->height); 126 | 127 | // TODO: Run the refinery in a worker thread? 128 | damage_refine(&self->damage_refinery, &refined_damage, damage, fb); 129 | damage = &refined_damage; 130 | } else { 131 | // Resizing to zero causes the damage refinery to be reset when 132 | // it's needed. 133 | damage_refinery_resize(&self->damage_refinery, 0, 0); 134 | } 135 | 136 | struct pixman_region16 transformed_damage; 137 | pixman_region_init(&transformed_damage); 138 | nvnc_transform_region(&transformed_damage, damage, fb->transform, 139 | fb->width, fb->height); 140 | 141 | resampler_feed(self->resampler, fb, &transformed_damage, 142 | nvnc_display__on_resampler_done, self); 143 | 144 | pixman_region_fini(&transformed_damage); 145 | pixman_region_fini(&refined_damage); 146 | } 147 | -------------------------------------------------------------------------------- /src/enc/h264/encoder.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "enc/h264-encoder.h" 18 | #include "config.h" 19 | 20 | #ifdef HAVE_FFMPEG 21 | extern struct h264_encoder_impl h264_encoder_ffmpeg_impl; 22 | #endif 23 | 24 | #ifdef HAVE_V4L2 25 | extern struct h264_encoder_impl h264_encoder_v4l2m2m_impl; 26 | #endif 27 | 28 | struct h264_encoder* h264_encoder_create(uint32_t width, uint32_t height, 29 | uint32_t format, int quality) 30 | { 31 | struct h264_encoder* encoder = NULL; 32 | 33 | #ifdef HAVE_V4L2 34 | encoder = h264_encoder_v4l2m2m_impl.create(width, height, format, quality); 35 | if (encoder) { 36 | return encoder; 37 | } 38 | #endif 39 | 40 | #ifdef HAVE_FFMPEG 41 | encoder = h264_encoder_ffmpeg_impl.create(width, height, format, quality); 42 | if (encoder) { 43 | return encoder; 44 | } 45 | #endif 46 | 47 | return encoder; 48 | } 49 | 50 | void h264_encoder_destroy(struct h264_encoder* self) 51 | { 52 | if (self) 53 | self->impl->destroy(self); 54 | } 55 | 56 | void h264_encoder_set_packet_handler_fn(struct h264_encoder* self, 57 | h264_encoder_packet_handler_fn fn) 58 | { 59 | self->on_packet_ready = fn; 60 | } 61 | 62 | void h264_encoder_set_userdata(struct h264_encoder* self, void* userdata) 63 | { 64 | self->userdata = userdata; 65 | } 66 | 67 | void h264_encoder_feed(struct h264_encoder* self, struct nvnc_fb* fb) 68 | { 69 | self->impl->feed(self, fb); 70 | } 71 | 72 | void h264_encoder_request_keyframe(struct h264_encoder* self) 73 | { 74 | self->next_frame_should_be_keyframe = true; 75 | } 76 | -------------------------------------------------------------------------------- /src/enc/h264/open-h264.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 - 2022 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "enc/h264-encoder.h" 18 | #include "rfb-proto.h" 19 | #include "enc/util.h" 20 | #include "vec.h" 21 | #include "fb.h" 22 | #include "rcbuf.h" 23 | #include "enc/encoder.h" 24 | #include "usdt.h" 25 | 26 | #include 27 | #include 28 | 29 | typedef void (*open_h264_ready_fn)(void*); 30 | 31 | struct open_h264_header { 32 | uint32_t length; 33 | uint32_t flags; 34 | } RFB_PACKED; 35 | 36 | struct open_h264 { 37 | struct encoder parent; 38 | 39 | struct h264_encoder* encoder; 40 | 41 | struct vec pending; 42 | uint64_t pts; 43 | 44 | uint32_t width; 45 | uint32_t height; 46 | uint32_t format; 47 | 48 | bool needs_reset; 49 | 50 | int quality; 51 | bool quality_changed; 52 | }; 53 | 54 | enum open_h264_flags { 55 | OPEN_H264_FLAG_RESET_CONTEXT = 0, 56 | OPEN_H264_FLAG_RESET_ALL_CONTEXTS = 1, 57 | }; 58 | 59 | struct encoder* open_h264_new(void); 60 | static struct encoded_frame* open_h264_pull(struct encoder* enc); 61 | 62 | struct encoder_impl encoder_impl_open_h264; 63 | 64 | static inline struct open_h264* open_h264(struct encoder* enc) 65 | { 66 | return (struct open_h264*)enc; 67 | } 68 | 69 | static void open_h264_handle_packet(const void* data, size_t size, uint64_t pts, 70 | void* userdata) 71 | { 72 | struct open_h264* self = userdata; 73 | 74 | // Let's not deplete the RAM if the client isn't pulling 75 | if (self->pending.len > 100000000) { 76 | // TODO: Drop buffer and request a keyframe? 77 | nvnc_log(NVNC_LOG_WARNING, "Pending buffer grew too large. Dropping packet..."); 78 | return; 79 | } 80 | 81 | vec_append(&self->pending, data, size); 82 | self->pts = pts; 83 | 84 | struct encoded_frame* result = open_h264_pull(&self->parent); 85 | 86 | DTRACE_PROBE1(neatvnc, open_h264_finish_frame, rpts); 87 | 88 | encoder_finish_frame(&self->parent, result); 89 | 90 | encoded_frame_unref(result); 91 | } 92 | 93 | static int open_h264_init_pending(struct open_h264* self) 94 | { 95 | if (vec_init(&self->pending, 4096) < 0) 96 | return -1; 97 | 98 | vec_append_zero(&self->pending, sizeof(struct rfb_server_fb_rect)); 99 | vec_append_zero(&self->pending, sizeof(struct open_h264_header)); 100 | 101 | return 0; 102 | } 103 | 104 | struct encoder* open_h264_new(void) 105 | { 106 | struct open_h264* self = calloc(1, sizeof(*self)); 107 | if (!self) 108 | return NULL; 109 | 110 | encoder_init(&self->parent, &encoder_impl_open_h264); 111 | 112 | if (open_h264_init_pending(self) < 0) { 113 | free(self); 114 | return NULL; 115 | } 116 | 117 | self->pts = NVNC_NO_PTS; 118 | self->quality = 6; 119 | 120 | return (struct encoder*)self; 121 | } 122 | 123 | static void open_h264_destroy(struct encoder* enc) 124 | { 125 | struct open_h264* self = open_h264(enc); 126 | 127 | if (self->encoder) 128 | h264_encoder_destroy(self->encoder); 129 | vec_destroy(&self->pending); 130 | 131 | free(self); 132 | } 133 | 134 | static int open_h264_resize(struct open_h264* self, struct nvnc_fb* fb) 135 | { 136 | int quality = 51 - round((50.0 / 9.0) * (float)self->quality); 137 | 138 | struct h264_encoder* encoder = h264_encoder_create(fb->width, 139 | fb->height, fb->fourcc_format, quality); 140 | if (!encoder) 141 | return -1; 142 | 143 | if (self->encoder) 144 | h264_encoder_destroy(self->encoder); 145 | 146 | h264_encoder_set_userdata(encoder, self); 147 | h264_encoder_set_packet_handler_fn(encoder, open_h264_handle_packet); 148 | 149 | self->encoder = encoder; 150 | 151 | self->width = fb->width; 152 | self->height = fb->height; 153 | self->format = fb->fourcc_format; 154 | self->needs_reset = true; 155 | self->quality_changed = false; 156 | 157 | return 0; 158 | } 159 | 160 | static int open_h264_encode(struct encoder* enc, struct nvnc_fb* fb, 161 | struct pixman_region16* damage) 162 | { 163 | DTRACE_PROBE1(neatvnc, open_h264_encode, fb->pts); 164 | 165 | struct open_h264* self = open_h264(enc); 166 | (void)damage; 167 | 168 | if (fb->width != self->width || fb->height != self->height || 169 | fb->fourcc_format != self->format || 170 | self->quality_changed) { 171 | if (open_h264_resize(self, fb) < 0) 172 | return -1; 173 | } 174 | 175 | assert(self->width && self->height); 176 | 177 | // TODO: encoder_feed should return an error code 178 | h264_encoder_feed(self->encoder, fb); 179 | return 0; 180 | } 181 | 182 | static struct encoded_frame* open_h264_pull(struct encoder* enc) 183 | { 184 | struct open_h264* self = open_h264(enc); 185 | 186 | size_t payload_size = self->pending.len 187 | - sizeof(struct rfb_server_fb_rect) 188 | - sizeof(struct open_h264_header); 189 | if (payload_size == 0) 190 | return NULL; 191 | 192 | uint64_t pts = self->pts; 193 | self->pts = NVNC_NO_PTS; 194 | 195 | uint32_t flags = self->needs_reset ? OPEN_H264_FLAG_RESET_CONTEXT : 0; 196 | self->needs_reset = false; 197 | 198 | struct rfb_server_fb_rect* rect = self->pending.data; 199 | rect->encoding = htonl(RFB_ENCODING_OPEN_H264); 200 | rect->width = htons(self->width); 201 | rect->height = htons(self->height); 202 | rect->x = htons(self->parent.x_pos); 203 | rect->y = htons(self->parent.y_pos); 204 | 205 | struct open_h264_header* header = 206 | (void*)(((uint8_t*)self->pending.data) + sizeof(*rect)); 207 | header->length = htonl(payload_size); 208 | header->flags = htonl(flags); 209 | 210 | int n_rects = 1; 211 | 212 | struct encoded_frame* payload; 213 | payload = encoded_frame_new(self->pending.data, self->pending.len, 214 | n_rects, self->width, self->height, pts); 215 | 216 | open_h264_init_pending(self); 217 | return payload; 218 | } 219 | 220 | static void open_h264_request_keyframe(struct encoder* enc) 221 | { 222 | struct open_h264* self = open_h264(enc); 223 | h264_encoder_request_keyframe(self->encoder); 224 | } 225 | 226 | static void open_h264_set_quality(struct encoder* enc, int value) 227 | { 228 | struct open_h264* self = open_h264(enc); 229 | if (value == 10) 230 | value = 6; 231 | self->quality_changed |= self->quality != value; 232 | self->quality = value; 233 | } 234 | 235 | struct encoder_impl encoder_impl_open_h264 = { 236 | .flags = ENCODER_IMPL_FLAG_IGNORES_DAMAGE, 237 | .destroy = open_h264_destroy, 238 | .encode = open_h264_encode, 239 | .request_key_frame = open_h264_request_keyframe, 240 | .set_quality = open_h264_set_quality, 241 | }; 242 | -------------------------------------------------------------------------------- /src/enc/interface.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 - 2022 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | #include "enc/encoder.h" 17 | #include "config.h" 18 | 19 | #include 20 | #include 21 | 22 | struct encoder* raw_encoder_new(void); 23 | struct encoder* zrle_encoder_new(void); 24 | struct encoder* tight_encoder_new(uint16_t width, uint16_t height); 25 | #ifdef ENABLE_OPEN_H264 26 | struct encoder* open_h264_new(void); 27 | #endif 28 | 29 | extern struct encoder_impl encoder_impl_raw; 30 | extern struct encoder_impl encoder_impl_zrle; 31 | extern struct encoder_impl encoder_impl_tight; 32 | #ifdef ENABLE_OPEN_H264 33 | extern struct encoder_impl encoder_impl_open_h264; 34 | #endif 35 | 36 | struct encoder* encoder_new(enum rfb_encodings type, uint16_t width, 37 | uint16_t height) 38 | { 39 | switch (type) { 40 | case RFB_ENCODING_RAW: return raw_encoder_new(); 41 | case RFB_ENCODING_ZRLE: return zrle_encoder_new(); 42 | case RFB_ENCODING_TIGHT: return tight_encoder_new(width, height); 43 | #ifdef ENABLE_OPEN_H264 44 | case RFB_ENCODING_OPEN_H264: return open_h264_new(); 45 | #endif 46 | default: break; 47 | } 48 | 49 | return NULL; 50 | } 51 | 52 | void encoder_init(struct encoder* self, struct encoder_impl* impl) 53 | { 54 | self->ref = 1; 55 | self->impl = impl; 56 | } 57 | 58 | enum rfb_encodings encoder_get_type(const struct encoder* self) 59 | { 60 | if (self->impl == &encoder_impl_raw) 61 | return RFB_ENCODING_RAW; 62 | if (self->impl == &encoder_impl_zrle) 63 | return RFB_ENCODING_ZRLE; 64 | if (self->impl == &encoder_impl_tight) 65 | return RFB_ENCODING_TIGHT; 66 | #ifdef ENABLE_OPEN_H264 67 | if (self->impl == &encoder_impl_open_h264) 68 | return RFB_ENCODING_OPEN_H264; 69 | #endif 70 | 71 | abort(); 72 | return 0; 73 | } 74 | 75 | void encoder_ref(struct encoder* self) 76 | { 77 | assert(self->ref > 0); 78 | self->ref++; 79 | } 80 | 81 | void encoder_unref(struct encoder* self) 82 | { 83 | if (!self) 84 | return; 85 | 86 | if (--self->ref != 0) 87 | return; 88 | 89 | if (self->impl->destroy) 90 | self->impl->destroy(self); 91 | } 92 | 93 | void encoder_set_output_format(struct encoder* self, 94 | const struct rfb_pixel_format* pixfmt) 95 | { 96 | if (self->impl->set_output_format) 97 | self->impl->set_output_format(self, pixfmt); 98 | } 99 | 100 | void encoder_set_quality(struct encoder* self, int value) 101 | { 102 | if (self->impl->set_quality) 103 | self->impl->set_quality(self, value); 104 | } 105 | 106 | int encoder_encode(struct encoder* self, struct nvnc_fb* fb, 107 | struct pixman_region16* damage) 108 | { 109 | assert(self->impl->encode); 110 | return self->impl->encode(self, fb, damage); 111 | } 112 | 113 | void encoder_request_key_frame(struct encoder* self) 114 | { 115 | if (self->impl->request_key_frame) 116 | return self->impl->request_key_frame(self); 117 | } 118 | 119 | void encoder_finish_frame(struct encoder* self, struct encoded_frame* result) 120 | { 121 | if (self->on_done) 122 | self->on_done(self, result); 123 | } 124 | -------------------------------------------------------------------------------- /src/enc/util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 - 2022 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "enc/encoder.h" 18 | #include "enc/util.h" 19 | #include "rfb-proto.h" 20 | #include "vec.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #define UDIV_UP(a, b) (((a) + (b) - 1) / (b)) 28 | 29 | int encode_rect_head(struct vec* dst, enum rfb_encodings encoding, 30 | uint32_t x, uint32_t y, uint32_t width, uint32_t height) 31 | { 32 | struct rfb_server_fb_rect head = { 33 | .encoding = htonl(encoding), 34 | .x = htons(x), 35 | .y = htons(y), 36 | .width = htons(width), 37 | .height = htons(height), 38 | }; 39 | 40 | return vec_append(dst, &head, sizeof(head)); 41 | } 42 | 43 | uint32_t calc_bytes_per_cpixel(const struct rfb_pixel_format* fmt) 44 | { 45 | return fmt->bits_per_pixel == 32 ? UDIV_UP(fmt->depth, 8) 46 | : UDIV_UP(fmt->bits_per_pixel, 8); 47 | } 48 | 49 | uint32_t calculate_region_area(struct pixman_region16* region) 50 | { 51 | uint32_t area = 0; 52 | 53 | int n_rects = 0; 54 | struct pixman_box16* rects = pixman_region_rectangles(region, 55 | &n_rects); 56 | 57 | for (int i = 0; i < n_rects; ++i) { 58 | int width = rects[i].x2 - rects[i].x1; 59 | int height = rects[i].y2 - rects[i].y1; 60 | area += width * height; 61 | } 62 | 63 | return area; 64 | } 65 | 66 | struct encoded_frame* encoded_frame_new(void* payload, size_t size, int n_rects, 67 | uint16_t width, uint16_t height, uint64_t pts) 68 | { 69 | struct encoded_frame* self = calloc(1, sizeof(*self)); 70 | if (!self) 71 | return NULL; 72 | 73 | self->buf.ref = 1; 74 | self->buf.size = size; 75 | self->buf.payload = payload; 76 | 77 | self->n_rects = n_rects; 78 | self->width = width; 79 | self->height = height; 80 | self->pts = pts; 81 | 82 | return self; 83 | } 84 | -------------------------------------------------------------------------------- /src/fb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 - 2022 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "fb.h" 18 | #include "pixels.h" 19 | #include "neatvnc.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "config.h" 27 | 28 | #ifdef HAVE_GBM 29 | #include 30 | #endif 31 | 32 | #define UDIV_UP(a, b) (((a) + (b) - 1) / (b)) 33 | #define ALIGN_UP(n, a) (UDIV_UP(n, a) * a) 34 | #define EXPORT __attribute__((visibility("default"))) 35 | 36 | EXPORT 37 | struct nvnc_fb* nvnc_fb_new(uint16_t width, uint16_t height, 38 | uint32_t fourcc_format, uint16_t stride) 39 | { 40 | struct nvnc_fb* fb = calloc(1, sizeof(*fb)); 41 | if (!fb) 42 | return NULL; 43 | 44 | uint32_t bpp = pixel_size_from_fourcc(fourcc_format); 45 | 46 | fb->type = NVNC_FB_SIMPLE; 47 | fb->ref = 1; 48 | fb->width = width; 49 | fb->height = height; 50 | fb->fourcc_format = fourcc_format; 51 | fb->stride = stride; 52 | fb->pts = NVNC_NO_PTS; 53 | 54 | size_t size = height * stride * bpp; 55 | size_t alignment = MAX(4, sizeof(void*)); 56 | size_t aligned_size = ALIGN_UP(size, alignment); 57 | 58 | fb->addr = aligned_alloc(alignment, aligned_size); 59 | if (!fb->addr) { 60 | free(fb); 61 | fb = NULL; 62 | } 63 | 64 | return fb; 65 | } 66 | 67 | EXPORT 68 | struct nvnc_fb* nvnc_fb_from_buffer(void* buffer, uint16_t width, uint16_t height, 69 | uint32_t fourcc_format, int32_t stride) 70 | { 71 | struct nvnc_fb* fb = calloc(1, sizeof(*fb)); 72 | if (!fb) 73 | return NULL; 74 | 75 | fb->type = NVNC_FB_SIMPLE; 76 | fb->ref = 1; 77 | fb->addr = buffer; 78 | fb->is_external = true; 79 | fb->width = width; 80 | fb->height = height; 81 | fb->fourcc_format = fourcc_format; 82 | fb->stride = stride; 83 | fb->pts = NVNC_NO_PTS; 84 | 85 | return fb; 86 | } 87 | 88 | EXPORT 89 | struct nvnc_fb* nvnc_fb_from_gbm_bo(struct gbm_bo* bo) 90 | { 91 | #ifdef HAVE_GBM 92 | struct nvnc_fb* fb = calloc(1, sizeof(*fb)); 93 | if (!fb) 94 | return NULL; 95 | 96 | fb->type = NVNC_FB_GBM_BO; 97 | fb->ref = 1; 98 | fb->is_external = true; 99 | fb->width = gbm_bo_get_width(bo); 100 | fb->height = gbm_bo_get_height(bo); 101 | fb->fourcc_format = gbm_bo_get_format(bo); 102 | fb->bo = bo; 103 | fb->pts = NVNC_NO_PTS; 104 | 105 | return fb; 106 | #else 107 | nvnc_log(NVNC_LOG_ERROR, "nvnc_fb_from_gbm_bo was not enabled during build time"); 108 | return NULL; 109 | #endif 110 | } 111 | 112 | EXPORT 113 | void* nvnc_fb_get_addr(const struct nvnc_fb* fb) 114 | { 115 | return fb->addr; 116 | } 117 | 118 | EXPORT 119 | uint16_t nvnc_fb_get_width(const struct nvnc_fb* fb) 120 | { 121 | return fb->width; 122 | } 123 | 124 | EXPORT 125 | uint16_t nvnc_fb_get_height(const struct nvnc_fb* fb) 126 | { 127 | return fb->height; 128 | } 129 | 130 | EXPORT 131 | uint32_t nvnc_fb_get_fourcc_format(const struct nvnc_fb* fb) 132 | { 133 | return fb->fourcc_format; 134 | } 135 | 136 | EXPORT 137 | int32_t nvnc_fb_get_stride(const struct nvnc_fb* fb) 138 | { 139 | return fb->stride; 140 | } 141 | 142 | EXPORT 143 | int nvnc_fb_get_pixel_size(const struct nvnc_fb* fb) 144 | { 145 | return pixel_size_from_fourcc(fb->fourcc_format); 146 | } 147 | 148 | EXPORT 149 | struct gbm_bo* nvnc_fb_get_gbm_bo(const struct nvnc_fb* fb) 150 | { 151 | return fb->bo; 152 | } 153 | 154 | EXPORT 155 | enum nvnc_transform nvnc_fb_get_transform(const struct nvnc_fb* fb) 156 | { 157 | return fb->transform; 158 | } 159 | 160 | EXPORT 161 | enum nvnc_fb_type nvnc_fb_get_type(const struct nvnc_fb* fb) 162 | { 163 | return fb->type; 164 | } 165 | 166 | EXPORT 167 | uint64_t nvnc_fb_get_pts(const struct nvnc_fb* fb) 168 | { 169 | return fb->pts; 170 | } 171 | 172 | static void nvnc__fb_free(struct nvnc_fb* fb) 173 | { 174 | nvnc_cleanup_fn cleanup = fb->common.cleanup_fn; 175 | if (cleanup) 176 | cleanup(fb->common.userdata); 177 | 178 | nvnc_fb_unmap(fb); 179 | 180 | if (!fb->is_external) 181 | switch (fb->type) { 182 | case NVNC_FB_UNSPEC: 183 | abort(); 184 | case NVNC_FB_SIMPLE: 185 | free(fb->addr); 186 | break; 187 | case NVNC_FB_GBM_BO: 188 | #ifdef HAVE_GBM 189 | gbm_bo_destroy(fb->bo); 190 | #else 191 | abort(); 192 | #endif 193 | break; 194 | } 195 | 196 | free(fb); 197 | } 198 | 199 | EXPORT 200 | void nvnc_fb_ref(struct nvnc_fb* fb) 201 | { 202 | fb->ref++; 203 | } 204 | 205 | EXPORT 206 | void nvnc_fb_unref(struct nvnc_fb* fb) 207 | { 208 | if (fb && --fb->ref == 0) 209 | nvnc__fb_free(fb); 210 | } 211 | 212 | EXPORT 213 | void nvnc_fb_set_release_fn(struct nvnc_fb* fb, nvnc_fb_release_fn fn, void* context) 214 | { 215 | fb->on_release = fn; 216 | fb->release_context = context; 217 | } 218 | 219 | EXPORT 220 | void nvnc_fb_set_transform(struct nvnc_fb* fb, enum nvnc_transform transform) 221 | { 222 | fb->transform = transform; 223 | } 224 | 225 | EXPORT 226 | void nvnc_fb_set_pts(struct nvnc_fb* fb, uint64_t pts) 227 | { 228 | fb->pts = pts; 229 | } 230 | 231 | void nvnc_fb_hold(struct nvnc_fb* fb) 232 | { 233 | fb->hold_count++; 234 | } 235 | 236 | void nvnc_fb_release(struct nvnc_fb* fb) 237 | { 238 | if (!fb) 239 | return; 240 | 241 | if (--fb->hold_count != 0) 242 | return; 243 | 244 | nvnc_fb_unmap(fb); 245 | fb->pts = NVNC_NO_PTS; 246 | 247 | if (fb->on_release) 248 | fb->on_release(fb, fb->release_context); 249 | } 250 | 251 | int nvnc_fb_map(struct nvnc_fb* fb) 252 | { 253 | #ifdef HAVE_GBM 254 | if (fb->type != NVNC_FB_GBM_BO || fb->bo_map_handle) 255 | return 0; 256 | 257 | uint32_t stride = 0; 258 | fb->addr = gbm_bo_map(fb->bo, 0, 0, fb->width, fb->height, 259 | GBM_BO_TRANSFER_READ, &stride, &fb->bo_map_handle); 260 | fb->stride = stride / nvnc_fb_get_pixel_size(fb); 261 | if (fb->addr) 262 | return 0; 263 | 264 | fb->bo_map_handle = NULL; 265 | return -1; 266 | #else 267 | return 0; 268 | #endif 269 | } 270 | 271 | void nvnc_fb_unmap(struct nvnc_fb* fb) 272 | { 273 | #ifdef HAVE_GBM 274 | if (fb->type != NVNC_FB_GBM_BO) 275 | return; 276 | 277 | if (fb->bo_map_handle) 278 | gbm_bo_unmap(fb->bo, fb->bo_map_handle); 279 | 280 | fb->bo_map_handle = NULL; 281 | fb->addr = NULL; 282 | fb->stride = 0; 283 | #endif 284 | } 285 | -------------------------------------------------------------------------------- /src/fb_pool.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "fb.h" 18 | #include "neatvnc.h" 19 | 20 | #include "sys/queue.h" 21 | 22 | #include 23 | #include 24 | 25 | #define EXPORT __attribute__((visibility("default"))) 26 | 27 | struct fbq_item { 28 | struct nvnc_fb* fb; 29 | TAILQ_ENTRY(fbq_item) link; 30 | }; 31 | 32 | TAILQ_HEAD(fbq, fbq_item); 33 | 34 | struct nvnc_fb_pool { 35 | int ref; 36 | 37 | struct fbq fbs; 38 | 39 | uint16_t width; 40 | uint16_t height; 41 | int32_t stride; 42 | uint32_t fourcc_format; 43 | 44 | nvnc_fb_alloc_fn alloc_fn; 45 | }; 46 | 47 | EXPORT 48 | struct nvnc_fb_pool* nvnc_fb_pool_new(uint16_t width, uint16_t height, 49 | uint32_t fourcc_format, uint16_t stride) 50 | { 51 | struct nvnc_fb_pool* self = calloc(1, sizeof(*self)); 52 | if (!self) 53 | return NULL; 54 | 55 | self->ref = 1; 56 | 57 | TAILQ_INIT(&self->fbs); 58 | self->width = width; 59 | self->height = height; 60 | self->stride = stride; 61 | self->fourcc_format = fourcc_format; 62 | self->alloc_fn = nvnc_fb_new; 63 | 64 | return self; 65 | } 66 | 67 | static void nvnc_fb_pool__destroy_fbs(struct nvnc_fb_pool* self) 68 | { 69 | while (!TAILQ_EMPTY(&self->fbs)) { 70 | struct fbq_item* item = TAILQ_FIRST(&self->fbs); 71 | TAILQ_REMOVE(&self->fbs, item, link); 72 | nvnc_fb_unref(item->fb); 73 | free(item); 74 | } 75 | } 76 | 77 | static void nvnc_fb_pool__destroy(struct nvnc_fb_pool* self) 78 | { 79 | nvnc_fb_pool__destroy_fbs(self); 80 | free(self); 81 | } 82 | 83 | EXPORT 84 | bool nvnc_fb_pool_resize(struct nvnc_fb_pool* self, uint16_t width, 85 | uint16_t height, uint32_t fourcc_format, uint16_t stride) 86 | { 87 | if (width == self->width && height == self->height && 88 | fourcc_format == self->fourcc_format && 89 | stride == self->stride) 90 | return false; 91 | 92 | nvnc_fb_pool__destroy_fbs(self); 93 | 94 | self->width = width; 95 | self->height = height; 96 | self->stride = stride; 97 | self->fourcc_format = fourcc_format; 98 | 99 | return true; 100 | } 101 | 102 | EXPORT 103 | void nvnc_fb_pool_ref(struct nvnc_fb_pool* self) 104 | { 105 | self->ref++; 106 | } 107 | 108 | EXPORT 109 | void nvnc_fb_pool_unref(struct nvnc_fb_pool* self) 110 | { 111 | if (--self->ref == 0) 112 | nvnc_fb_pool__destroy(self); 113 | } 114 | 115 | static void nvnc_fb_pool__on_fb_release(struct nvnc_fb* fb, void* userdata) 116 | { 117 | struct nvnc_fb_pool* pool = userdata; 118 | 119 | nvnc_fb_pool_release(pool, fb); 120 | nvnc_fb_pool_unref(pool); 121 | } 122 | 123 | static struct nvnc_fb* nvnc_fb_pool__acquire_new(struct nvnc_fb_pool* self) 124 | { 125 | struct nvnc_fb* fb = self->alloc_fn(self->width, self->height, 126 | self->fourcc_format, self->stride); 127 | if (!fb) 128 | return NULL; 129 | 130 | nvnc_fb_set_release_fn(fb, nvnc_fb_pool__on_fb_release, self); 131 | nvnc_fb_pool_ref(self); 132 | 133 | return fb; 134 | } 135 | 136 | static struct nvnc_fb* nvnc_fb_pool__acquire_from_list(struct nvnc_fb_pool* self) 137 | { 138 | struct fbq_item* item = TAILQ_FIRST(&self->fbs); 139 | struct nvnc_fb* fb = item->fb; 140 | assert(item && fb); 141 | 142 | TAILQ_REMOVE(&self->fbs, item, link); 143 | free(item); 144 | 145 | nvnc_fb_pool_ref(self); 146 | 147 | return fb; 148 | } 149 | 150 | EXPORT 151 | struct nvnc_fb* nvnc_fb_pool_acquire(struct nvnc_fb_pool* self) 152 | { 153 | return TAILQ_EMPTY(&self->fbs) ? 154 | nvnc_fb_pool__acquire_new(self) : 155 | nvnc_fb_pool__acquire_from_list(self); 156 | } 157 | 158 | EXPORT 159 | void nvnc_fb_pool_release(struct nvnc_fb_pool* self, struct nvnc_fb* fb) 160 | { 161 | if (fb->width != self->width || fb->height != self->height || 162 | fb->fourcc_format != self->fourcc_format || 163 | fb->stride != self->stride) { 164 | return; 165 | } 166 | 167 | nvnc_fb_ref(fb); 168 | 169 | struct fbq_item* item = calloc(1, sizeof(*item)); 170 | assert(item); 171 | item->fb = fb; 172 | TAILQ_INSERT_TAIL(&self->fbs, item, link); 173 | } 174 | 175 | EXPORT 176 | void nvnc_fb_pool_set_alloc_fn(struct nvnc_fb_pool* self, nvnc_fb_alloc_fn fn) 177 | { 178 | self->alloc_fn = fn; 179 | } 180 | -------------------------------------------------------------------------------- /src/logging.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 - 2022 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "neatvnc.h" 18 | #include "common.h" 19 | #include "logging.h" 20 | #include "config.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #ifdef __STDC_NO_THREADS__ 29 | #define thread_local _Thread_local 30 | #else 31 | #include 32 | #endif 33 | 34 | #ifdef HAVE_LIBAVUTIL 35 | #include 36 | #endif 37 | 38 | #define EXPORT __attribute__((visibility("default"))) 39 | 40 | static nvnc_log_fn log_fn = nvnc_default_logger; 41 | static thread_local nvnc_log_fn thread_local_log_fn = NULL; 42 | 43 | #ifndef NDEBUG 44 | static enum nvnc_log_level log_level = NVNC_LOG_DEBUG; 45 | #else 46 | static enum nvnc_log_level log_level = NVNC_LOG_WARNING; 47 | #endif 48 | 49 | static bool is_initialised = false; 50 | 51 | static nvnc_log_fn get_log_fn(void) 52 | { 53 | return thread_local_log_fn ? thread_local_log_fn : log_fn; 54 | } 55 | 56 | static char* trim_left(char* str) 57 | { 58 | while (isspace(*str)) 59 | ++str; 60 | return str; 61 | } 62 | 63 | static char* trim_right(char* str) 64 | { 65 | char* end = str + strlen(str) - 1; 66 | while (str < end && isspace(*end)) 67 | *end-- = '\0'; 68 | return str; 69 | } 70 | 71 | static inline char* trim(char* str) 72 | { 73 | return trim_right(trim_left(str)); 74 | } 75 | 76 | static const char* log_level_to_string(enum nvnc_log_level level) 77 | { 78 | switch (level) { 79 | case NVNC_LOG_PANIC: return "PANIC"; 80 | case NVNC_LOG_ERROR: return "ERROR"; 81 | case NVNC_LOG_WARNING: return "Warning"; 82 | case NVNC_LOG_INFO: return "Info"; 83 | case NVNC_LOG_DEBUG: return "DEBUG"; 84 | case NVNC_LOG_TRACE: return "TRACE"; 85 | } 86 | 87 | return "UNKNOWN"; 88 | } 89 | 90 | static FILE* stream_for_log_level(enum nvnc_log_level level) 91 | { 92 | switch (level) { 93 | case NVNC_LOG_PANIC: return stderr; 94 | case NVNC_LOG_ERROR: return stderr; 95 | case NVNC_LOG_WARNING: return stderr; 96 | case NVNC_LOG_INFO: return stdout; 97 | case NVNC_LOG_DEBUG: return stdout; 98 | case NVNC_LOG_TRACE: return stdout; 99 | } 100 | 101 | return stderr; 102 | } 103 | 104 | static void nvnc__vlog(const struct nvnc_log_data* meta, const char* fmt, 105 | va_list args) 106 | { 107 | char message[1024]; 108 | 109 | if (meta->level <= log_level) { 110 | vsnprintf(message, sizeof(message), fmt, args); 111 | get_log_fn()(meta, trim(message)); 112 | } 113 | 114 | if (meta->level == NVNC_LOG_PANIC) 115 | abort(); 116 | } 117 | 118 | EXPORT 119 | void nvnc_default_logger(const struct nvnc_log_data* meta, 120 | const char* message) 121 | { 122 | const char* level = log_level_to_string(meta->level); 123 | FILE* stream = stream_for_log_level(meta->level); 124 | 125 | if (meta->level == NVNC_LOG_INFO) 126 | fprintf(stream, "Info: %s\n", message); 127 | else 128 | fprintf(stream, "%s: %s: %d: %s\n", level, meta->file, 129 | meta->line, message); 130 | 131 | fflush(stream); 132 | } 133 | 134 | #ifdef HAVE_LIBAVUTIL 135 | static enum nvnc_log_level nvnc__log_level_from_av(int level) 136 | { 137 | switch (level) { 138 | case AV_LOG_PANIC: return NVNC_LOG_PANIC; 139 | case AV_LOG_FATAL: return NVNC_LOG_ERROR; 140 | case AV_LOG_ERROR: return NVNC_LOG_ERROR; 141 | case AV_LOG_WARNING: return NVNC_LOG_WARNING; 142 | case AV_LOG_INFO: return NVNC_LOG_INFO; 143 | case AV_LOG_VERBOSE: return NVNC_LOG_INFO; 144 | case AV_LOG_DEBUG: return NVNC_LOG_DEBUG; 145 | case AV_LOG_TRACE: return NVNC_LOG_TRACE; 146 | } 147 | 148 | return NVNC_LOG_TRACE; 149 | } 150 | 151 | static int nvnc__log_level_to_av(enum nvnc_log_level level) 152 | { 153 | switch (level) { 154 | case NVNC_LOG_PANIC: return AV_LOG_PANIC; 155 | case NVNC_LOG_ERROR: return AV_LOG_ERROR; 156 | case NVNC_LOG_WARNING: return AV_LOG_WARNING; 157 | case NVNC_LOG_INFO: return AV_LOG_INFO; 158 | case NVNC_LOG_DEBUG: return AV_LOG_DEBUG; 159 | case NVNC_LOG_TRACE: return AV_LOG_TRACE; 160 | } 161 | 162 | return AV_LOG_TRACE; 163 | } 164 | 165 | static void nvnc__av_log_callback(void* ptr, int level, const char* fmt, 166 | va_list va) 167 | { 168 | struct nvnc_log_data meta = { 169 | .level = nvnc__log_level_from_av(level), 170 | .file = "libav", 171 | .line = 0, 172 | }; 173 | nvnc__vlog(&meta, fmt, va); 174 | } 175 | #endif 176 | 177 | EXPORT 178 | void nvnc_set_log_level(enum nvnc_log_level level) 179 | { 180 | log_level = level; 181 | 182 | #ifdef HAVE_LIBAVUTIL 183 | av_log_set_level(nvnc__log_level_to_av(level)); 184 | #endif 185 | } 186 | 187 | EXPORT 188 | void nvnc_set_log_fn(nvnc_log_fn fn) 189 | { 190 | log_fn = fn ? fn : nvnc_default_logger; 191 | } 192 | 193 | EXPORT 194 | void nvnc_set_log_fn_thread_local(nvnc_log_fn fn) 195 | { 196 | thread_local_log_fn = fn; 197 | } 198 | 199 | EXPORT 200 | void nvnc__log(const struct nvnc_log_data* meta, 201 | const char* fmt, ...) 202 | { 203 | va_list ap; 204 | va_start(ap, fmt); 205 | nvnc__vlog(meta, fmt, ap); 206 | va_end(ap); 207 | } 208 | 209 | void nvnc__log_init(void) 210 | { 211 | if (is_initialised) 212 | return; 213 | 214 | #ifdef HAVE_LIBAVUTIL 215 | av_log_set_callback(nvnc__av_log_callback); 216 | #endif 217 | 218 | is_initialised = true; 219 | } 220 | -------------------------------------------------------------------------------- /src/pngfb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This is not a part of the library. It's only used in tests, examples and 3 | * benchmarks. 4 | */ 5 | 6 | #include "neatvnc.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | struct nvnc_fb* read_png_file(const char* filename); 16 | 17 | struct nvnc_fb* read_png_file(const char* filename) 18 | { 19 | int width, height; 20 | png_byte color_type; 21 | png_byte bit_depth; 22 | png_bytep* row_pointers = NULL; 23 | 24 | FILE* fp = fopen(filename, "rb"); 25 | 26 | png_structp png = 27 | png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 28 | if (!png) 29 | abort(); 30 | 31 | png_infop info = png_create_info_struct(png); 32 | if (!info) 33 | abort(); 34 | 35 | if (setjmp(png_jmpbuf(png))) 36 | abort(); 37 | 38 | png_init_io(png, fp); 39 | 40 | png_read_info(png, info); 41 | 42 | width = png_get_image_width(png, info); 43 | height = png_get_image_height(png, info); 44 | color_type = png_get_color_type(png, info); 45 | bit_depth = png_get_bit_depth(png, info); 46 | 47 | // Read any color_type into 8bit depth, RGBA format. 48 | // See http://www.libpng.org/pub/png/libpng-manual.txt 49 | 50 | if (bit_depth == 16) 51 | png_set_strip_16(png); 52 | 53 | if (color_type == PNG_COLOR_TYPE_PALETTE) 54 | png_set_palette_to_rgb(png); 55 | 56 | // PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16bit depth. 57 | if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) 58 | png_set_expand_gray_1_2_4_to_8(png); 59 | 60 | if (png_get_valid(png, info, PNG_INFO_tRNS)) 61 | png_set_tRNS_to_alpha(png); 62 | 63 | // These color_type don't have an alpha channel then fill it with 0xff. 64 | if (color_type == PNG_COLOR_TYPE_RGB || 65 | color_type == PNG_COLOR_TYPE_GRAY || 66 | color_type == PNG_COLOR_TYPE_PALETTE) 67 | png_set_filler(png, 0xFF, PNG_FILLER_AFTER); 68 | 69 | if (color_type == PNG_COLOR_TYPE_GRAY || 70 | color_type == PNG_COLOR_TYPE_GRAY_ALPHA) 71 | png_set_gray_to_rgb(png); 72 | 73 | png_read_update_info(png, info); 74 | 75 | size_t row_bytes = png_get_rowbytes(png, info); 76 | struct nvnc_fb* fb = nvnc_fb_new(width, height, DRM_FORMAT_ABGR8888, 77 | row_bytes / 4); 78 | assert(fb); 79 | 80 | uint8_t* addr = nvnc_fb_get_addr(fb); 81 | 82 | row_pointers = malloc(sizeof(png_bytep) * height); 83 | assert(row_pointers); 84 | 85 | for (int y = 0; y < height; ++y) 86 | row_pointers[y] = addr + y * row_bytes; 87 | 88 | png_read_image(png, row_pointers); 89 | 90 | free(row_pointers); 91 | fclose(fp); 92 | 93 | png_destroy_read_struct(&png, &info, NULL); 94 | 95 | return fb; 96 | } 97 | -------------------------------------------------------------------------------- /src/rcbuf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "rcbuf.h" 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | struct rcbuf* rcbuf_new(void* payload, size_t size) 24 | { 25 | struct rcbuf* self = calloc(1, sizeof(*self)); 26 | if (!self) 27 | return NULL; 28 | 29 | self->ref = 1; 30 | self->payload = payload; 31 | self->size = size; 32 | 33 | return self; 34 | } 35 | 36 | struct rcbuf* rcbuf_from_string(const char* str) 37 | { 38 | char* value = strdup(str); 39 | return value ? rcbuf_new(value, strlen(str)) : NULL; 40 | } 41 | 42 | struct rcbuf* rcbuf_from_mem(const void* addr, size_t size) 43 | { 44 | void* mem = malloc(size); 45 | if (!mem) 46 | return NULL; 47 | 48 | memcpy(mem, addr, size); 49 | 50 | struct rcbuf* rcbuf = rcbuf_new(mem, size); 51 | if (!rcbuf) 52 | free(mem); 53 | 54 | return rcbuf; 55 | } 56 | 57 | void rcbuf_ref(struct rcbuf* self) 58 | { 59 | self->ref++; 60 | } 61 | 62 | void rcbuf_unref(struct rcbuf* self) 63 | { 64 | assert(self->ref > 0); 65 | 66 | if (--self->ref > 0) 67 | return; 68 | 69 | free(self->payload); 70 | free(self); 71 | } 72 | -------------------------------------------------------------------------------- /src/resampler.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 - 2022 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "resampler.h" 18 | #include "neatvnc.h" 19 | #include "fb.h" 20 | #include "transform-util.h" 21 | #include "pixels.h" 22 | #include "usdt.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | struct fb_side_data { 31 | struct pixman_region16 buffer_damage; 32 | LIST_ENTRY(fb_side_data) link; 33 | }; 34 | 35 | LIST_HEAD(fb_side_data_list, fb_side_data); 36 | 37 | struct resampler { 38 | struct nvnc_fb_pool *pool; 39 | struct fb_side_data_list fb_side_data_list; 40 | }; 41 | 42 | struct resampler_work { 43 | struct pixman_region16 frame_damage; 44 | struct nvnc_fb* src; 45 | struct nvnc_fb* dst; 46 | resampler_fn on_done; 47 | void* userdata; 48 | }; 49 | 50 | static void fb_side_data_destroy(void* userdata) 51 | { 52 | struct fb_side_data* fb_side_data = userdata; 53 | LIST_REMOVE(fb_side_data, link); 54 | pixman_region_fini(&fb_side_data->buffer_damage); 55 | free(fb_side_data); 56 | } 57 | 58 | static void resampler_damage_all_buffers(struct resampler* self, 59 | struct pixman_region16* region) 60 | { 61 | struct fb_side_data *item; 62 | LIST_FOREACH(item, &self->fb_side_data_list, link) 63 | pixman_region_union(&item->buffer_damage, &item->buffer_damage, 64 | region); 65 | } 66 | 67 | static void resampler_work_free(void* userdata) 68 | { 69 | struct resampler_work* work = userdata; 70 | 71 | nvnc_fb_release(work->src); 72 | nvnc_fb_unref(work->src); 73 | 74 | nvnc_fb_unref(work->dst); 75 | 76 | pixman_region_fini(&work->frame_damage); 77 | 78 | free(work); 79 | } 80 | 81 | struct resampler* resampler_create(void) 82 | { 83 | struct resampler* self = calloc(1, sizeof(*self)); 84 | if (!self) 85 | return NULL; 86 | 87 | self->pool = nvnc_fb_pool_new(0, 0, DRM_FORMAT_INVALID, 0); 88 | if (!self->pool) { 89 | free(self); 90 | return NULL; 91 | } 92 | 93 | LIST_INIT(&self->fb_side_data_list); 94 | 95 | return self; 96 | } 97 | 98 | void resampler_destroy(struct resampler* self) 99 | { 100 | nvnc_fb_pool_unref(self->pool); 101 | free(self); 102 | } 103 | 104 | void resample_now(struct nvnc_fb* dst, struct nvnc_fb* src, 105 | struct pixman_region16* damage) 106 | { 107 | assert(dst->transform == NVNC_TRANSFORM_NORMAL); 108 | 109 | bool ok __attribute__((unused)); 110 | 111 | pixman_format_code_t dst_fmt = 0; 112 | ok = fourcc_to_pixman_fmt(&dst_fmt, dst->fourcc_format); 113 | assert(ok); 114 | 115 | pixman_image_t* dstimg = pixman_image_create_bits_no_clear( 116 | dst_fmt, dst->width, dst->height, dst->addr, 117 | nvnc_fb_get_pixel_size(dst) * dst->stride); 118 | 119 | pixman_format_code_t src_fmt = 0; 120 | ok = fourcc_to_pixman_fmt(&src_fmt, src->fourcc_format); 121 | assert(ok); 122 | 123 | pixman_image_t* srcimg = pixman_image_create_bits_no_clear( 124 | src_fmt, src->width, src->height, src->addr, 125 | nvnc_fb_get_pixel_size(src) * src->stride); 126 | 127 | pixman_transform_t pxform; 128 | nvnc_transform_to_pixman_transform(&pxform, src->transform, 129 | src->width, src->height); 130 | 131 | pixman_image_set_transform(srcimg, &pxform); 132 | 133 | /* Side data contains the union of the buffer damage and the frame 134 | * damage. 135 | */ 136 | if (damage) { 137 | pixman_image_set_clip_region(dstimg, damage); 138 | } 139 | 140 | pixman_image_composite(PIXMAN_OP_SRC, srcimg, NULL, dstimg, 141 | 0, 0, 142 | 0, 0, 143 | 0, 0, 144 | dst->width, dst->height); 145 | 146 | pixman_image_unref(srcimg); 147 | pixman_image_unref(dstimg); 148 | } 149 | 150 | static void do_work(struct aml_work* work) 151 | { 152 | struct resampler_work* ctx = aml_get_userdata(work); 153 | 154 | struct nvnc_fb* src = ctx->src; 155 | struct nvnc_fb* dst = ctx->dst; 156 | struct fb_side_data* dst_side_data = nvnc_get_userdata(dst); 157 | 158 | resample_now(dst, src, &dst_side_data->buffer_damage); 159 | } 160 | 161 | static void on_work_done(struct aml_work* work) 162 | { 163 | struct resampler_work* ctx = aml_get_userdata(work); 164 | ctx->on_done(ctx->dst, &ctx->frame_damage, ctx->userdata); 165 | } 166 | 167 | int resampler_feed(struct resampler* self, struct nvnc_fb* fb, 168 | struct pixman_region16* damage, resampler_fn on_done, 169 | void* userdata) 170 | { 171 | DTRACE_PROBE2(neatvnc, resampler_feed, self, fb->pts); 172 | 173 | if (fb->transform == NVNC_TRANSFORM_NORMAL) { 174 | on_done(fb, damage, userdata); 175 | return 0; 176 | } 177 | 178 | uint32_t width = fb->width; 179 | uint32_t height = fb->height; 180 | 181 | nvnc_transform_dimensions(fb->transform, &width, &height); 182 | nvnc_fb_pool_resize(self->pool, width, height, fb->fourcc_format, 183 | width); 184 | 185 | struct aml* aml = aml_get_default(); 186 | assert(aml); 187 | 188 | struct resampler_work* ctx = calloc(1, sizeof(*ctx)); 189 | if (!ctx) 190 | return -1; 191 | 192 | pixman_region_init(&ctx->frame_damage); 193 | pixman_region_copy(&ctx->frame_damage, damage); 194 | 195 | ctx->dst = nvnc_fb_pool_acquire(self->pool); 196 | if (!ctx->dst) 197 | goto acquire_failure; 198 | 199 | struct fb_side_data* fb_side_data = nvnc_get_userdata(fb); 200 | if (!fb_side_data) { 201 | fb_side_data = calloc(1, sizeof(*fb_side_data)); 202 | if (!fb_side_data) 203 | goto side_data_failure; 204 | 205 | /* This is a new buffer, so the whole surface is damaged. */ 206 | pixman_region_init_rect(&fb_side_data->buffer_damage, 0, 0, 207 | width, height); 208 | 209 | nvnc_set_userdata(fb, fb_side_data, fb_side_data_destroy); 210 | LIST_INSERT_HEAD(&self->fb_side_data_list, fb_side_data, link); 211 | } 212 | 213 | resampler_damage_all_buffers(self, damage); 214 | 215 | ctx->src = fb; 216 | nvnc_fb_ref(fb); 217 | nvnc_fb_hold(fb); 218 | 219 | ctx->on_done = on_done; 220 | ctx->userdata = userdata; 221 | 222 | struct aml_work* work = aml_work_new(do_work, on_work_done, ctx, 223 | resampler_work_free); 224 | if (!work) { 225 | resampler_work_free(ctx); 226 | return -1; 227 | } 228 | 229 | nvnc_fb_map(fb); 230 | 231 | int rc = aml_start(aml, work); 232 | aml_unref(work); 233 | return rc; 234 | 235 | side_data_failure: 236 | nvnc_fb_pool_release(self->pool, ctx->dst); 237 | acquire_failure: 238 | free(ctx); 239 | return -1; 240 | } 241 | -------------------------------------------------------------------------------- /src/stream/common.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 - 2025 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "stream/stream.h" 18 | #include "stream/common.h" 19 | 20 | #include 21 | 22 | void stream_init(struct stream* self) 23 | { 24 | self->ref = 1; 25 | } 26 | 27 | void stream_req__finish(struct stream_req* req, enum stream_req_status status) 28 | { 29 | if (req->on_done) 30 | req->on_done(req->userdata, status); 31 | 32 | // exec userdata is heap allocated 33 | if (req->exec && req->userdata) 34 | free(req->userdata); 35 | 36 | rcbuf_unref(req->payload); 37 | free(req); 38 | } 39 | 40 | void stream__remote_closed(struct stream* self) 41 | { 42 | stream_close(self); 43 | 44 | if (self->on_event) 45 | self->on_event(self, STREAM_EVENT_REMOTE_CLOSED); 46 | } 47 | -------------------------------------------------------------------------------- /src/stream/interface.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 - 2025 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "stream/stream.h" 18 | 19 | #include 20 | 21 | void stream_ref(struct stream* self) 22 | { 23 | self->ref++; 24 | } 25 | 26 | int stream_close(struct stream* self) 27 | { 28 | assert(self->impl && self->impl->close); 29 | return self->impl->close(self); 30 | } 31 | 32 | void stream_destroy(struct stream* self) 33 | { 34 | assert(self->impl && self->impl->destroy); 35 | if (--self->ref == 0) 36 | return self->impl->destroy(self); 37 | } 38 | 39 | int stream_send(struct stream* self, struct rcbuf* payload, 40 | stream_req_fn on_done, void* userdata) 41 | { 42 | assert(self->impl && self->impl->send); 43 | return self->impl->send(self, payload, on_done, userdata); 44 | } 45 | 46 | int stream_send_first(struct stream* self, struct rcbuf* payload) 47 | { 48 | assert(self->impl && self->impl->send); 49 | return self->impl->send_first(self, payload); 50 | } 51 | 52 | int stream_write(struct stream* self, const void* payload, size_t len, 53 | stream_req_fn on_done, void* userdata) 54 | { 55 | struct rcbuf* buf = rcbuf_from_mem(payload, len); 56 | return buf ? stream_send(self, buf, on_done, userdata) : -1; 57 | } 58 | 59 | ssize_t stream_read(struct stream* self, void* dst, size_t size) 60 | { 61 | assert(self->impl && self->impl->read); 62 | return self->impl->read(self, dst, size); 63 | } 64 | 65 | void stream_exec_and_send(struct stream* self, stream_exec_fn exec_fn, 66 | void* userdata) 67 | { 68 | assert(self->impl); 69 | if (self->impl->exec_and_send) 70 | self->impl->exec_and_send(self, exec_fn, userdata); 71 | else 72 | stream_send(self, exec_fn(self, userdata), NULL, NULL); 73 | } 74 | -------------------------------------------------------------------------------- /src/stream/rsa-aes.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "rcbuf.h" 26 | #include "stream/stream.h" 27 | #include "stream/tcp.h" 28 | #include "stream/common.h" 29 | #include "crypto.h" 30 | #include "neatvnc.h" 31 | 32 | #define RSA_AES_BUFFER_SIZE 8192 33 | #define UDIV_UP(a, b) (((a) + (b) - 1) / (b)) 34 | 35 | struct stream_rsa_aes { 36 | struct stream base; 37 | 38 | size_t read_index; 39 | uint8_t* read_buffer; 40 | 41 | struct crypto_cipher* cipher; 42 | }; 43 | 44 | static_assert(sizeof(struct stream_rsa_aes) <= STREAM_ALLOC_SIZE, 45 | "struct stream_rsa_aes has grown too large, increase STREAM_ALLOC_SIZE"); 46 | 47 | static void stream_rsa_aes_destroy(struct stream* base) 48 | { 49 | struct stream_rsa_aes* self = (struct stream_rsa_aes*)base; 50 | crypto_cipher_del(self->cipher); 51 | free(self->read_buffer); 52 | stream_tcp_destroy(base); 53 | } 54 | 55 | static void stream_rsa_aes_read_into_buffer(struct stream_rsa_aes* self) 56 | { 57 | ssize_t n_read = stream_tcp_read(&self->base, 58 | self->read_buffer + self->read_index, 59 | RSA_AES_BUFFER_SIZE - self->read_index); 60 | if (n_read > 0) 61 | self->read_index += n_read; 62 | } 63 | 64 | static ssize_t stream_rsa_aes_parse_header(struct stream_rsa_aes* self) 65 | { 66 | if (self->read_index <= 2) { 67 | return -1; 68 | } 69 | 70 | uint16_t len_be; 71 | memcpy(&len_be, self->read_buffer, sizeof(len_be)); 72 | size_t len = ntohs(len_be); 73 | 74 | if (self->read_index < 2 + 16 + len) { 75 | return -1; 76 | } 77 | 78 | return len; 79 | } 80 | 81 | static ssize_t stream_rsa_aes_read_message(struct stream_rsa_aes* self, 82 | uint8_t* dst, size_t size) 83 | { 84 | ssize_t msg_len = stream_rsa_aes_parse_header(self); 85 | if (msg_len < 0) { 86 | return 0; 87 | } 88 | 89 | // The entire message must fit in dst 90 | /* TODO: With this, stream_tcp__on_event won't run until network input 91 | * is received. We need to somehow schedule on_event or also buffer the 92 | * decrypted data here. 93 | * Another option would be to keep back the message counter in the 94 | * cipher until the message has been fully read. 95 | */ 96 | if ((size_t)msg_len > size) 97 | return 0; 98 | 99 | uint16_t msg_len_be = htons(msg_len); 100 | 101 | uint8_t expected_mac[16]; 102 | ssize_t n = crypto_cipher_decrypt(self->cipher, dst, expected_mac, 103 | self->read_buffer + 2, msg_len, 104 | (uint8_t*)&msg_len_be, sizeof(msg_len_be)); 105 | 106 | uint8_t* actual_mac = self->read_buffer + 2 + msg_len; 107 | if (memcmp(expected_mac, actual_mac, 16) != 0) { 108 | nvnc_log(NVNC_LOG_DEBUG, "Message authentication failed"); 109 | errno = EBADMSG; 110 | return -1; 111 | } 112 | 113 | self->read_index -= 2 + 16 + msg_len; 114 | memmove(self->read_buffer, self->read_buffer + 2 + 16 + msg_len, 115 | self->read_index); 116 | 117 | return n; 118 | } 119 | 120 | static ssize_t stream_rsa_aes_read(struct stream* base, void* dst, size_t size) 121 | { 122 | struct stream_rsa_aes* self = (struct stream_rsa_aes*)base; 123 | 124 | stream_rsa_aes_read_into_buffer(self); 125 | if (self->base.state == STREAM_STATE_CLOSED) 126 | return 0; 127 | 128 | size_t total_read = 0; 129 | 130 | while (true) { 131 | ssize_t n_read = stream_rsa_aes_read_message(self, dst, size); 132 | if (n_read == 0) 133 | break; 134 | 135 | if (n_read < 0) { 136 | if (errno == EAGAIN) { 137 | break; 138 | } 139 | return -1; 140 | } 141 | 142 | total_read += n_read; 143 | dst += n_read; 144 | size -= n_read; 145 | } 146 | 147 | return total_read; 148 | } 149 | 150 | static int stream_rsa_aes_send(struct stream* base, struct rcbuf* payload, 151 | stream_req_fn on_done, void* userdata) 152 | { 153 | struct stream_rsa_aes* self = (struct stream_rsa_aes*)base; 154 | size_t n_msg = UDIV_UP(payload->size, RSA_AES_BUFFER_SIZE); 155 | 156 | struct vec buf; 157 | vec_init(&buf, payload->size + n_msg * (2 + 16)); 158 | 159 | for (size_t i = 0; i < n_msg; ++i) { 160 | size_t msglen = MIN(payload->size - i * RSA_AES_BUFFER_SIZE, 161 | RSA_AES_BUFFER_SIZE); 162 | uint16_t msglen_be = htons(msglen); 163 | 164 | vec_append(&buf, &msglen_be, sizeof(msglen_be)); 165 | 166 | uint8_t mac[16]; 167 | crypto_cipher_encrypt(self->cipher, &buf, mac, 168 | payload->payload + i * RSA_AES_BUFFER_SIZE, 169 | msglen, (uint8_t*)&msglen_be, sizeof(msglen_be)); 170 | vec_append(&buf, mac, sizeof(mac)); 171 | } 172 | 173 | size_t payload_size = payload->size; 174 | rcbuf_unref(payload); 175 | 176 | int r = stream_tcp_send(base, rcbuf_new(buf.data, buf.len), on_done, 177 | userdata); 178 | if (r < 0) { 179 | return r; 180 | } 181 | 182 | return payload_size; 183 | } 184 | 185 | static struct stream_impl impl = { 186 | .close = stream_tcp_close, 187 | .destroy = stream_rsa_aes_destroy, 188 | .read = stream_rsa_aes_read, 189 | .send = stream_rsa_aes_send, 190 | }; 191 | 192 | int stream_upgrade_to_rsa_eas(struct stream* base, 193 | enum crypto_cipher_type cipher_type, 194 | const uint8_t* enc_key, const uint8_t* dec_key) 195 | { 196 | struct stream_rsa_aes* self = (struct stream_rsa_aes*)base; 197 | 198 | self->read_index = 0; 199 | self->read_buffer = malloc(RSA_AES_BUFFER_SIZE); 200 | if (!self->read_buffer) 201 | return -1; 202 | 203 | self->cipher = crypto_cipher_new(enc_key, dec_key, cipher_type); 204 | if (!self->cipher) { 205 | free(self->read_buffer); 206 | return -1; 207 | } 208 | 209 | self->base.impl = &impl; 210 | return 0; 211 | } 212 | -------------------------------------------------------------------------------- /src/stream/ws/framing.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "stream/websocket.h" 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | static inline uint64_t u64_from_network_order(uint64_t x) 27 | { 28 | #if __BYTE_ORDER__ == __BIG_ENDIAN__ 29 | return x; 30 | #else 31 | return __builtin_bswap64(x); 32 | #endif 33 | } 34 | 35 | static inline uint64_t u64_to_network_order(uint64_t x) 36 | { 37 | #if __BYTE_ORDER__ == __BIG_ENDIAN__ 38 | return x; 39 | #else 40 | return __builtin_bswap64(x); 41 | #endif 42 | } 43 | 44 | const char *ws_opcode_name(enum ws_opcode op) 45 | { 46 | switch (op) { 47 | case WS_OPCODE_CONT: return "cont"; 48 | case WS_OPCODE_TEXT: return "text"; 49 | case WS_OPCODE_BIN: return "bin"; 50 | case WS_OPCODE_CLOSE: return "close"; 51 | case WS_OPCODE_PING: return "ping"; 52 | case WS_OPCODE_PONG: return "pong"; 53 | } 54 | return "INVALID"; 55 | } 56 | 57 | bool ws_parse_frame_header(struct ws_frame_header* header, 58 | const uint8_t* payload, size_t length) 59 | { 60 | if (length < 2) 61 | return false; 62 | 63 | int i = 0; 64 | 65 | header->fin = !!(payload[i] & 0x80); 66 | header->opcode = (payload[i++] & 0x0f); 67 | header->mask = !!(payload[i] & 0x80); 68 | header->payload_length = payload[i++] & 0x7f; 69 | 70 | if (header->payload_length == 126) { 71 | if (length - i < 2) 72 | return false; 73 | 74 | uint16_t value = 0; 75 | memcpy(&value, &payload[i], 2); 76 | header->payload_length = ntohs(value); 77 | i += 2; 78 | } else if (header->payload_length == 127) { 79 | if (length - i < 8) 80 | return false; 81 | 82 | uint64_t value = 0; 83 | memcpy(&value, &payload[i], 8); 84 | header->payload_length = u64_from_network_order(value); 85 | i += 8; 86 | } 87 | 88 | if (header->mask) { 89 | if (length - i < 4) 90 | return false; 91 | 92 | memcpy(header->masking_key, &payload[i], 4); 93 | i += 4; 94 | } 95 | 96 | header->header_length = i; 97 | 98 | return true; 99 | } 100 | 101 | void ws_apply_mask(const struct ws_frame_header* header, 102 | uint8_t* restrict payload) 103 | { 104 | assert(header->mask); 105 | 106 | uint64_t len = header->payload_length; 107 | const uint8_t* restrict key = header->masking_key; 108 | 109 | for (uint64_t i = 0; i < len; ++i) { 110 | payload[i] ^= key[i % 4]; 111 | } 112 | } 113 | 114 | void ws_copy_payload(const struct ws_frame_header* header, 115 | uint8_t* restrict dst, const uint8_t* restrict src, size_t len) 116 | { 117 | if (!header->mask) { 118 | memcpy(dst, src, len); 119 | return; 120 | } 121 | 122 | const uint8_t* restrict key = header->masking_key; 123 | for (uint64_t i = 0; i < len; ++i) { 124 | dst[i] = src[i] ^ key[i % 4]; 125 | } 126 | } 127 | 128 | int ws_write_frame_header(uint8_t* dst, const struct ws_frame_header* header) 129 | { 130 | int i = 0; 131 | dst[i++] = ((uint8_t)header->fin << 7) | (header->opcode); 132 | 133 | if (header->payload_length <= 125) { 134 | dst[i++] = ((uint8_t)header->mask << 7) | header->payload_length; 135 | } else if (header->payload_length <= UINT16_MAX) { 136 | dst[i++] = ((uint8_t)header->mask << 7) | 126; 137 | uint16_t be = htons(header->payload_length); 138 | memcpy(&dst[i], &be, 2); 139 | i += 2; 140 | } else { 141 | dst[i++] = ((uint8_t)header->mask << 7) | 127; 142 | uint64_t be = u64_to_network_order(header->payload_length); 143 | memcpy(&dst[i], &be, 8); 144 | i += 8; 145 | } 146 | 147 | if (header->mask) { 148 | memcpy(dst, header->masking_key, 4); 149 | i += 4; 150 | } 151 | 152 | return i; 153 | } 154 | -------------------------------------------------------------------------------- /src/stream/ws/handshake.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "stream/websocket.h" 18 | #include "stream/http.h" 19 | #include "crypto.h" 20 | #include "base64.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | static const char magic_uuid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 29 | 30 | static void tolower_and_remove_ws(char* dst, const char* src) 31 | { 32 | while (*src) { 33 | if (!isspace(*src)) 34 | *dst++ = tolower(*src); 35 | src++; 36 | } 37 | *dst = '\0'; 38 | } 39 | 40 | // TODO: Do some more sanity checks on the input 41 | ssize_t ws_handshake(char* output, size_t output_maxlen, const char* input) 42 | { 43 | bool ok = false; 44 | struct http_req req = {}; 45 | if (http_req_parse(&req, input) < 0) 46 | return -1; 47 | 48 | char protocols[256] = ","; 49 | char versions[256] = ","; 50 | char tmpstring[256]; 51 | 52 | const char *challenge = NULL; 53 | for (size_t i = 0; i < req.field_index; ++i) { 54 | if (strcasecmp(req.field[i].key, "Sec-WebSocket-Key") == 0) { 55 | challenge = req.field[i].value; 56 | } 57 | if (strcasecmp(req.field[i].key, "Sec-WebSocket-Protocol") == 0) { 58 | snprintf(tmpstring, sizeof(tmpstring), "%s%s,", 59 | protocols, req.field[i].value); 60 | tolower_and_remove_ws(protocols, tmpstring); 61 | } 62 | if (strcasecmp(req.field[i].key, "Sec-WebSocket-Version") == 0) { 63 | snprintf(tmpstring, sizeof(tmpstring), "%s%s,", 64 | versions, req.field[i].value); 65 | tolower_and_remove_ws(versions, tmpstring); 66 | } 67 | } 68 | 69 | if (!challenge) 70 | goto failure; 71 | 72 | bool have_protocols = strlen(protocols) != 1; 73 | bool have_versions = strlen(versions) != 1; 74 | 75 | if (have_protocols && !strstr(protocols, ",binary,")) 76 | goto failure; 77 | 78 | if (have_versions && !strstr(versions, ",13,")) 79 | goto failure; 80 | 81 | uint8_t hash[20]; 82 | crypto_hash_many(hash, sizeof(hash), CRYPTO_HASH_SHA1, 83 | (struct crypto_data_entry[]){ 84 | { (uint8_t*)challenge, strlen(challenge) }, 85 | { (uint8_t*)magic_uuid, strlen(magic_uuid) }, 86 | {} 87 | }); 88 | 89 | char response[BASE64_ENCODED_SIZE(sizeof(hash))] = {}; 90 | base64_encode(response, hash, sizeof(hash)); 91 | 92 | size_t len = snprintf(output, output_maxlen, 93 | "HTTP/1.1 101 Switching Protocols\r\n" 94 | "Upgrade: websocket\r\n" 95 | "Connection: Upgrade\r\n" 96 | "Sec-WebSocket-Accept: %s\r\n" 97 | "%s%s" 98 | "\r\n", 99 | response, 100 | have_protocols ? "Sec-WebSocket-Protocol: binary\r\n" : "", 101 | have_versions ? "Sec-WebSocket-Version: 13\r\n" : ""); 102 | 103 | ssize_t header_len = req.header_length; 104 | ok = len < output_maxlen; 105 | failure: 106 | http_req_free(&req); 107 | return ok ? header_len : -1; 108 | } 109 | -------------------------------------------------------------------------------- /src/transform-util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 - 2021 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | * 16 | * For code borrowed from wlroots: 17 | * Copyright (c) 2017, 2018 Drew DeVault 18 | * Copyright (c) 2014 Jari Vetoniemi 19 | * 20 | * Permission is hereby granted, free of charge, to any person obtaining a copy 21 | * of this software and associated documentation files (the "Software"), to deal 22 | * in the Software without restriction, including without limitation the rights 23 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 24 | * copies of the Software, and to permit persons to whom the Software is 25 | * furnished to do so, subject to the following conditions: 26 | * 27 | * The above copyright notice and this permission notice shall be included in 28 | * all copies or substantial portions of the Software. 29 | * 30 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 31 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 32 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 33 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 34 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 35 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 36 | * SOFTWARE. 37 | */ 38 | 39 | #include "transform-util.h" 40 | #include "neatvnc.h" 41 | 42 | #include 43 | #include 44 | 45 | /* Note: This function yields the inverse pixman transform of the 46 | * nvnc_transform. 47 | */ 48 | void nvnc_transform_to_pixman_transform(pixman_transform_t* dst, 49 | enum nvnc_transform src, int width, int height) 50 | { 51 | #define F1 pixman_fixed_1 52 | switch (src) { 53 | case NVNC_TRANSFORM_NORMAL: 54 | { 55 | pixman_transform_t t = {{ 56 | { F1, 0, 0 }, 57 | { 0, F1, 0 }, 58 | { 0, 0, F1 }, 59 | }}; 60 | *dst = t; 61 | } 62 | return; 63 | case NVNC_TRANSFORM_90: 64 | { 65 | pixman_transform_t t = {{ 66 | { 0, F1, 0 }, 67 | { -F1, 0, height * F1 }, 68 | { 0, 0, F1 }, 69 | }}; 70 | *dst = t; 71 | } 72 | return; 73 | case NVNC_TRANSFORM_180: 74 | { 75 | pixman_transform_t t = {{ 76 | { -F1, 0, width * F1 }, 77 | { 0, -F1, height * F1 }, 78 | { 0, 0, F1 }, 79 | }}; 80 | *dst = t; 81 | } 82 | return; 83 | case NVNC_TRANSFORM_270: 84 | { 85 | pixman_transform_t t = {{ 86 | { 0, -F1, width * F1 }, 87 | { F1, 0, 0 }, 88 | { 0, 0, F1 }, 89 | }}; 90 | *dst = t; 91 | } 92 | return; 93 | case NVNC_TRANSFORM_FLIPPED: 94 | { 95 | pixman_transform_t t = {{ 96 | { -F1, 0, width * F1 }, 97 | { 0, F1, 0 }, 98 | { 0, 0, F1 }, 99 | }}; 100 | *dst = t; 101 | } 102 | return; 103 | case NVNC_TRANSFORM_FLIPPED_90: 104 | { 105 | pixman_transform_t t = {{ 106 | { 0, F1, 0 }, 107 | { F1, 0, 0 }, 108 | { 0, 0, F1 }, 109 | }}; 110 | *dst = t; 111 | } 112 | return; 113 | case NVNC_TRANSFORM_FLIPPED_180: 114 | { 115 | pixman_transform_t t = {{ 116 | { F1, 0, 0 }, 117 | { 0, -F1, height * F1 }, 118 | { 0, 0, F1 }, 119 | }}; 120 | *dst = t; 121 | } 122 | return; 123 | case NVNC_TRANSFORM_FLIPPED_270: 124 | { 125 | pixman_transform_t t = {{ 126 | { 0, -F1, width * F1 }, 127 | { -F1, 0, height * F1 }, 128 | { 0, 0, F1 }, 129 | }}; 130 | *dst = t; 131 | } 132 | return; 133 | } 134 | #undef F1 135 | 136 | abort(); 137 | } 138 | 139 | static bool is_transform_90_degrees(enum nvnc_transform transform) 140 | { 141 | switch (transform) { 142 | case NVNC_TRANSFORM_90: 143 | case NVNC_TRANSFORM_270: 144 | case NVNC_TRANSFORM_FLIPPED_90: 145 | case NVNC_TRANSFORM_FLIPPED_270: 146 | return true; 147 | default: 148 | break; 149 | } 150 | 151 | return false; 152 | } 153 | 154 | void nvnc_transform_dimensions(enum nvnc_transform transform, uint32_t* width, 155 | uint32_t* height) 156 | { 157 | if (is_transform_90_degrees(transform)) { 158 | uint32_t tmp = *width; 159 | *width = *height; 160 | *height = tmp; 161 | } 162 | } 163 | 164 | /* Borrowed this from wlroots */ 165 | void nvnc_transform_region(struct pixman_region16* dst, 166 | struct pixman_region16* src, enum nvnc_transform transform, 167 | int width, int height) 168 | { 169 | if (transform == NVNC_TRANSFORM_NORMAL) { 170 | pixman_region_copy(dst, src); 171 | return; 172 | } 173 | 174 | int nrects = 0; 175 | pixman_box16_t* src_rects = pixman_region_rectangles(src, &nrects); 176 | 177 | pixman_box16_t* dst_rects = malloc(nrects * sizeof(*dst_rects)); 178 | if (dst_rects == NULL) { 179 | return; 180 | } 181 | 182 | for (int i = 0; i < nrects; ++i) { 183 | switch (transform) { 184 | case NVNC_TRANSFORM_NORMAL: 185 | dst_rects[i].x1 = src_rects[i].x1; 186 | dst_rects[i].y1 = src_rects[i].y1; 187 | dst_rects[i].x2 = src_rects[i].x2; 188 | dst_rects[i].y2 = src_rects[i].y2; 189 | break; 190 | case NVNC_TRANSFORM_90: 191 | dst_rects[i].x1 = height - src_rects[i].y2; 192 | dst_rects[i].y1 = src_rects[i].x1; 193 | dst_rects[i].x2 = height - src_rects[i].y1; 194 | dst_rects[i].y2 = src_rects[i].x2; 195 | break; 196 | case NVNC_TRANSFORM_180: 197 | dst_rects[i].x1 = width - src_rects[i].x2; 198 | dst_rects[i].y1 = height - src_rects[i].y2; 199 | dst_rects[i].x2 = width - src_rects[i].x1; 200 | dst_rects[i].y2 = height - src_rects[i].y1; 201 | break; 202 | case NVNC_TRANSFORM_270: 203 | dst_rects[i].x1 = src_rects[i].y1; 204 | dst_rects[i].y1 = width - src_rects[i].x2; 205 | dst_rects[i].x2 = src_rects[i].y2; 206 | dst_rects[i].y2 = width - src_rects[i].x1; 207 | break; 208 | case NVNC_TRANSFORM_FLIPPED: 209 | dst_rects[i].x1 = width - src_rects[i].x2; 210 | dst_rects[i].y1 = src_rects[i].y1; 211 | dst_rects[i].x2 = width - src_rects[i].x1; 212 | dst_rects[i].y2 = src_rects[i].y2; 213 | break; 214 | case NVNC_TRANSFORM_FLIPPED_90: 215 | dst_rects[i].x1 = src_rects[i].y1; 216 | dst_rects[i].y1 = src_rects[i].x1; 217 | dst_rects[i].x2 = src_rects[i].y2; 218 | dst_rects[i].y2 = src_rects[i].x2; 219 | break; 220 | case NVNC_TRANSFORM_FLIPPED_180: 221 | dst_rects[i].x1 = src_rects[i].x1; 222 | dst_rects[i].y1 = height - src_rects[i].y2; 223 | dst_rects[i].x2 = src_rects[i].x2; 224 | dst_rects[i].y2 = height - src_rects[i].y1; 225 | break; 226 | case NVNC_TRANSFORM_FLIPPED_270: 227 | dst_rects[i].x1 = height - src_rects[i].y2; 228 | dst_rects[i].y1 = width - src_rects[i].x2; 229 | dst_rects[i].x2 = height - src_rects[i].y1; 230 | dst_rects[i].y2 = width - src_rects[i].x1; 231 | break; 232 | } 233 | } 234 | 235 | pixman_region_fini(dst); 236 | pixman_region_init_rects(dst, dst_rects, nrects); 237 | free(dst_rects); 238 | } 239 | -------------------------------------------------------------------------------- /src/vec.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Andri Yngvason 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "vec.h" 18 | #include "likely.h" 19 | 20 | #include 21 | #include 22 | 23 | int vec_init(struct vec* vec, size_t cap) 24 | { 25 | memset(vec, 0, sizeof(*vec)); 26 | return vec_reserve(vec, cap); 27 | } 28 | 29 | void vec_destroy(struct vec* vec) 30 | { 31 | free(vec->data); 32 | } 33 | 34 | int vec_reserve(struct vec* vec, size_t size) 35 | { 36 | if (likely(size <= vec->cap)) 37 | return 0; 38 | 39 | void* data = realloc(vec->data, size); 40 | if (unlikely(!data)) 41 | return -1; 42 | 43 | vec->cap = size; 44 | vec->data = data; 45 | 46 | return 0; 47 | } 48 | 49 | static int vec__grow(struct vec* vec, size_t size) 50 | { 51 | if (likely(vec->len + size < vec->cap)) 52 | return 0; 53 | 54 | return vec_reserve(vec, 2 * (vec->len + size)); 55 | } 56 | 57 | int vec_assign(struct vec* vec, const void* data, size_t size) 58 | { 59 | vec->len = 0; 60 | 61 | if (unlikely(vec_reserve(vec, size) < 0)) 62 | return -1; 63 | 64 | vec->len = size; 65 | memcpy(vec->data, data, size); 66 | 67 | return 0; 68 | } 69 | 70 | int vec_append(struct vec* vec, const void* data, size_t size) 71 | { 72 | if (unlikely(vec__grow(vec, size) < 0)) 73 | return -1; 74 | 75 | char* p = vec->data; 76 | memcpy(&p[vec->len], data, size); 77 | vec->len += size; 78 | 79 | return 0; 80 | } 81 | 82 | void* vec_append_zero(struct vec* vec, size_t size) 83 | { 84 | if (unlikely(vec__grow(vec, size) < 0)) 85 | return NULL; 86 | 87 | char* p = vec->data; 88 | void* r = &p[vec->len]; 89 | memset(r, 0, size); 90 | vec->len += size; 91 | 92 | return r; 93 | } 94 | 95 | void vec_bzero(struct vec* vec) 96 | { 97 | memset(vec->data, 0, vec->cap); 98 | } 99 | -------------------------------------------------------------------------------- /test-images/mandrill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/any1/neatvnc/62912487621b402ca5a253fa4df7ddde0e8f66cd/test-images/mandrill.png -------------------------------------------------------------------------------- /test-images/manifest: -------------------------------------------------------------------------------- 1 | tv-test-card.png: https://commons.wikimedia.org/wiki/File:PM5544_with_non-PAL_signals.png 2 | mandrill.png: https://sipi.usc.edu/database/database.php?volume=misc&image=10#top 3 | -------------------------------------------------------------------------------- /test-images/tv-test-card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/any1/neatvnc/62912487621b402ca5a253fa4df7ddde0e8f66cd/test-images/tv-test-card.png -------------------------------------------------------------------------------- /test-zrle.c: -------------------------------------------------------------------------------- 1 | #include "zrle.h" 2 | #include "rfb-proto.h" 3 | 4 | #include 5 | #include 6 | 7 | int test_pixel32_to_cpixel_3(void) 8 | { 9 | static uint32_t src[64] = { 0 }; 10 | static uint8_t dst[64 * 3] = { 0 }; 11 | 12 | struct rfb_pixel_format srcfmt = { 0 }, dstfmt = { 0 }; 13 | 14 | srcfmt.depth = 24; 15 | srcfmt.bits_per_pixel = 32; 16 | srcfmt.true_colour_flag = 1; 17 | srcfmt.red_max = 255; 18 | srcfmt.green_max = 255; 19 | srcfmt.blue_max = 255; 20 | srcfmt.red_shift = 24; 21 | srcfmt.green_shift = 16; 22 | srcfmt.blue_shift = 8; 23 | 24 | dstfmt.depth = 24; 25 | dstfmt.bits_per_pixel = 32; 26 | dstfmt.true_colour_flag = 1; 27 | dstfmt.red_max = 255; 28 | dstfmt.green_max = 255; 29 | dstfmt.blue_max = 255; 30 | dstfmt.red_shift = 8; 31 | dstfmt.green_shift = 16; 32 | dstfmt.blue_shift = 24; 33 | 34 | for (int i = 0; i < 64; ++i) 35 | src[i] = 0x12345678; 36 | 37 | pixel32_to_cpixel(dst, &dstfmt, src, &srcfmt, 3, 64); 38 | 39 | for (int i = 0; i < 64; ++i) { 40 | uint8_t r, g, b; 41 | 42 | r = dst[i * 3 + 0]; 43 | g = dst[i * 3 + 1]; 44 | b = dst[i * 3 + 2]; 45 | 46 | if (r != 0x12 || g != 0x34 || b != 0x56) { 47 | printf("pixel32_to_cpixel_3 failed\n"); 48 | return 1; 49 | } 50 | } 51 | 52 | printf("pixel32_to_cpixel_3 ran OK\n"); 53 | return 0; 54 | } 55 | 56 | int test_pixel32_to_cpixel_2(void) 57 | { 58 | static uint32_t src[64] = { 0 }; 59 | static uint16_t dst[64] = { 0 }; 60 | 61 | struct rfb_pixel_format srcfmt = { 0 }, dstfmt = { 0 }; 62 | 63 | srcfmt.depth = 24; 64 | srcfmt.bits_per_pixel = 32; 65 | srcfmt.true_colour_flag = 1; 66 | srcfmt.red_max = 255; 67 | srcfmt.green_max = 255; 68 | srcfmt.blue_max = 255; 69 | srcfmt.red_shift = 24; 70 | srcfmt.green_shift = 16; 71 | srcfmt.blue_shift = 8; 72 | 73 | dstfmt.depth = 16; 74 | dstfmt.bits_per_pixel = 16; 75 | dstfmt.true_colour_flag = 1; 76 | dstfmt.red_max = 31; 77 | dstfmt.green_max = 63; 78 | dstfmt.blue_max = 31; 79 | dstfmt.red_shift = 11; 80 | dstfmt.green_shift = 5; 81 | dstfmt.blue_shift = 0; 82 | 83 | #if __BYTE_ORDER__ == __BIG_ENDIAN__ 84 | dstfmt.big_endian_flag = 1; 85 | #endif 86 | 87 | for (int i = 0; i < 64; ++i) 88 | src[i] = 0x12345678; 89 | 90 | pixel32_to_cpixel((uint8_t*)dst, &dstfmt, src, &srcfmt, 2, 64); 91 | 92 | for (int i = 0; i < 64; ++i) { 93 | uint8_t r, g, b; 94 | 95 | r = (dst[i] >> 11) & 31; 96 | g = (dst[i] >> 5) & 63; 97 | b = (dst[i] >> 0) & 31; 98 | 99 | if (r != (0x12 >> 3) || g != (0x34 >> 2) || b != (0x56 >> 3)) { 100 | printf("pixel32_to_cpixel_2 failed: %x %x %x\n", r, g, b); 101 | return 1; 102 | } 103 | } 104 | 105 | printf("pixel32_to_cpixel_2 ran OK\n"); 106 | return 0; 107 | } 108 | 109 | int test_pixel32_to_cpixel_1(void) 110 | { 111 | static uint32_t src[64] = { 0 }; 112 | static uint8_t dst[64] = { 0 }; 113 | 114 | struct rfb_pixel_format srcfmt = { 0 }, dstfmt = { 0 }; 115 | 116 | srcfmt.depth = 24; 117 | srcfmt.bits_per_pixel = 32; 118 | srcfmt.true_colour_flag = 1; 119 | srcfmt.red_max = 255; 120 | srcfmt.green_max = 255; 121 | srcfmt.blue_max = 255; 122 | srcfmt.red_shift = 24; 123 | srcfmt.green_shift = 16; 124 | srcfmt.blue_shift = 8; 125 | 126 | dstfmt.depth = 8; 127 | dstfmt.bits_per_pixel = 8; 128 | dstfmt.true_colour_flag = 1; 129 | dstfmt.red_max = 7; 130 | dstfmt.green_max = 7; 131 | dstfmt.blue_max = 3; 132 | dstfmt.red_shift = 5; 133 | dstfmt.green_shift = 2; 134 | dstfmt.blue_shift = 0; 135 | 136 | #if __BYTE_ORDER__ == __BIG_ENDIAN__ 137 | dstfmt.big_endian_flag = 1; 138 | #endif 139 | 140 | for (int i = 0; i < 64; ++i) 141 | src[i] = 0x98765432; 142 | 143 | pixel32_to_cpixel((uint8_t*)dst, &dstfmt, src, &srcfmt, 1, 64); 144 | 145 | for (int i = 0; i < 64; ++i) { 146 | uint8_t r, g, b; 147 | 148 | r = (dst[i] >> 5) & 7; 149 | g = (dst[i] >> 2) & 7; 150 | b = (dst[i] >> 0) & 3; 151 | 152 | if (r != (0x98 >> 5) || g != (0x76 >> 5) || b != (0x54 >> 6)) { 153 | printf("pixel32_to_cpixel_1 failed: %x %x %x\n", r, g, b); 154 | return 1; 155 | } 156 | } 157 | 158 | printf("pixel32_to_cpixel_1 ran OK\n"); 159 | return 0; 160 | } 161 | int main() 162 | { 163 | if (test_pixel32_to_cpixel_3()) return 1; 164 | if (test_pixel32_to_cpixel_2()) return 1; 165 | if (test_pixel32_to_cpixel_1()) return 1; 166 | 167 | return 0; 168 | } 169 | -------------------------------------------------------------------------------- /test/meson.build: -------------------------------------------------------------------------------- 1 | pixels = executable('pixels', 2 | [ 3 | 'test-pixels.c', 4 | '../src/pixels.c', 5 | ], 6 | include_directories: inc, 7 | dependencies: [ 8 | pixman, 9 | libdrm_inc, 10 | libm, 11 | ], 12 | ) 13 | test('pixels', pixels) 14 | 15 | base64 = executable('base64', 16 | [ 17 | 'test-base64.c', 18 | '../src/base64.c', 19 | ], 20 | include_directories: inc, 21 | ) 22 | test('base64', base64) 23 | -------------------------------------------------------------------------------- /test/test-base64.c: -------------------------------------------------------------------------------- 1 | #include "base64.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | static bool test_encode_0(void) 8 | { 9 | char buf[1] = {}; 10 | base64_encode(buf, (const uint8_t*)"", 0); 11 | return strlen(buf) == 0; 12 | } 13 | 14 | static bool test_encode_1(void) 15 | { 16 | static const char input[] = "a"; 17 | char buf[BASE64_ENCODED_SIZE(sizeof(input) - 1)] = {}; 18 | base64_encode(buf, (const uint8_t*)input, sizeof(input) - 1); 19 | return strcmp(buf, "YQ==") == 0; 20 | } 21 | 22 | static bool test_encode_2(void) 23 | { 24 | static const char input[] = "ab"; 25 | char buf[BASE64_ENCODED_SIZE(sizeof(input) - 1)] = {}; 26 | base64_encode(buf, (const uint8_t*)input, sizeof(input) - 1); 27 | return strcmp(buf, "YWI=") == 0; 28 | } 29 | 30 | static bool test_encode_3(void) 31 | { 32 | static const char input[] = "abc"; 33 | char buf[BASE64_ENCODED_SIZE(sizeof(input) - 1)] = {}; 34 | base64_encode(buf, (const uint8_t*)input, sizeof(input) - 1); 35 | return strcmp(buf, "YWJj") == 0; 36 | } 37 | 38 | static bool test_encode_4(void) 39 | { 40 | static const char input[] = "abcd"; 41 | char buf[BASE64_ENCODED_SIZE(sizeof(input) - 1)] = {}; 42 | base64_encode(buf, (const uint8_t*)input, sizeof(input) - 1); 43 | return strcmp(buf, "YWJjZA==") == 0; 44 | } 45 | 46 | static bool test_encode_5(void) 47 | { 48 | static const char input[] = "abcde"; 49 | char buf[BASE64_ENCODED_SIZE(sizeof(input) - 1)] = {}; 50 | base64_encode(buf, (const uint8_t*)input, sizeof(input) - 1); 51 | return strcmp(buf, "YWJjZGU=") == 0; 52 | } 53 | 54 | static bool test_decode_0(void) 55 | { 56 | uint8_t buf[1] = {}; 57 | ssize_t r = base64_decode(buf, ""); 58 | return r == 0 && buf[0] == 0; 59 | } 60 | 61 | static bool test_decode_1(void) 62 | { 63 | static const char input[] = "YQ=="; 64 | uint8_t buf[BASE64_DECODED_MAX_SIZE(sizeof(input) - 1)] = {}; 65 | ssize_t r = base64_decode(buf, input); 66 | return r == 1 && memcmp(buf, "a", r) == 0; 67 | } 68 | 69 | static bool test_decode_2(void) 70 | { 71 | static const char input[] = "YWI="; 72 | uint8_t buf[BASE64_DECODED_MAX_SIZE(sizeof(input) - 1)] = {}; 73 | ssize_t r = base64_decode(buf, input); 74 | return r == 2 && memcmp(buf, "ab", r) == 0; 75 | } 76 | 77 | static bool test_decode_3(void) 78 | { 79 | static const char input[] = "YWJj"; 80 | uint8_t buf[BASE64_DECODED_MAX_SIZE(sizeof(input) - 1)] = {}; 81 | ssize_t r = base64_decode(buf, input); 82 | return r == 3 && memcmp(buf, "abc", r) == 0; 83 | } 84 | 85 | static bool test_decode_4(void) 86 | { 87 | static const char input[] = "YWJjZA=="; 88 | uint8_t buf[BASE64_DECODED_MAX_SIZE(sizeof(input) - 1)] = {}; 89 | ssize_t r = base64_decode(buf, input); 90 | return r == 4 && memcmp(buf, "abcd", r) == 0; 91 | } 92 | 93 | static bool test_decode_5(void) 94 | { 95 | static const char input[] = "YWJjZGU="; 96 | uint8_t buf[BASE64_DECODED_MAX_SIZE(sizeof(input) - 1)] = {}; 97 | ssize_t r = base64_decode(buf, input); 98 | return r == 5 && memcmp(buf, "abcde", r) == 0; 99 | } 100 | 101 | #define XSTR(s) STR(s) 102 | #define STR(s) #s 103 | 104 | #define RUN_TEST(name) ({ \ 105 | bool ok = test_ ## name(); \ 106 | printf("[%s] %s\n", ok ? " OK " : "FAIL", XSTR(name)); \ 107 | ok; \ 108 | }) 109 | 110 | int main() 111 | { 112 | bool ok = true; 113 | 114 | ok &= RUN_TEST(encode_0); 115 | ok &= RUN_TEST(encode_1); 116 | ok &= RUN_TEST(encode_2); 117 | ok &= RUN_TEST(encode_3); 118 | ok &= RUN_TEST(encode_4); 119 | ok &= RUN_TEST(encode_5); 120 | 121 | ok &= RUN_TEST(decode_0); 122 | ok &= RUN_TEST(decode_1); 123 | ok &= RUN_TEST(decode_2); 124 | ok &= RUN_TEST(decode_3); 125 | ok &= RUN_TEST(decode_4); 126 | ok &= RUN_TEST(decode_5); 127 | 128 | return ok ? 0 : 1; 129 | } 130 | -------------------------------------------------------------------------------- /test/test-pixels.c: -------------------------------------------------------------------------------- 1 | #include "pixels.h" 2 | #include "rfb-proto.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define swap32(x) (((x >> 24) & 0xff) | ((x << 8) & 0xff0000) | \ 11 | ((x >> 8) & 0xff00) | ((x << 24) & 0xff000000)) 12 | 13 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 14 | #define u32_le(x) x 15 | #else 16 | #define u32_le(x) swap32(x) 17 | #endif 18 | 19 | #define XSTR(s) STR(s) 20 | #define STR(s) #s 21 | 22 | #define UDIV_UP(a, b) (((a) + (b) - 1) / (b)) 23 | #define ARRAY_LEN(a) (sizeof(a) / (sizeof(a[0]))) 24 | 25 | static bool test_pixel_to_cpixel_4bpp(void) 26 | { 27 | uint32_t src = u32_le(0x11223344u); 28 | uint32_t dst; 29 | uint8_t* src_addr = (uint8_t*)&src; 30 | 31 | struct rfb_pixel_format dstfmt = { 0 }, srcfmt = { 0 }; 32 | 33 | rfb_pixfmt_from_fourcc(&dstfmt, DRM_FORMAT_RGBA8888); 34 | 35 | dst = 0; 36 | rfb_pixfmt_from_fourcc(&srcfmt, DRM_FORMAT_RGBA8888); 37 | pixel_to_cpixel((uint8_t*)&dst, &dstfmt, src_addr, &srcfmt, 4, 1); 38 | if ((src & 0xffffff00u) != (dst & 0xffffff00u)) 39 | return false; 40 | 41 | dst = 0; 42 | rfb_pixfmt_from_fourcc(&dstfmt, DRM_FORMAT_ABGR8888); 43 | pixel_to_cpixel((uint8_t*)&dst, &dstfmt, src_addr, &srcfmt, 4, 1); 44 | if (dst != u32_le(0x00332211u)) 45 | return false; 46 | 47 | dst = 0; 48 | rfb_pixfmt_from_fourcc(&dstfmt, DRM_FORMAT_ARGB8888); 49 | pixel_to_cpixel((uint8_t*)&dst, &dstfmt, src_addr, &srcfmt, 4, 1); 50 | if (dst != u32_le(0x00112233u)) 51 | return false; 52 | 53 | dst = 0; 54 | rfb_pixfmt_from_fourcc(&dstfmt, DRM_FORMAT_BGRA8888); 55 | pixel_to_cpixel((uint8_t*)&dst, &dstfmt, src_addr, &srcfmt, 4, 1); 56 | if (dst != u32_le(0x33221100u)) 57 | return false; 58 | 59 | return true; 60 | } 61 | 62 | static bool test_pixel_to_cpixel_3bpp(void) 63 | { 64 | //44 is extra data that should not be copied anywhere below. 65 | uint32_t src = u32_le(0x44112233u); 66 | uint32_t dst; 67 | uint8_t* src_addr = (uint8_t*)&src; 68 | 69 | struct rfb_pixel_format dstfmt = { 0 }, srcfmt = { 0 }; 70 | 71 | rfb_pixfmt_from_fourcc(&srcfmt, DRM_FORMAT_RGB888); 72 | 73 | dst = 0; 74 | rfb_pixfmt_from_fourcc(&dstfmt, DRM_FORMAT_RGBA8888); 75 | pixel_to_cpixel((uint8_t*)&dst, &dstfmt, src_addr, &srcfmt, 4, 1); 76 | if (dst != u32_le(0x11223300u)) 77 | return false; 78 | 79 | dst = 0; 80 | rfb_pixfmt_from_fourcc(&dstfmt, DRM_FORMAT_ABGR8888); 81 | pixel_to_cpixel((uint8_t*)&dst, &dstfmt, src_addr, &srcfmt, 4, 1); 82 | if (dst != u32_le(0x00332211u)) 83 | return false; 84 | 85 | dst = 0; 86 | rfb_pixfmt_from_fourcc(&dstfmt, DRM_FORMAT_ARGB8888); 87 | pixel_to_cpixel((uint8_t*)&dst, &dstfmt, src_addr, &srcfmt, 4, 1); 88 | if (dst != u32_le(0x00112233u)) 89 | return false; 90 | 91 | dst = 0; 92 | rfb_pixfmt_from_fourcc(&dstfmt, DRM_FORMAT_BGRA8888); 93 | pixel_to_cpixel((uint8_t*)&dst, &dstfmt, src_addr, &srcfmt, 4, 1); 94 | if (dst != u32_le(0x33221100u)) 95 | return false; 96 | 97 | return true; 98 | } 99 | 100 | static bool test_fourcc_to_pixman_fmt(void) 101 | { 102 | pixman_format_code_t r; 103 | 104 | #define check(a, b) \ 105 | r = 0; \ 106 | if (!fourcc_to_pixman_fmt(&r, b) || a != r) { \ 107 | fprintf(stderr, "Failed check for %s\n", XSTR(a)); \ 108 | return false; \ 109 | } while(0) 110 | 111 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 112 | check(PIXMAN_a2r10g10b10, DRM_FORMAT_ARGB2101010); 113 | check(PIXMAN_r8g8b8a8, DRM_FORMAT_RGBA8888); 114 | check(PIXMAN_b8g8r8a8, DRM_FORMAT_BGRA8888); 115 | check(PIXMAN_r5g6b5, DRM_FORMAT_RGB565); 116 | #else 117 | check(PIXMAN_r8g8b8a8, DRM_FORMAT_ABGR8888); 118 | check(PIXMAN_b8g8r8a8, DRM_FORMAT_ARGB8888); 119 | check(PIXMAN_r5g6b5, DRM_FORMAT_BGR565); 120 | #endif 121 | 122 | #undef check 123 | return true; 124 | } 125 | 126 | static bool test_extract_alpha_mask_rgba8888(void) 127 | { 128 | uint32_t test_data[] = { 129 | u32_le(0x00000000u), // Black, transparent 130 | u32_le(0xff000000u), // Red, transparent 131 | u32_le(0x00ff0000u), // Red, transparent 132 | u32_le(0x0000ff00u), // Green, transparent 133 | u32_le(0x000000ffu), // Black, opaque 134 | u32_le(0xff0000ffu), // Red, opaque 135 | u32_le(0x00ff00ffu), // Red, opaque 136 | u32_le(0x0000ffffu), // Green, opaque 137 | }; 138 | 139 | uint8_t mask[UDIV_UP(ARRAY_LEN(test_data), 8)] = { 0 }; 140 | 141 | bool ok = extract_alpha_mask(mask, test_data, DRM_FORMAT_RGBA8888, 142 | ARRAY_LEN(test_data)); 143 | if (!ok) { 144 | fprintf(stderr, "Failed to extract alpha mask"); 145 | return false; 146 | } 147 | 148 | if (mask[0] != 0x0f) { 149 | fprintf(stderr, "Expected alpha mask to be 0b00001111 (0x0f), but it was %x", 150 | mask[0]); 151 | return false; 152 | } 153 | 154 | memset(mask, 0, sizeof(mask)); 155 | 156 | ok = extract_alpha_mask(mask, test_data, DRM_FORMAT_BGRA8888, 157 | ARRAY_LEN(test_data)); 158 | if (!ok) { 159 | fprintf(stderr, "Failed to extract alpha mask"); 160 | return false; 161 | } 162 | 163 | if (mask[0] != 0x0f) { 164 | fprintf(stderr, "Expected alpha mask to be 0b00001111 (0x0f), but it was %x", 165 | mask[0]); 166 | return false; 167 | } 168 | 169 | return true; 170 | } 171 | 172 | static bool test_drm_format_to_string(void) 173 | { 174 | if (strcmp(drm_format_to_string(DRM_FORMAT_RGBA8888), "RGBA8888") != 0) 175 | return false; 176 | 177 | if (strcmp(drm_format_to_string(DRM_FORMAT_RGBX8888), "RGBX8888") != 0) 178 | return false; 179 | 180 | if (strcmp(drm_format_to_string(DRM_FORMAT_RGB565), "RGB565") != 0) 181 | return false; 182 | 183 | return true; 184 | } 185 | 186 | static bool test_rfb_pixfmt_to_string(void) 187 | { 188 | struct rfb_pixel_format rgbx8888; 189 | struct rfb_pixel_format bgrx8888; 190 | struct rfb_pixel_format xrgb8888; 191 | struct rfb_pixel_format xbgr8888; 192 | 193 | rfb_pixfmt_from_fourcc(&rgbx8888, DRM_FORMAT_RGBX8888); 194 | rfb_pixfmt_from_fourcc(&bgrx8888, DRM_FORMAT_BGRX8888); 195 | rfb_pixfmt_from_fourcc(&xrgb8888, DRM_FORMAT_XRGB8888); 196 | rfb_pixfmt_from_fourcc(&xbgr8888, DRM_FORMAT_XBGR8888); 197 | 198 | if (strcmp(rfb_pixfmt_to_string(&rgbx8888), "RGBX8888") != 0) 199 | return false; 200 | 201 | if (strcmp(rfb_pixfmt_to_string(&bgrx8888), "BGRX8888") != 0) 202 | return false; 203 | 204 | if (strcmp(rfb_pixfmt_to_string(&xrgb8888), "XRGB8888") != 0) 205 | return false; 206 | 207 | if (strcmp(rfb_pixfmt_to_string(&xbgr8888), "XBGR8888") != 0) 208 | return false; 209 | 210 | return true; 211 | } 212 | 213 | int main() 214 | { 215 | bool ok = test_pixel_to_cpixel_4bpp() && 216 | test_pixel_to_cpixel_3bpp() && 217 | test_fourcc_to_pixman_fmt() && 218 | test_extract_alpha_mask_rgba8888() && 219 | test_drm_format_to_string() && 220 | test_rfb_pixfmt_to_string(); 221 | return ok ? 0 : 1; 222 | } 223 | --------------------------------------------------------------------------------