diff --git a/include/neatvnc.h b/include/neatvnc.h index 6c88dfe..ab9e512 100644 --- a/include/neatvnc.h +++ b/include/neatvnc.h @@ -19,9 +19,35 @@ #include #include #include +#include +#include +#include #define NVNC_NO_PTS UINT64_MAX +#define nvnc_log(lvl, fmt, ...) do { \ + assert(lvl != NVNC_LOG_TRACE); \ + struct nvnc_log_data ld = { \ + .level = lvl, \ + .file = __FILE__, \ + .line = __LINE__, \ + }; \ + nvnc__log(&ld, fmt, ## __VA_ARGS__); \ +} while(0) + +#ifndef NDEBUG +#define nvnc_trace(fmt, ...) do { \ + struct nvnc_log_data ld = { \ + .level = NVNC_LOG_TRACE, \ + .file = __FILE__, \ + .line = __LINE__, \ + }; \ + nvnc__log(&ld, fmt, ## __VA_ARGS__); \ +} while(0) +#else +#define nvnc_trace(...) +#endif + struct nvnc; struct nvnc_client; struct nvnc_display; @@ -56,6 +82,21 @@ enum nvnc_transform { NVNC_TRANSFORM_FLIPPED_270 = 7, }; +enum nvnc_log_level { + NVNC_LOG_PANIC = 0, + NVNC_LOG_ERROR = 1, + NVNC_LOG_WARNING = 2, + NVNC_LOG_INFO = 3, + NVNC_LOG_DEBUG = 4, + NVNC_LOG_TRACE = 5, +}; + +struct nvnc_log_data { + enum nvnc_log_level level; + const char* file; + int line; +}; + typedef void (*nvnc_key_fn)(struct nvnc_client*, uint32_t key, bool is_pressed); typedef void (*nvnc_pointer_fn)(struct nvnc_client*, uint16_t x, uint16_t y, @@ -70,6 +111,7 @@ typedef bool (*nvnc_auth_fn)(const char* username, const char* password, typedef void (*nvnc_cut_text_fn)(struct nvnc*, const char* text, uint32_t len); typedef void (*nvnc_fb_release_fn)(struct nvnc_fb*, void* context); typedef void (*nvnc_cleanup_fn)(void* userdata); +typedef void (*nvnc_log_fn)(const struct nvnc_log_data*, const char* message); extern const char nvnc_version[]; @@ -151,3 +193,7 @@ void nvnc_send_cut_text(struct nvnc*, const char* text, uint32_t len); void nvnc_set_cursor(struct nvnc*, struct nvnc_fb*, uint16_t width, uint16_t height, uint16_t hotspot_x, uint16_t hotspot_y, bool is_damaged); + +void nvnc_set_log_fn(nvnc_log_fn); +void nvnc_set_log_level(enum nvnc_log_level); +void nvnc__log(const struct nvnc_log_data*, const char* fmt, ...); diff --git a/meson.build b/meson.build index 5cfd533..2916d74 100644 --- a/meson.build +++ b/meson.build @@ -84,6 +84,7 @@ sources = [ 'src/murmurhash.c', 'src/encoder.c', 'src/cursor.c', + 'src/logging.c', ] dependencies = [ diff --git a/src/logging.c b/src/logging.c new file mode 100644 index 0000000..2254d1f --- /dev/null +++ b/src/logging.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2019 - 2022 Andri Yngvason + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "neatvnc.h" +#include "common.h" + +#include +#include +#include + +#define EXPORT __attribute__((visibility("default"))) + +static void default_logger(const struct nvnc_log_data* meta, + const char* message); + +static nvnc_log_fn log_fn = default_logger; + +#ifndef NDEBUG +static enum nvnc_log_level log_level = NVNC_LOG_DEBUG; +#else +static enum nvnc_log_level log_level = NVNC_LOG_WARNING; +#endif + +static const char* log_level_to_string(enum nvnc_log_level level) +{ + switch (level) { + case NVNC_LOG_PANIC: return "PANIC"; + case NVNC_LOG_ERROR: return "ERROR"; + case NVNC_LOG_WARNING: return "Warning"; + case NVNC_LOG_INFO: return "Info"; + case NVNC_LOG_DEBUG: return "DEBUG"; + case NVNC_LOG_TRACE: return "TRACE"; + } + + return "UNKNOWN"; +} + +static FILE* stream_for_log_level(enum nvnc_log_level level) +{ + switch (level) { + case NVNC_LOG_PANIC: return stderr; + case NVNC_LOG_ERROR: return stderr; + case NVNC_LOG_WARNING: return stderr; + case NVNC_LOG_INFO: return stdout; + case NVNC_LOG_DEBUG: return stdout; + case NVNC_LOG_TRACE: return stdout; + } + + return stderr; +} + +static void nvnc__vlog(const struct nvnc_log_data* meta, const char* fmt, + va_list args) +{ + char message[1024]; + + if (meta->level <= log_level) { + vsnprintf(message, sizeof(message), fmt, args); + log_fn(meta, message); + } + + if (meta->level == NVNC_LOG_PANIC) + abort(); +} + +static void default_logger(const struct nvnc_log_data* meta, + const char* message) +{ + const char* level = log_level_to_string(meta->level); + FILE* stream = stream_for_log_level(meta->level); + + if (meta->level == NVNC_LOG_INFO) + fprintf(stream, "Info: %s\n", message); + else + fprintf(stream, "%s: %s: %d: %s\n", level, meta->file, + meta->line, message); + + fflush(stream); +} + +EXPORT +void nvnc_set_log_level(enum nvnc_log_level level) +{ + log_level = level; +} + +EXPORT +void nvnc_set_log_fn(nvnc_log_fn fn) +{ + log_fn = fn; +} + +EXPORT +void nvnc__log(const struct nvnc_log_data* meta, + const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + nvnc__vlog(meta, fmt, ap); + va_end(ap); +}