void wlc_view_set_app_id_ptr(struct wlc_view *view, const char *app_id) { if (view && !chck_cstreq(view->data.app_id.data, app_id) && chck_string_set_cstr(&view->data.app_id, app_id, true)) { WLC_INTERFACE_EMIT(view.properties_updated, convert_to_wlc_handle(view), WLC_BIT_PROPERTY_APP_ID); } }
WLC_API void wlc_run(void) { if (!wlc.display) return; wlc.compositor.state.ready = false; bool emit_ready = true; const char *xwayland = getenv("WLC_XWAYLAND"); if (!xwayland || !chck_cstreq(xwayland, "0")) emit_ready = !wlc_xwayland_init(); // Emit ready immediately when no Xwayland if (emit_ready) { WLC_INTERFACE_EMIT(compositor.ready); wlc.compositor.state.ready = true; } wlc_set_active(true); if (wlc_compositor_is_good(&wlc.compositor)) wl_display_run(wlc.display); wlc_cleanup(); }
static void handle_arguments(int argc, char *argv[]) { for (int i = 1; i < argc; ++i) { if (chck_cstreq(argv[i], "--log")) { if (i + 1 >= argc) { plog(0, PLOG_ERROR, "--log takes an argument (filename)"); abort(); } log_set_file(argv[++i]); } } }
static bool is_hotplug(uint32_t drm_id, struct udev_device *device) { uint32_t id; const char *sysnum; if (!(sysnum = udev_device_get_sysnum(device)) || !chck_cstr_to_u32(sysnum, &id) || id != drm_id) return false; const char *val; if (!(val = udev_device_get_property_value(device, "HOTPLUG"))) return false; return chck_cstreq(val, "1"); }
int main(void) { /* TEST: stripping */ { struct chck_string v = {0}; assert(chck_string_set_cstr(&v, " contains some whitespace ", true)); assert(chck_cstr_strip(v.data) == v.data + 3); assert(strlen(v.data + 3) == 24); assert(chck_cstreq(v.data + 3, "contains some whitespace")); assert(chck_string_set_cstr(&v, "asd", true)); assert(chck_cstr_strip(v.data) == v.data); assert(strlen(v.data) == 3); assert(chck_cstreq(v.data, "asd")); assert(chck_string_set_cstr(&v, "foo baz lol", true)); assert(chck_cstr_remove_chars(v.data, "baz") == v.data); assert(chck_cstreq(v.data, "foo lol")); chck_string_release(&v); assert(chck_string_set_cstr(&v, "foo baz lol", true)); assert(chck_cstr_remove_chars(v.data, "qwerty") == v.data); assert(chck_cstreq(v.data, "foo baz lol")); chck_string_release(&v); assert(chck_string_set_cstr(&v, "foo --- bar", true)); assert(chck_cstr_replace_char(v.data, '-', '.') == v.data); assert(chck_cstreq(v.data, "foo ... bar")); chck_string_release(&v); assert(chck_string_set_cstr(&v, "foo --- bar", true)); assert(chck_cstr_replace_char(v.data, '.', '-') == v.data); assert(chck_cstreq(v.data, "foo --- bar")); chck_string_release(&v); } /* TEST: tokenizing */ { const char *v = " token: this :please "; { size_t len, i = 0; const char *t, *state = NULL; const char *except[] = { " token", " this ", "please " }; while ((t = chck_cstr_tokenize(v, &len, ":", false, &state))) { assert(i < 3); assert(len == strlen(except[i])); assert(chck_cstrneq(except[i], t, len)); ++i; } assert(i == 3); } { size_t len, i = 0; const char *t, *state = NULL; const char *except[] = { "token", "this", "please" }; while ((t = chck_cstr_tokenize(v, &len, ":", true, &state))) { assert(i < 3); assert(len == strlen(except[i])); assert(chck_cstrneq(except[i], t, len)); ++i; } assert(i == 3); } v = "some : words : \" but this is included \" : \"yay : yoy\" : \"foo\""; { size_t len, i = 0; const char *t, *state = NULL; const char *except[] = { "some", "words", " but this is included ", "yay : yoy", "foo" }; while ((t = chck_cstr_tokenize_quoted(v, &len, ":", "\"", &state))) { assert(i < 5); assert(len == strlen(except[i])); assert(chck_cstrneq(except[i], t, len)); ++i; } assert(i == 5); } v = "some words \" but this is included \" \"yay yoy\" \"foo\""; { size_t len, i = 0; const char *t, *state = NULL; const char *except[] = { "some", "words", " but this is included ", "yay yoy", "foo" }; while ((t = chck_cstr_tokenize_quoted(v, &len, " ", "\"", &state))) { assert(i < 5); assert(len == strlen(except[i])); assert(chck_cstrneq(except[i], t, len)); ++i; } assert(i == 5); } } /* TEST: bool conversion tests */ { bool v; assert(chck_cstr_to_bool("true", &v) && v == true); assert(chck_cstr_to_bool("false", &v) && v == false); assert(chck_cstr_to_bool("1", &v) && v == true); assert(chck_cstr_to_bool("0", &v) && v == false); assert(!chck_cstr_to_bool("falsef", NULL)); assert(!chck_cstr_to_bool("fals", NULL)); assert(!chck_cstr_to_bool("truee", NULL)); assert(!chck_cstr_to_bool("tru", NULL)); assert(!chck_cstr_to_bool("not-a-bool", NULL)); assert(!chck_cstr_to_bool("5", NULL)); } /* TEST: float conversion tests */ { float v; assert(chck_cstr_to_f("0.123", &v) && chck_equalf(v, 0.123, 1.0f)); assert(chck_cstr_to_f("0.1e2", &v) && chck_equalf(v, 0.1e2, 1.0f)); assert(!chck_cstr_to_f("0.1e1000", NULL)); assert(!chck_cstr_to_f("not-float", NULL)); } /* TEST: double conversion tests */ { double v; assert(chck_cstr_to_d("0.123", &v) && chck_equal(v, 0.123, 1.0)); assert(chck_cstr_to_d("0.1e100", &v) && chck_equal(v, 0.1e100, 1.0)); assert(!chck_cstr_to_d("0.1e1000", NULL)); assert(!chck_cstr_to_d("not-double", NULL)); } /* TEST: long double conversion tests */ { long double v; // valgrind does not handle long double so we don't do 1e1000 here assert(chck_cstr_to_ld("0.123", &v) && chck_equalld(v, 0.123l, 1.0l)); assert(chck_cstr_to_ld("0.1e100", &v) && chck_equalld(v, 0.1e100l, 1.0)); assert(!chck_cstr_to_ld("0.1e100000", NULL)); assert(!chck_cstr_to_ld("not-long-double", NULL)); } /* TEST: i64 conversion tests */ { int64_t v; assert(chck_cstr_to_i64("1", &v) && v == 1); assert(chck_cstr_to_i64("-1", &v) && v == -1); assert(chck_cstr_to_i64("9223372036854775807", &v) && v == INT64_MAX); assert(chck_cstr_to_i64("-9223372036854775808", &v) && v == INT64_MIN); assert(!chck_cstr_to_i64("9223372036854775808", &v)); assert(!chck_cstr_to_i64("-9223372036854775809", &v)); assert(!chck_cstr_to_i64("1.0", &v)); uint64_t uv; assert(chck_cstr_to_u64("1", &uv) && uv == 1); assert(!chck_cstr_to_u64("-1", &uv)); assert(chck_cstr_to_u64("18446744073709551615", &uv) && uv == UINT64_MAX); assert(!chck_cstr_to_u64("18446744073709551616", &uv)); assert(!chck_cstr_to_u64("1.0", &uv)); } /* TEST: i32 conversion tests */ { int32_t v; assert(chck_cstr_to_i32("1", &v) && v == 1); assert(chck_cstr_to_i32("-1", &v) && v == -1); assert(chck_cstr_to_i32("2147483647", &v) && v == INT32_MAX); assert(chck_cstr_to_i32("-2147483648", &v) && v == INT32_MIN); assert(!chck_cstr_to_i32("2147483648", &v)); assert(!chck_cstr_to_i32("-2147483649", &v)); assert(!chck_cstr_to_i32("1.0", &v)); uint32_t uv; assert(chck_cstr_to_u32("1", &uv) && uv == 1); assert(!chck_cstr_to_u32("-1", &uv)); assert(chck_cstr_to_u32("4294967295", &uv) && uv == UINT32_MAX); assert(!chck_cstr_to_u32("4294967296", &uv)); assert(!chck_cstr_to_u32("1.0", &uv)); } /* TEST: i16 conversion tests */ { int16_t v; assert(chck_cstr_to_i16("1", &v) && v == 1); assert(chck_cstr_to_i16("-1", &v) && v == -1); assert(chck_cstr_to_i16("32767", &v) && v == INT16_MAX); assert(chck_cstr_to_i16("-32768", &v) && v == INT16_MIN); assert(!chck_cstr_to_i16("32768", &v)); assert(!chck_cstr_to_i16("-32769", &v)); assert(!chck_cstr_to_i16("1.0", &v)); uint16_t uv; assert(chck_cstr_to_u16("1", &uv) && uv == 1); assert(!chck_cstr_to_u16("-1", &uv)); assert(chck_cstr_to_u16("65535", &uv) && uv == UINT16_MAX); assert(!chck_cstr_to_u16("65536", &uv)); assert(!chck_cstr_to_u16("1.0", &uv)); } /* TEST: i8 conversion tests */ { int8_t v; assert(chck_cstr_to_i8("1", &v) && v == 1); assert(chck_cstr_to_i8("-1", &v) && v == -1); assert(chck_cstr_to_i8("127", &v) && v == INT8_MAX); assert(chck_cstr_to_i8("-128", &v) && v == INT8_MIN); assert(!chck_cstr_to_i8("128", &v)); assert(!chck_cstr_to_i8("-129", &v)); assert(!chck_cstr_to_i8("1.0", &v)); uint8_t uv; assert(chck_cstr_to_u8("1", &uv) && uv == 1); assert(!chck_cstr_to_u8("-1", &uv)); assert(chck_cstr_to_u8("255", &uv) && uv == UINT8_MAX); assert(!chck_cstr_to_u8("257", &uv)); assert(!chck_cstr_to_u8("1.0", &uv)); } /* TEST: string tests */ { struct chck_string str = {0}; assert(chck_string_set_cstr(&str, "foobar", false)); assert(str.data && str.size == strlen("foobar") && chck_string_eq_cstr(&str, "foobar")); assert(chck_string_ends_with_cstr(&str, "") && chck_string_ends_with_cstr(&str, NULL) && chck_string_ends_with_cstr(&str, "foobar") && chck_string_ends_with_cstr(&str, "bar") && !chck_string_ends_with_cstr(&str, "bur")); assert(chck_string_starts_with_cstr(&str, "") && chck_string_starts_with_cstr(&str, NULL) && chck_string_starts_with_cstr(&str, "foobar") && chck_string_starts_with_cstr(&str, "foo") && !chck_string_starts_with_cstr(&str, "fuo")); assert(!chck_string_is_empty(&str)); assert(chck_string_set_cstr(&str, "", false)); assert(!str.data && str.size == strlen("") && chck_string_eq_cstr(&str, "")); assert(!str.data && str.size == 0 && chck_string_eq_cstr(&str, NULL)); // string_eq_cstr treats empty string as NULL assert(chck_string_ends_with_cstr(&str, "") && chck_string_ends_with_cstr(&str, NULL) && !chck_string_ends_with_cstr(&str, "foobar")); assert(chck_string_starts_with_cstr(&str, "") && chck_string_starts_with_cstr(&str, NULL) && !chck_string_starts_with_cstr(&str, "foobar")); assert(chck_string_is_empty(&str)); assert(chck_string_set_cstr(&str, NULL, false)); assert(!str.data && str.size == 0 && !chck_string_eq_cstr(&str, "foobar")); assert(chck_string_ends_with_cstr(&str, "") && chck_string_ends_with_cstr(&str, NULL) && !chck_string_ends_with_cstr(&str, "foobar")); assert(chck_string_starts_with_cstr(&str, "") && chck_string_starts_with_cstr(&str, NULL) && !chck_string_starts_with_cstr(&str, "foobar")); assert(chck_string_is_empty(&str)); // is_heap is false, so nothing is copied. // since chck_strings do not copy when is_heap is false, printf for .data would print whole "foobar" in this case. // if is_heap is set to true, the string is copied and terminated correctly. assert(chck_string_set_cstr_with_length(&str, "foobar", 3, false)); assert(str.data && str.size == strlen("foo") && chck_string_eq_cstr(&str, "foo") && !chck_string_eq_cstr(&str, "foobar")); struct chck_string str2 = {0}; assert(chck_string_set(&str2, &str, false)); assert(str.data == str2.data && str2.size == strlen("foo") && chck_string_eq(&str, &str2)); assert(chck_string_ends_with(&str, &str2)); assert(chck_string_starts_with(&str, &str2)); assert(chck_string_set(&str2, &str, true)); assert(str.data != str2.data && str2.size == strlen("foo") && chck_string_eq(&str, &str2)); assert(chck_string_ends_with(&str, &str2)); assert(chck_string_starts_with(&str, &str2)); assert(chck_string_set_cstr(&str2, "foobar2", false)); assert(str.data != str2.data && str2.size == strlen("foobar2") && !chck_string_eq(&str, &str2)); assert(!chck_string_ends_with(&str, &str2)); assert(!chck_string_starts_with(&str, &str2)); assert(chck_string_set_format(&str, "%s-%s", "test", str2.data)); assert(str.data && str.size == strlen("test-foobar2") && str.is_heap && chck_string_eq_cstr(&str, "test-foobar2")); chck_string_release(&str); chck_string_release(&str2); assert(!str.data && !str2.data && str.size + str2.size == 0); assert(chck_string_eq(&str, &str2)); assert(chck_cstreq("foobar", "foobar")); assert(!chck_cstreq("foobar", "foo")); assert(!chck_cstreq("foobar", NULL)); assert(chck_cstreq(NULL, NULL)); assert(chck_cstrneq("foobar", "foo", 3)); assert(chck_cstrneq("fo", "fo", 3)); assert(!chck_cstrneq("foobar", "foa", 3)); assert(chck_cstrneq(NULL, NULL, 3)); assert(chck_cstrneq(NULL, NULL, 0)); assert(!chck_cstr_is_empty("foobar")); assert(chck_cstr_ends_with("foobar", "foobar") && chck_cstr_ends_with("foobar", "bar") && !chck_cstr_ends_with("foobar", "bur")); assert(chck_cstr_starts_with("foobar", "foobar") && chck_cstr_starts_with("foobar", "foo") && !chck_cstr_starts_with("foobar", "fur")); assert(chck_cstr_is_empty("") && chck_cstr_is_empty(NULL)); assert(chck_cstr_ends_with("", "") && chck_cstr_ends_with(NULL, NULL)); assert(chck_cstr_starts_with("", "") && chck_cstr_starts_with(NULL, NULL)); } return EXIT_SUCCESS; }
WLC_API bool wlc_init(const struct wlc_interface *interface, int argc, char *argv[]) { assert(interface); if (!interface) die("no wlc_interface was given"); if (wlc.display) return true; memset(&wlc, 0, sizeof(wlc)); wl_log_set_handler_server(wl_cb_log); for (int i = 1; i < argc; ++i) { if (chck_cstreq(argv[i], "--log")) { if (i + 1 >= argc) die("--log takes an argument (filename)"); wlc_set_log_file(fopen(argv[++i], "a")); } } unsetenv("TERM"); const char *x11display = getenv("DISPLAY"); bool privilidged = false; const bool has_logind = wlc_logind_available(); if (getuid() != geteuid() || getgid() != getegid()) { wlc_log(WLC_LOG_INFO, "Doing work on SUID/SGID side and dropping permissions"); privilidged = true; } else if (getuid() == 0) { die("Do not run wlc compositor as root"); } else if (!x11display && !has_logind && access("/dev/input/event0", R_OK | W_OK) != 0) { die("Not running from X11 and no access to /dev/input/event0 or logind available"); } #ifndef NDEBUG { struct sigaction action = { .sa_handler = backtrace }; sigaction(SIGABRT, &action, NULL); sigaction(SIGSEGV, &action, NULL); // XXX: Some weird sigfpes seems to come when running // wlc compositor inside wlc compositor (X11 backend). // Seems to be caused by resolution changes and mouse clicks. // Gather more information about this later and see what's going on. if (!getenv("WAYLAND_DISPLAY")) fpesetup(&action); } #endif int vt = 0; #ifdef HAS_LOGIND // Init logind if we are not running as SUID. // We need event loop for logind to work, and thus we won't allow it on SUID process. if (!privilidged && !x11display && has_logind) { if (!(wlc.display = wl_display_create())) die("Failed to create wayland display"); if (!(vt = wlc_logind_init("seat0"))) die("Failed to init logind"); } #else (void)privilidged; #endif if (!x11display) wlc_tty_init(vt); // -- we open tty before dropping permissions // so the fd process can also handle cleanup in case of crash // if logind initialized correctly, fd process does nothing but handle crash. { struct wl_display *display = wlc.display; wlc.display = NULL; wlc_fd_init(argc, argv, (vt != 0)); wlc.display = display; } // -- permissions are now dropped wl_signal_init(&wlc.signals.terminate); wl_signal_init(&wlc.signals.activate); wl_signal_init(&wlc.signals.compositor); wl_signal_init(&wlc.signals.focus); wl_signal_init(&wlc.signals.surface); wl_signal_init(&wlc.signals.input); wl_signal_init(&wlc.signals.output); wl_signal_init(&wlc.signals.render); wl_signal_init(&wlc.signals.xwayland); wl_signal_add(&wlc.signals.compositor, &compositor_listener); if (!wlc_resources_init()) die("Failed to init resource manager"); if (!wlc.display && !(wlc.display = wl_display_create())) die("Failed to create wayland display"); const char *socket_name; if (!(socket_name = wl_display_add_socket_auto(wlc.display))) die("Failed to add socket to wayland display"); if (socket_name) // shut up static analyze setenv("WAYLAND_DISPLAY", socket_name, true); if (wl_display_init_shm(wlc.display) != 0) die("Failed to init shm"); if (!wlc_udev_init()) die("Failed to init udev"); const char *libinput = getenv("WLC_LIBINPUT"); if (!x11display || (libinput && !chck_cstreq(libinput, "0"))) { if (!wlc_input_init()) die("Failed to init input"); } memcpy(&wlc.interface, interface, sizeof(wlc.interface)); if (!wlc_compositor(&wlc.compositor)) die("Failed to init compositor"); const char *xwayland = getenv("WLC_XWAYLAND"); if (!xwayland || !chck_cstreq(xwayland, "0")) { if (!(wlc_xwayland_init())) die("Failed to init xwayland"); } else { wlc.set_ready_on_run = true; } wlc_set_active(true); return wlc_compositor_is_good(&wlc.compositor); }
WLC_API bool wlc_init(const struct wlc_interface *interface, int argc, char *argv[]) { assert(interface); if (!interface) die("no wlc_interface was given"); if (wlc.display) return true; // reset wlc state, but keep log function void *log_fun = wlc.log_fun; memset(&wlc, 0, sizeof(wlc)); wlc.log_fun = log_fun; wl_log_set_handler_server(wl_cb_log); unsetenv("TERM"); const char *x11display = getenv("DISPLAY"); bool privileged = false; const bool has_logind = wlc_logind_available(); if (getuid() != geteuid() || getgid() != getegid()) { wlc_log(WLC_LOG_INFO, "Doing work on SUID/SGID side and dropping permissions"); privileged = true; } else if (getuid() == 0) { die("Do not run wlc compositor as root"); } else if (!x11display && !has_logind && access("/dev/input/event0", R_OK | W_OK) != 0) { die("Not running from X11 and no access to /dev/input/event0 or logind available"); } int vt = 0; #ifdef HAS_LOGIND // Init logind if we are not running as SUID. // We need event loop for logind to work, and thus we won't allow it on SUID process. if (!privileged && !x11display && has_logind) { if (!(wlc.display = wl_display_create())) die("Failed to create wayland display"); if (!(vt = wlc_logind_init("seat0"))) die("Failed to init logind"); } #else (void)privileged; #endif if (!x11display) wlc_tty_init(vt); // -- we open tty before dropping permissions // so the fd process can also handle cleanup in case of crash // if logind initialized correctly, fd process does nothing but handle crash. { struct wl_display *display = wlc.display; wlc.display = NULL; wlc_fd_init(argc, argv, (vt != 0)); wlc.display = display; } // -- permissions are now dropped wl_signal_init(&wlc.signals.terminate); wl_signal_init(&wlc.signals.activate); wl_signal_init(&wlc.signals.compositor); wl_signal_init(&wlc.signals.focus); wl_signal_init(&wlc.signals.surface); wl_signal_init(&wlc.signals.input); wl_signal_init(&wlc.signals.output); wl_signal_init(&wlc.signals.render); wl_signal_init(&wlc.signals.xwayland); wl_signal_add(&wlc.signals.compositor, &compositor_listener); if (!wlc_resources_init()) die("Failed to init resource manager"); if (!wlc.display && !(wlc.display = wl_display_create())) die("Failed to create wayland display"); const char *socket_name; if (!(socket_name = wl_display_add_socket_auto(wlc.display))) die("Failed to add socket to wayland display"); if (socket_name) // shut up static analyze setenv("WAYLAND_DISPLAY", socket_name, true); if (wl_display_init_shm(wlc.display) != 0) die("Failed to init shm"); if (!wlc_udev_init()) die("Failed to init udev"); const char *libinput = getenv("WLC_LIBINPUT"); if (!x11display || (libinput && !chck_cstreq(libinput, "0"))) { if (!wlc_input_init()) die("Failed to init input"); } if (!wlc_compositor(&wlc.compositor)) die("Failed to init compositor"); memcpy(&wlc.interface, interface, sizeof(wlc.interface)); return true; }