static bool create_fb(struct gbm_surface *surface, struct drm_fb *fb) { assert(surface && fb); if (!gbm.api.gbm_surface_has_free_buffers(surface)) goto no_buffers; if (!(fb->bo = gbm.api.gbm_surface_lock_front_buffer(surface))) goto failed_to_lock; uint32_t width = gbm.api.gbm_bo_get_width(fb->bo); uint32_t height = gbm.api.gbm_bo_get_height(fb->bo); uint32_t handle = gbm.api.gbm_bo_get_handle(fb->bo).u32; uint32_t stride = gbm.api.gbm_bo_get_stride(fb->bo); if (drm.api.drmModeAddFB(drm.fd, width, height, 24, 32, stride, handle, &fb->fd)) goto failed_to_create_fb; fb->stride = stride; return true; no_buffers: wlc_log(WLC_LOG_WARN, "gbm is out of buffers"); goto fail; failed_to_lock: wlc_log(WLC_LOG_WARN, "Failed to lock front buffer"); goto fail; failed_to_create_fb: wlc_log(WLC_LOG_WARN, "Failed to create fb"); fail: release_fb(surface, fb); return false; }
static bool x11_xcb_load(void) { const char *lib = "libX11-xcb.so", *func = NULL; if (!(x11.api.x11_xcb_handle = dlopen(lib, RTLD_LAZY))) { wlc_log(WLC_LOG_WARN, "%s", dlerror()); return false; } #define load(x) (x11.api.x = dlsym(x11.api.x11_xcb_handle, (func = #x))) if (!load(XGetXCBConnection)) goto function_pointer_exception; if (!load(XSetEventQueueOwner)) goto function_pointer_exception; #undef load return true; function_pointer_exception: wlc_log(WLC_LOG_WARN, "Could not load function '%s' from '%s'", func, lib); return false; }
static bool x11_load(void) { const char *lib = "libX11.so", *func = NULL; if (!(x11.api.x11_handle = dlopen(lib, RTLD_LAZY))) { wlc_log(WLC_LOG_WARN, "%s", dlerror()); return false; } #define load(x) (x11.api.x = dlsym(x11.api.x11_handle, (func = #x))) if (!load(XOpenDisplay)) goto function_pointer_exception; if (!load(XCloseDisplay)) goto function_pointer_exception; #undef load return true; function_pointer_exception: wlc_log(WLC_LOG_WARN, "Could not load function '%s' from '%s'", func, lib); return false; }
static bool xcb_xkb_load(void) { const char *lib = "libxcb-xkb.so", *func = NULL; if (!(x11.api.xcb_xkb_handle = dlopen(lib, RTLD_LAZY))) { wlc_log(WLC_LOG_WARN, "%s", dlerror()); return false; } #define load(x) (x11.api.x = dlsym(x11.api.xcb_xkb_handle, (func = #x))) if (!load(xcb_xkb_per_client_flags)) goto function_pointer_exception; if (!load(xcb_xkb_per_client_flags_reply)) goto function_pointer_exception; if (!load(xcb_xkb_select_events_checked)) goto function_pointer_exception; if (!load(xcb_xkb_use_extension)) goto function_pointer_exception; if (!load(xcb_xkb_use_extension_reply)) goto function_pointer_exception; if (!load(xcb_xkb_id)) goto function_pointer_exception; #undef load return true; function_pointer_exception: wlc_log(WLC_LOG_WARN, "Could not load function '%s' from '%s'", func, lib); return false; }
static void backtrace(int signal) { (void)signal; if (clearenv() != 0) exit(EXIT_FAILURE); /* GDB */ #if defined(__linux__) || defined(__APPLE__) pid_t child_pid = fork(); #if HAS_YAMA_PRCTL /* tell yama that we allow our child_pid to trace our process */ if (child_pid > 0) { if (!prctl(PR_GET_DUMPABLE)) { wlc_log(WLC_LOG_WARN, "Compositor binary is suid/sgid, most likely since you are running from TTY."); wlc_log(WLC_LOG_WARN, "Kernel ptracing security policy does not allow attaching to suid/sgid processes."); wlc_log(WLC_LOG_WARN, "If you don't get backtrace below, try `setcap cap_sys_ptrace=eip gdb` temporarily."); } prctl(PR_SET_DUMPABLE, 1); prctl(PR_SET_PTRACER, child_pid); } #endif if (child_pid < 0) { wlc_log(WLC_LOG_WARN, "Fork failed for gdb backtrace"); } else if (child_pid == 0) { /* * NOTE: gdb-7.8 does not seem to work with this, * either downgrade to 7.7 or use gdb from master. */ /* sed -n '/bar/h;/bar/!H;$!b;x;p' (another way, if problems) */ char buf[255]; const int fd = fileno(wlc_get_log_file()); snprintf(buf, sizeof(buf) - 1, "gdb -p %d -n -batch -ex bt 2>/dev/null | sed -n '/<signal handler/{n;x;b};H;${x;p}' 1>&%d", getppid(), fd); execl("/bin/sh", "/bin/sh", "-c", buf, NULL); wlc_log(WLC_LOG_ERROR, "Failed to launch gdb for backtrace"); _exit(EXIT_FAILURE); } else { waitpid(child_pid, NULL, 0); } #endif /* SIGABRT || SIGSEGV */ exit(EXIT_FAILURE); }
void wlc_cleanup(void) { if (wlc.display) { wlc_log(WLC_LOG_INFO, "Cleanup wlc"); // fd process never allocates display wlc_compositor_release(&wlc.compositor); wl_list_remove(&compositor_listener.link); wlc_xwayland_terminate(); wlc_input_terminate(); wlc_udev_terminate(); wlc_fd_terminate(); } // however if main process crashed, fd process does // know enough about tty to reset it. wlc_tty_terminate(); if (wlc.display) wl_display_destroy(wlc.display); // reset te struct, but keep the wlc log state FILE *f = wlc.log_file; int cached_tm_mday = wlc.cached_tm_mday; memset(&wlc, 0, sizeof(wlc)); wlc_set_log_file(f); wlc.cached_tm_mday = cached_tm_mday; }
static void fpehandler(int signal) { (void)signal; wlc_log(WLC_LOG_INFO, "SIGFPE signal received"); abort(); }
void wlc_cleanup(void) { if (wlc.display) { wlc_log(WLC_LOG_INFO, "Cleanup wlc"); // fd process never allocates display wlc_compositor_release(&wlc.compositor); wl_list_remove(&compositor_listener.link); wlc_xwayland_terminate(); wlc_resources_terminate(); wlc_input_terminate(); wlc_udev_terminate(); wlc_fd_terminate(); } // however if main process crashed, fd process does // know enough about tty to reset it. wlc_tty_terminate(); if (wlc.display) wl_display_destroy(wlc.display); memset(&wlc, 0, sizeof(wlc)); }
static bool surface_attach(struct ctx *context, struct wlc_context *bound, struct wlc_surface *surface, struct wlc_buffer *buffer) { assert(context && bound && surface); struct wl_resource *wl_buffer; if (!buffer || !(wl_buffer = convert_to_wl_resource(buffer, "buffer"))) { surface_destroy(context, bound, surface); return true; } EGLint format; bool attached = false; struct wl_shm_buffer *shm_buffer = wl_shm_buffer_get(wl_buffer); if (shm_buffer) { attached = shm_attach(surface, buffer, shm_buffer); } else if (wlc_context_query_buffer(bound, (void*)wl_buffer, EGL_TEXTURE_FORMAT, &format)) { attached = egl_attach(context, bound, surface, buffer, format); } else { /* unknown buffer */ wlc_log(WLC_LOG_WARN, "Unknown buffer"); } if (attached) wlc_dlog(WLC_DBG_RENDER, "-> Attached surface (%" PRIuWLC ") with buffer of size (%ux%u)", convert_to_wlc_resource(surface), buffer->size.w, buffer->size.h); return attached; }
static bool drm_load(void) { const char *lib = "libdrm.so", *func = NULL; if (!(drm.api.handle = dlopen(lib, RTLD_LAZY))) { wlc_log(WLC_LOG_WARN, "%s", dlerror()); return false; } #define load(x) (drm.api.x = dlsym(drm.api.handle, (func = #x))) if (!load(drmIoctl)) goto function_pointer_exception; if (!load(drmModeAddFB)) goto function_pointer_exception; if (!load(drmModeRmFB)) goto function_pointer_exception; if (!load(drmModePageFlip)) goto function_pointer_exception; if (!load(drmModeSetCrtc)) goto function_pointer_exception; if (!load(drmHandleEvent)) goto function_pointer_exception; if (!load(drmModeGetResources)) goto function_pointer_exception; if (!load(drmModeGetCrtc)) goto function_pointer_exception; if (!load(drmModeFreeCrtc)) goto function_pointer_exception; if (!load(drmModeGetConnector)) goto function_pointer_exception; if (!load(drmModeFreeConnector)) goto function_pointer_exception; if (!load(drmModeGetEncoder)) goto function_pointer_exception; if (!load(drmModeFreeEncoder)) goto function_pointer_exception; #undef load return true; function_pointer_exception: wlc_log(WLC_LOG_WARN, "Could not load function '%s' from '%s'", func, lib); return false; }
static void sigusr_handler(int signal_number) { assert(signal_number == SIGUSR1); wlc_log(WLC_LOG_INFO, "Xwayland started (DISPLAY %s)", xserver.display_name); sigaction(signal_number, &xserver.old_sigusr1, NULL); setenv("DISPLAY", xserver.display_name, true); wl_signal_emit(&wlc_system_signals()->xwayland, &(bool){true});
static void gl_call(const char *func, uint32_t line, const char *glfunc) { GLenum error; if ((error = gl.api.glGetError()) == GL_NO_ERROR) return; wlc_log(WLC_LOG_ERROR, "gles2: function %s at line %u: %s == %s", func, line, glfunc, gl_error_string(error)); }
WLC_API void wlc_terminate(void) { if (!wlc.display) return; wlc_log(WLC_LOG_INFO, "Terminating wlc..."); wl_signal_emit(&wlc.signals.terminate, NULL); }
static void egl_call(const char *func, uint32_t line, const char *eglfunc) { EGLint error; if ((error = eglGetError()) == EGL_SUCCESS) return; wlc_log(WLC_LOG_ERROR, "egl: function %s at line %u: %s\n%s", func, line, eglfunc, egl_error_string(error)); }
static bool gbm_load(void) { const char *lib = "libgbm.so", *func = NULL; if (!(gbm.api.handle = dlopen(lib, RTLD_LAZY))) { wlc_log(WLC_LOG_WARN, "%s", dlerror()); return false; } #define load(x) (gbm.api.x = dlsym(gbm.api.handle, (func = #x))) if (!load(gbm_create_device)) goto function_pointer_exception; if (!load(gbm_device_destroy)) goto function_pointer_exception; if (!load(gbm_surface_create)) goto function_pointer_exception; if (!load(gbm_surface_destroy)) goto function_pointer_exception; if (!load(gbm_bo_get_handle)) goto function_pointer_exception; if (!load(gbm_bo_get_width)) goto function_pointer_exception; if (!load(gbm_bo_get_height)) goto function_pointer_exception; if (!load(gbm_bo_get_stride)) goto function_pointer_exception; if (!load(gbm_surface_has_free_buffers)) goto function_pointer_exception; if (!load(gbm_surface_lock_front_buffer)) goto function_pointer_exception; if (!load(gbm_surface_release_buffer)) goto function_pointer_exception; #undef load return true; function_pointer_exception: wlc_log(WLC_LOG_WARN, "Could not load function '%s' from '%s'", func, lib); return false; }
static int udev_event(int fd, uint32_t mask, void *data) { (void)fd, (void)mask; struct udev *udev = data; struct udev_device *device; if (!(device = udev_monitor_receive_device(udev->monitor))) return 0; wlc_log(WLC_LOG_INFO, "udev: got device %s", udev_device_get_sysname(device)); // FIXME: pass correct drm id if (is_hotplug(0, device)) { wlc_log(WLC_LOG_INFO, "udev: hotplug"); struct wlc_output_event ev = { .type = WLC_OUTPUT_EVENT_UPDATE }; wl_signal_emit(&wlc_system_signals()->output, &ev); goto out; }
static bool xcb_call(const char *func, uint32_t line, xcb_void_cookie_t cookie) { xcb_generic_error_t *error; if (!(error = xcb_request_check(x11.connection, cookie))) return true; wlc_log(WLC_LOG_ERROR, "xwm: function %s at line %u x11 error code %d", func, line, error->error_code); free(error); return false; }
static bool page_flip(struct wlc_backend_surface *bsurface) { assert(bsurface && bsurface->internal); struct drm_surface *dsurface = bsurface->internal; assert(!dsurface->flipping); struct drm_fb *fb = &dsurface->fb[dsurface->index]; release_fb(dsurface->surface, fb); struct wlc_output *o; except((o = wl_container_of(bsurface, o, bsurface))); if (!create_fb(dsurface->surface, fb)) return false; if (fb->stride != dsurface->stride) { if (drm.api.drmModeSetCrtc(drm.fd, dsurface->crtc->crtc_id, fb->fd, 0, 0, &dsurface->connector->connector_id, 1, &dsurface->connector->modes[o->active.mode])) goto set_crtc_fail; dsurface->stride = fb->stride; } if (drm.api.drmModePageFlip(drm.fd, dsurface->crtc->crtc_id, fb->fd, DRM_MODE_PAGE_FLIP_EVENT, bsurface)) goto failed_to_page_flip; dsurface->flipping = true; return true; set_crtc_fail: wlc_log(WLC_LOG_WARN, "Failed to set mode: %m"); goto fail; failed_to_page_flip: wlc_log(WLC_LOG_WARN, "Failed to page flip: %m"); fail: release_fb(dsurface->surface, fb); return false; }
void wlc_set_active(bool active) { if (active == wlc.active) return; wlc.active = active; struct wlc_activate_event ev = { .active = active, .vt = 0 }; wl_signal_emit(&wlc.signals.activate, &ev); wlc_log(WLC_LOG_INFO, (wlc.active ? "become active" : "deactive")); } bool wlc_get_active(void) { return wlc.active; } const struct wlc_interface* wlc_interface(void) { return &wlc.interface; } struct wlc_system_signals* wlc_system_signals(void) { return &wlc.signals; } struct wl_event_loop* wlc_event_loop(void) { return wl_display_get_event_loop(wlc.display); } struct wl_display* wlc_display(void) { return wlc.display; } static void compositor_event(struct wl_listener *listener, void *data) { (void)listener, (void)data; // this event is currently only used for knowing when compositor died wl_display_terminate(wlc.display); }
static int open_socket(struct sockaddr_un *addr, size_t path_size) { int fd; socklen_t size = offsetof(struct sockaddr_un, sun_path) + path_size + 1; if ((fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0)) < 0) goto socket_fail; /* Unlink the socket location in case it was being used by a process which left around a stale lockfile. */ unlink(addr->sun_path); if (bind(fd, (struct sockaddr*)addr, size) < 0) goto bind_fail; if (listen(fd, 1) < 0) goto listen_fail; return fd; socket_fail: wlc_log(WLC_LOG_WARN, "Failed to create socket: %s", addr->sun_path); goto fail; bind_fail: wlc_log(WLC_LOG_WARN, "Failed to bind socket: %s", addr->sun_path); goto fail; listen_fail: wlc_log(WLC_LOG_WARN, "Failed to listen to socket"); if (addr->sun_path[0]) unlink(addr->sun_path); goto fail; fail: if (fd >= 0) close(fd); return -1; }
WLC_API void wlc_exec(const char *bin, char *const args[]) { assert(bin); if (chck_cstr_is_empty(bin)) return; pid_t p; if ((p = fork()) == 0) { setsid(); freopen("/dev/null", "w", stdout); freopen("/dev/null", "w", stderr); execvp(bin, args); _exit(EXIT_FAILURE); } else if (p < 0) { wlc_log(WLC_LOG_ERROR, "Failed to fork for '%s'", bin); } }
static void surface_release(struct wlc_backend_surface *bsurface) { struct drm_surface *dsurface = bsurface->internal; struct drm_fb *fb = &dsurface->fb[dsurface->index]; release_fb(dsurface->surface, fb); drm.api.drmModeSetCrtc(drm.fd, dsurface->crtc->crtc_id, dsurface->crtc->buffer_id, dsurface->crtc->x, dsurface->crtc->y, &dsurface->connector->connector_id, 1, &dsurface->crtc->mode); if (dsurface->crtc) drm.api.drmModeFreeCrtc(dsurface->crtc); if (dsurface->surface) gbm.api.gbm_surface_destroy(dsurface->surface); if (dsurface->encoder) drm.api.drmModeFreeEncoder(dsurface->encoder); if (dsurface->connector) drm.api.drmModeFreeConnector(dsurface->connector); wlc_log(WLC_LOG_INFO, "Released drm surface (%p)", bsurface); }
bool wlc_render(struct wlc_render *render, struct wlc_context *context) { assert(render && context); memset(render, 0, sizeof(struct wlc_render)); if (!wlc_context_bind(context)) return NULL; void* (*constructor[])(struct wlc_render_api*) = { wlc_gles2, NULL }; for (uint32_t i = 0; constructor[i]; ++i) { if ((render->render = constructor[i](&render->api))) return true; } wlc_log(WLC_LOG_WARN, "Could not initialize any rendering backend"); wlc_render_release(render, context); return false; }
static GLuint create_shader(const char *source, GLenum shader_type) { assert(source); GLuint shader = gl.api.glCreateShader(shader_type); assert(shader != 0); GL_CALL(gl.api.glShaderSource(shader, 1, (const char **)&source, NULL)); GL_CALL(gl.api.glCompileShader(shader)); GLint status; GL_CALL(gl.api.glGetShaderiv(shader, GL_COMPILE_STATUS, &status)); if (!status) { GLsizei len; char log[1024]; GL_CALL(gl.api.glGetShaderInfoLog(shader, sizeof(log), &len, log)); wlc_log(WLC_LOG_ERROR, "Compiling %s: %*s\n", (shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment"), len, log); abort(); } return shader; }
static struct ctx* create_context(void) { const char *vert_shader = "#version 100\n" "precision mediump float;\n" "uniform vec2 resolution;\n" "mat4 ortho = mat4(" " 2.0/resolution.x, 0, 0, 0," " 0, -2.0/resolution.y, 0, 0," " 0, 0, -1, 0," " -1, 1, 0, 1" ");\n" "attribute vec4 pos;\n" "attribute vec2 uv;\n" "varying vec2 v_uv;\n" "void main() {\n" " gl_Position = ortho * pos;\n" " v_uv = uv;\n" "}\n"; const char *frag_shader_dummy = "#version 100\n" "precision mediump float;\n" "void main() {\n" " gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" "}\n"; const char *frag_shader_cursor = "#version 100\n" "precision highp float;\n" "uniform sampler2D texture0;\n" "varying vec2 v_uv;\n" "void main() {\n" " vec4 palette[3];\n" " palette[0] = vec4(0.0, 0.0, 0.0, 1.0);\n" " palette[1] = vec4(1.0, 1.0, 1.0, 1.0);\n" " palette[2] = vec4(0.0, 0.0, 0.0, 0.0);\n" " gl_FragColor = palette[int(texture2D(texture0, v_uv).r * 256.0)];\n" "}\n"; const char *frag_shader_bg = "#version 100\n" "precision mediump float;\n" "uniform float time;\n" "uniform vec2 resolution;\n" "varying vec2 v_uv;\n" "#define M_PI 3.1415926535897932384626433832795\n" "float impulse(float x, float k) {\n" " float h = k * x;\n" " return h * exp(1.0 - h);\n" "}\n" "void main() {\n" " vec3 color = vec3(0.0);\n" " vec2 pos = (v_uv * 4.0 - 2.0);\n" " float frame = time * M_PI * 10.0;\n" " float f = impulse(0.01, sin(frame) + 1.0) + 0.25;\n" " color += vec3(0.15, 0.3, 0.35) * (1.0 / distance(vec2(1.0, 0.0), pos) * f);\n" " for (int i = 0; i < 3; ++i) {\n" " float t = frame + (float(i) * 1.8);\n" " color += vec3(0.15, 0.18, 0.15) * float(i + 1) * (1.0 / distance(vec2(sin(t * 0.8) * 0.5 + 1.0, cos(t) * 0.5), pos) * 0.09);\n" " }\n" " gl_FragColor = vec4(color, 1.0);\n" "}\n"; const char *frag_shader_rgb = "#version 100\n" "precision mediump float;\n" "uniform sampler2D texture0;\n" "uniform float dim;\n" "varying vec2 v_uv;\n" "void main() {\n" " gl_FragColor = vec4(texture2D(texture0, v_uv).rgb * dim, 1.0);\n" "}\n"; const char *frag_shader_rgba = "#version 100\n" "precision mediump float;\n" "uniform sampler2D texture0;\n" "uniform float dim;\n" "varying vec2 v_uv;\n" "void main() {\n" " vec4 col = texture2D(texture0, v_uv);\n" " gl_FragColor = vec4(col.rgb * dim, col.a);\n" "}\n"; const char *frag_shader_egl = "#version 100\n" "#extension GL_OES_EGL_image_external : require\n" "precision mediump float;\n" "uniform samplerExternalOES texture0;\n" "uniform float dim;\n" "varying vec2 v_uv;\n" "void main()\n" "{\n" " vec4 col = texture2D(texture0, v_uv);\n" " gl_FragColor = vec4(col.rgb * dim, col.a)\n;" "}\n"; #define FRAGMENT_CONVERT_YUV \ " y *= dim;\n" \ " u *= dim;\n" \ " v *= dim;\n" \ " gl_FragColor.r = y + 1.59602678 * v;\n" \ " gl_FragColor.g = y - 0.39176229 * u - 0.81296764 * v;\n" \ " gl_FragColor.b = y + 2.01723214 * u;\n" \ " gl_FragColor.a = 1.0;\n" const char *frag_shader_y_uv = "#version 100\n" "precision mediump float;\n" "uniform sampler2D texture0;\n" "uniform sampler2D texture1;\n" "uniform float dim;\n" "varying vec2 v_uv;\n" "void main() {\n" " float y = 1.16438356 * (texture2D(texture0, v_uv).x - 0.0625);\n" " float u = texture2D(texture1, v_uv).r - 0.5;\n" " float v = texture2D(texture1, v_uv).g - 0.5;\n" FRAGMENT_CONVERT_YUV "}\n"; const char *frag_shader_y_u_v = "#version 100\n" "precision mediump float;\n" "uniform sampler2D texture0;\n" "uniform sampler2D texture1;\n" "uniform sampler2D texture2;\n" "uniform float dim;\n" "varying vec2 v_uv;\n" "void main() {\n" " float y = 1.16438356 * (texture2D(texture0, v_uv).x - 0.0625);\n" " float u = texture2D(texture1, v_uv).x - 0.5;\n" " float v = texture2D(texture2, v_uv).x - 0.5;\n" FRAGMENT_CONVERT_YUV "}\n"; const char *frag_shader_y_xuxv = "#version 100\n" "precision mediump float;\n" "uniform sampler2D texture0;\n" "uniform sampler2D texture1;\n" "uniform float dim;\n" "varying vec2 v_uv;\n" "void main() {\n" " float y = 1.16438356 * (texture2D(texture0, v_uv).x - 0.0625);\n" " float u = texture2D(texture1, v_uv).g - 0.5;\n" " float v = texture2D(texture1, v_uv).a - 0.5;\n" FRAGMENT_CONVERT_YUV "}\n"; struct ctx *context; if (!(context = calloc(1, sizeof(struct ctx)))) return NULL; const char *str; str = (const char*)GL_CALL(gl.api.glGetString(GL_VERSION)); wlc_log(WLC_LOG_INFO, "GL version: %s", str ? str : "(null)"); str = (const char*)GL_CALL(gl.api.glGetString(GL_VENDOR)); wlc_log(WLC_LOG_INFO, "GL vendor: %s", str ? str : "(null)"); context->extensions = (const char*)GL_CALL(gl.api.glGetString(GL_EXTENSIONS)); if (has_extension(context, "GL_OES_EGL_image_external")) { context->api.glEGLImageTargetTexture2DOES = gl.api.glEGLImageTargetTexture2DOES; } else { wlc_log(WLC_LOG_WARN, "gles2: GL_OES_EGL_image_external not available"); frag_shader_egl = frag_shader_dummy; } const struct { const char *vert; const char *frag; } map[PROGRAM_LAST] = { { vert_shader, frag_shader_rgb }, // PROGRAM_RGB { vert_shader, frag_shader_rgba }, // PROGRAM_RGBA { vert_shader, frag_shader_egl }, // PROGRAM_EGL { vert_shader, frag_shader_y_uv }, // PROGRAM_Y_UV { vert_shader, frag_shader_y_u_v }, // PROGRAM_Y_U_V { vert_shader, frag_shader_y_xuxv }, // PROGRAM_Y_XUXV { vert_shader, frag_shader_cursor }, // PROGRAM_CURSOR { vert_shader, frag_shader_bg }, // PROGRAM_BG }; for (GLuint i = 0; i < PROGRAM_LAST; ++i) { GLuint vert = create_shader(map[i].vert, GL_VERTEX_SHADER); GLuint frag = create_shader(map[i].frag, GL_FRAGMENT_SHADER); context->programs[i].obj = gl.api.glCreateProgram(); GL_CALL(gl.api.glAttachShader(context->programs[i].obj, vert)); GL_CALL(gl.api.glAttachShader(context->programs[i].obj, frag)); GL_CALL(gl.api.glLinkProgram(context->programs[i].obj)); GL_CALL(gl.api.glDeleteShader(vert)); GL_CALL(gl.api.glDeleteShader(frag)); GLint status; GL_CALL(gl.api.glGetProgramiv(context->programs[i].obj, GL_LINK_STATUS, &status)); if (!status) { GLsizei len; char log[1024]; GL_CALL(gl.api.glGetProgramInfoLog(context->programs[i].obj, sizeof(log), &len, log)); wlc_log(WLC_LOG_ERROR, "Linking:\n%*s\n", len, log); abort(); } set_program(context, i); GL_CALL(gl.api.glBindAttribLocation(context->programs[i].obj, 0, "pos")); GL_CALL(gl.api.glBindAttribLocation(context->programs[i].obj, 1, "uv")); for (int u = 0; u < UNIFORM_LAST; ++u) { context->programs[i].uniforms[u] = GL_CALL(gl.api.glGetUniformLocation(context->programs[i].obj, uniform_names[u])); } GL_CALL(gl.api.glUniform1i(context->programs[i].uniforms[UNIFORM_TEXTURE0], 0)); GL_CALL(gl.api.glUniform1i(context->programs[i].uniforms[UNIFORM_TEXTURE1], 1)); GL_CALL(gl.api.glUniform1i(context->programs[i].uniforms[UNIFORM_TEXTURE2], 2)); } struct { GLenum format; GLuint w, h; GLenum type; const void *data; } images[TEXTURE_LAST] = { { GL_LUMINANCE, 1, 1, GL_UNSIGNED_BYTE, (GLubyte[]){ 0 } }, // TEXTURE_BLACK
static bool xcb_load(void) { const char *lib = "libxcb.so", *func = NULL; if (!(x11.api.xcb_handle = dlopen(lib, RTLD_LAZY))) { wlc_log(WLC_LOG_WARN, "%s", dlerror()); return false; } #define load(x) (x11.api.x = dlsym(x11.api.xcb_handle, (func = #x))) if (!load(xcb_flush)) goto function_pointer_exception; if (!load(xcb_connection_has_error)) goto function_pointer_exception; if (!load(xcb_get_setup)) goto function_pointer_exception; if (!load(xcb_setup_roots_iterator)) goto function_pointer_exception; if (!load(xcb_generate_id)) goto function_pointer_exception; if (!load(xcb_create_window_checked)) goto function_pointer_exception; if (!load(xcb_destroy_window)) goto function_pointer_exception; if (!load(xcb_map_window_checked)) goto function_pointer_exception; if (!load(xcb_create_pixmap)) goto function_pointer_exception; if (!load(xcb_create_gc)) goto function_pointer_exception; if (!load(xcb_free_pixmap)) goto function_pointer_exception; if (!load(xcb_free_gc)) goto function_pointer_exception; if (!load(xcb_put_image)) goto function_pointer_exception; if (!load(xcb_create_cursor)) goto function_pointer_exception; if (!load(xcb_free_cursor)) goto function_pointer_exception; if (!load(xcb_change_property)) goto function_pointer_exception; if (!load(xcb_change_window_attributes_checked)) goto function_pointer_exception; if (!load(xcb_intern_atom)) goto function_pointer_exception; if (!load(xcb_intern_atom_reply)) goto function_pointer_exception; if (!load(xcb_request_check)) goto function_pointer_exception; if (!load(xcb_poll_for_event)) goto function_pointer_exception; if (!load(xcb_get_file_descriptor)) goto function_pointer_exception; if (!load(xcb_get_extension_data)) goto function_pointer_exception; #undef load return true; function_pointer_exception: wlc_log(WLC_LOG_WARN, "Could not load function '%s' from '%s'", func, lib); return false; }
static bool gles2_load(void) { const char *lib = "libGLESv2.so", *func = NULL; if (!(gl.api.handle = dlopen(lib, RTLD_LAZY))) { wlc_log(WLC_LOG_WARN, "%s", dlerror()); return false; } #define load(x) (gl.api.x = dlsym(gl.api.handle, (func = #x))) if (!(load(glGetError))) goto function_pointer_exception; if (!load(glGetString)) goto function_pointer_exception; if (!load(glEnable)) goto function_pointer_exception; if (!load(glClear)) goto function_pointer_exception; if (!load(glClearColor)) goto function_pointer_exception; if (!load(glViewport)) goto function_pointer_exception; if (!load(glBlendFunc)) goto function_pointer_exception; if (!(load(glCreateShader))) goto function_pointer_exception; if (!(load(glShaderSource))) goto function_pointer_exception; if (!(load(glCompileShader))) goto function_pointer_exception; if (!(load(glDeleteShader))) goto function_pointer_exception; if (!(load(glGetShaderiv))) goto function_pointer_exception; if (!(load(glGetShaderInfoLog))) goto function_pointer_exception; if (!(load(glCreateProgram))) goto function_pointer_exception; if (!(load(glAttachShader))) goto function_pointer_exception; if (!(load(glLinkProgram))) goto function_pointer_exception; if (!(load(glUseProgram))) goto function_pointer_exception; if (!(load(glDeleteProgram))) goto function_pointer_exception; if (!(load(glGetProgramiv))) goto function_pointer_exception; if (!(load(glGetProgramInfoLog))) goto function_pointer_exception; if (!(load(glEnableVertexAttribArray))) goto function_pointer_exception; if (!(load(glBindAttribLocation))) goto function_pointer_exception; if (!(load(glGetUniformLocation))) goto function_pointer_exception; if (!(load(glUniform1i))) goto function_pointer_exception; if (!(load(glUniform1fv))) goto function_pointer_exception; if (!(load(glUniform2fv))) goto function_pointer_exception; if (!(load(glVertexAttribPointer))) goto function_pointer_exception; if (!(load(glDrawArrays))) goto function_pointer_exception; if (!(load(glGenTextures))) goto function_pointer_exception; if (!(load(glDeleteTextures))) goto function_pointer_exception; if (!(load(glBindTexture))) goto function_pointer_exception; if (!(load(glActiveTexture))) goto function_pointer_exception; if (!(load(glTexParameteri))) goto function_pointer_exception; if (!(load(glPixelStorei))) goto function_pointer_exception; if (!(load(glTexImage2D))) goto function_pointer_exception; if (!(load(glReadPixels))) goto function_pointer_exception; // Needed for EGL hw surfaces load(glEGLImageTargetTexture2DOES); #undef load return true; function_pointer_exception: wlc_log(WLC_LOG_WARN, "Could not load function '%s' from '%s'", func, lib); return false; }
static int input_event(int fd, uint32_t mask, void *data) { (void)fd, (void)mask; struct input *input = data; if (libinput_dispatch(input->handle) != 0) wlc_log(WLC_LOG_WARN, "Failed to dispatch libinput"); struct libinput_event *event; while ((event = libinput_get_event(input->handle))) { struct libinput *handle = libinput_event_get_context(event); struct libinput_device *device = libinput_event_get_device(event); (void)handle; switch (libinput_event_get_type(event)) { case LIBINPUT_EVENT_DEVICE_ADDED: WLC_INTERFACE_EMIT(input.created, device); break; case LIBINPUT_EVENT_DEVICE_REMOVED: WLC_INTERFACE_EMIT(input.destroyed, device); break; case LIBINPUT_EVENT_POINTER_MOTION: { struct libinput_event_pointer *pev = libinput_event_get_pointer_event(event); struct wlc_input_event ev = {0}; ev.type = WLC_INPUT_EVENT_MOTION; ev.time = libinput_event_pointer_get_time(pev); ev.motion.dx = libinput_event_pointer_get_dx(pev); ev.motion.dy = libinput_event_pointer_get_dy(pev); wl_signal_emit(&wlc_system_signals()->input, &ev); } break; case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: { struct libinput_event_pointer *pev = libinput_event_get_pointer_event(event); struct wlc_input_event ev = {0}; ev.type = WLC_INPUT_EVENT_MOTION_ABSOLUTE; ev.time = libinput_event_pointer_get_time(pev); ev.motion_abs.x = pointer_abs_x; ev.motion_abs.y = pointer_abs_y; ev.motion_abs.internal = pev; wl_signal_emit(&wlc_system_signals()->input, &ev); } break; case LIBINPUT_EVENT_POINTER_BUTTON: { struct libinput_event_pointer *pev = libinput_event_get_pointer_event(event); struct wlc_input_event ev = {0}; ev.type = WLC_INPUT_EVENT_BUTTON; ev.time = libinput_event_pointer_get_time(pev); ev.button.code = libinput_event_pointer_get_button(pev); ev.button.state = (enum wl_pointer_button_state)libinput_event_pointer_get_button_state(pev); wl_signal_emit(&wlc_system_signals()->input, &ev); } break; case LIBINPUT_EVENT_POINTER_AXIS: { struct libinput_event_pointer *pev = libinput_event_get_pointer_event(event); struct wlc_input_event ev = {0}; ev.type = WLC_INPUT_EVENT_SCROLL; ev.time = libinput_event_pointer_get_time(pev); #if LIBINPUT_VERSION_MAJOR == 0 && LIBINPUT_VERSION_MINOR < 8 /* < libinput 0.8.x (at least to 0.6.x) */ const enum wl_pointer_axis axis = libinput_event_pointer_get_axis(pev); ev.scroll.amount[(axis == LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)] = libinput_event_pointer_get_axis_value(pev); ev.scroll.axis_bits |= (axis == LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL ? WLC_SCROLL_AXIS_HORIZONTAL : WLC_SCROLL_AXIS_VERTICAL); #else /* > libinput 0.8.0 */ if (libinput_event_pointer_has_axis(pev, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) { ev.scroll.amount[0] = libinput_event_pointer_get_axis_value(pev, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL); ev.scroll.axis_bits |= WLC_SCROLL_AXIS_VERTICAL; } if (libinput_event_pointer_has_axis(pev, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) { ev.scroll.amount[1] = libinput_event_pointer_get_axis_value(pev, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL); ev.scroll.axis_bits |= WLC_SCROLL_AXIS_HORIZONTAL; } #endif // We should get other axis information from libinput as well, like source (finger, wheel) (v0.8) wl_signal_emit(&wlc_system_signals()->input, &ev); } break; case LIBINPUT_EVENT_KEYBOARD_KEY: { struct libinput_event_keyboard *kev = libinput_event_get_keyboard_event(event); struct wlc_input_event ev = {0}; ev.type = WLC_INPUT_EVENT_KEY; ev.time = libinput_event_keyboard_get_time(kev); ev.key.code = libinput_event_keyboard_get_key(kev); ev.key.state = (enum wl_keyboard_key_state)libinput_event_keyboard_get_key_state(kev); ev.device = device; wl_signal_emit(&wlc_system_signals()->input, &ev); } break; case LIBINPUT_EVENT_TOUCH_UP: { struct libinput_event_touch *tev = libinput_event_get_touch_event(event); struct wlc_input_event ev = {0}; ev.type = WLC_INPUT_EVENT_TOUCH; ev.time = libinput_event_touch_get_time(tev); ev.touch.type = wlc_touch_type_for_libinput_type(libinput_event_get_type(event)); ev.touch.slot = libinput_event_touch_get_seat_slot(tev); wl_signal_emit(&wlc_system_signals()->input, &ev); } break; case LIBINPUT_EVENT_TOUCH_DOWN: case LIBINPUT_EVENT_TOUCH_MOTION: { struct libinput_event_touch *tev = libinput_event_get_touch_event(event); struct wlc_input_event ev = {0}; ev.type = WLC_INPUT_EVENT_TOUCH; ev.time = libinput_event_touch_get_time(tev); ev.touch.type = wlc_touch_type_for_libinput_type(libinput_event_get_type(event)); ev.touch.x = touch_abs_x; ev.touch.y = touch_abs_y; ev.touch.internal = tev; ev.touch.slot = libinput_event_touch_get_seat_slot(tev); wl_signal_emit(&wlc_system_signals()->input, &ev); } break; case LIBINPUT_EVENT_TOUCH_FRAME: case LIBINPUT_EVENT_TOUCH_CANCEL: { struct libinput_event_touch *tev = libinput_event_get_touch_event(event); struct wlc_input_event ev = {0}; ev.type = WLC_INPUT_EVENT_TOUCH; ev.time = libinput_event_touch_get_time(tev); ev.touch.type = wlc_touch_type_for_libinput_type(libinput_event_get_type(event)); wl_signal_emit(&wlc_system_signals()->input, &ev); } break; default: break; } libinput_event_destroy(event); } return 0; }
static bool egl_load(void) { const char *lib = "libEGL.so", *func = NULL; if (!(egl.api.handle = dlopen(lib, RTLD_LAZY))) { wlc_log(WLC_LOG_WARN, "%s", dlerror()); return false; } #define load(x) (egl.api.x = dlsym(egl.api.handle, (func = #x))) if (!load(eglGetError)) goto function_pointer_exception; if (!load(eglGetDisplay)) goto function_pointer_exception; if (!load(eglInitialize)) goto function_pointer_exception; if (!load(eglTerminate)) goto function_pointer_exception; if (!load(eglQueryString)) goto function_pointer_exception; if (!load(eglChooseConfig)) goto function_pointer_exception; if (!load(eglGetConfigAttrib)) goto function_pointer_exception; if (!load(eglBindAPI)) goto function_pointer_exception; if (!load(eglQueryContext)) goto function_pointer_exception; if (!load(eglCreateContext)) goto function_pointer_exception; if (!load(eglDestroyContext)) goto function_pointer_exception; if (!load(eglCreateWindowSurface)) goto function_pointer_exception; if (!load(eglDestroySurface)) goto function_pointer_exception; if (!load(eglMakeCurrent)) goto function_pointer_exception; if (!load(eglSwapBuffers)) goto function_pointer_exception; if (!load(eglSwapInterval)) goto function_pointer_exception; if (!load(eglGetProcAddress)) goto function_pointer_exception; #undef load #define load(x) (egl.api.x = (void*)egl.api.eglGetProcAddress((func = #x))) // EGL surfaces won't work without these load(eglCreateImageKHR); load(eglDestroyImageKHR); load(eglBindWaylandDisplayWL); load(eglUnbindWaylandDisplayWL); load(eglQueryWaylandBufferWL); #undef load return true; function_pointer_exception: wlc_log(WLC_LOG_WARN, "Could not load function '%s' from '%s'", func, lib); return false; }
static bool open_display(int socks[2]) { int lock_fd, dpy = -1; char lock_name[64]; retry: dpy += 1; for (lock_fd = -1; dpy <= 32 && lock_fd < 0; ++dpy) { snprintf(lock_name, sizeof(lock_name), LOCK_FMT, dpy); if ((lock_fd = open(lock_name, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0444)) >= 0) break; if ((lock_fd = open(lock_name, O_RDONLY | O_CLOEXEC)) < 0) continue; char pid[12]; memset(pid, 0, sizeof(pid)); ssize_t bytes = read(lock_fd, pid, sizeof(pid) - 1); close(lock_fd); lock_fd = -1; if (bytes != sizeof(pid) - 1) continue; pid_t owner; if (!chck_cstr_to_i32(pid, &owner)) continue; /** * Check if the pid for existing lock file is not alive by * sending kill signal and checking that errno == ESRCH (process not found, in most cases) */ errno = 0; if (kill(owner, 0) != 0 && errno == ESRCH) { unlink(lock_name); snprintf(lock_name, sizeof(lock_name), SOCKET_FMT, dpy); unlink(lock_name); /* try open again, as the X server for this lock is not running, * if we fail here, give up and try next display */ snprintf(lock_name, sizeof(lock_name), LOCK_FMT, dpy); if ((lock_fd = open(lock_name, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0444)) >= 0) break; } } if (dpy > 32) goto no_open_display; char pid[12]; snprintf(pid, sizeof(pid), "%10d", getpid()); if (write(lock_fd, pid, sizeof(pid) - 1) != sizeof(pid) - 1) { unlink(lock_name); close(lock_fd); goto retry; } close(lock_fd); struct sockaddr_un addr = { .sun_family = AF_LOCAL }; addr.sun_path[0] = '\0'; size_t path_size = snprintf(addr.sun_path + 1, sizeof(addr.sun_path) - 1, SOCKET_FMT, dpy); if ((socks[0] = open_socket(&addr, path_size)) < 0) { unlink(lock_name); unlink(addr.sun_path + 1); goto retry; } mkdir(socket_dir, 0777); path_size = snprintf(addr.sun_path, sizeof(addr.sun_path), SOCKET_FMT, dpy); if ((socks[1] = open_socket(&addr, path_size)) < 0) { close(socks[0]); unlink(lock_name); unlink(addr.sun_path); goto retry; } snprintf(xserver.display_name, sizeof(xserver.display_name), ":%d", (xserver.display = dpy)); return true; no_open_display: wlc_log(WLC_LOG_WARN, "No open display in first 32"); goto fail; fail: if (lock_fd > 0) { unlink(lock_name); close(lock_fd); } return false; }