435 lines
11 KiB
C
435 lines
11 KiB
C
#include "LunarWM_core.h"
|
|
|
|
#include "LunarWM_render.h"
|
|
#include "LunarWM_wayland.h"
|
|
#include "LunarWM_xr.h"
|
|
#include "RayExt.h"
|
|
#include "common.h"
|
|
#include "vec.h"
|
|
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <spawn.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include <lauxlib.h>
|
|
#include <lua.h>
|
|
#include <lualib.h>
|
|
|
|
extern char **environ;
|
|
|
|
static void cleanup_raylib_egl(LunarWM *wm)
|
|
{
|
|
if (IsWindowReady()) {
|
|
CloseWindow();
|
|
}
|
|
}
|
|
|
|
static void cleanup_lua_cfg(LunarWM *wm)
|
|
{
|
|
if (wm->cman) {
|
|
config_manager_destroy(wm->cman);
|
|
wm->cman = NULL;
|
|
}
|
|
}
|
|
|
|
void LunarWM_set_recenter_from_camera(LunarWM *wm)
|
|
{
|
|
Vector3 pos = wm->renderer.camera.position;
|
|
Vector3 fwd
|
|
= Vector3Normalize(Vector3Subtract(wm->renderer.camera.target, pos));
|
|
|
|
float len_xz = sqrtf(fwd.x * fwd.x + fwd.z * fwd.z);
|
|
float yaw = (len_xz > 1e-6f) ? atan2f(fwd.x, fwd.z) : 0.0f;
|
|
|
|
Quaternion q_step = QuaternionFromAxisAngle((Vector3) { 0, 1, 0 }, -yaw);
|
|
Vector3 t_step = Vector3Negate(Vector3RotateByQuaternion(pos, q_step));
|
|
|
|
Quaternion q_total = QuaternionMultiply(q_step, wm->xr.recenter_rot);
|
|
Vector3 t_total = Vector3Add(
|
|
Vector3RotateByQuaternion(wm->xr.recenter_trans, q_step), t_step);
|
|
|
|
wm->xr.recenter_rot = q_total;
|
|
wm->xr.recenter_trans = t_total;
|
|
wm->xr.recenter_active = true;
|
|
}
|
|
|
|
static void sync_config(LunarWM *wm)
|
|
{
|
|
if (wm->cman->cfg.cubemap) {
|
|
Skybox_init(&wm->renderer.skybox, wm->cman->cfg.cubemap);
|
|
} else {
|
|
Skybox_destroy(&wm->renderer.skybox);
|
|
}
|
|
|
|
if (IsTextureValid(wm->renderer.hud_rt.texture)) {
|
|
UnloadTexture(wm->renderer.hud_rt.texture);
|
|
wm->renderer.hud_rt.texture.id = 0;
|
|
wm->renderer.hud_rt.texture.width = 0;
|
|
wm->renderer.hud_rt.texture.height = 0;
|
|
}
|
|
|
|
int vw = (int)wm->cman->cfg.displays.virtual.resolution.x;
|
|
int vh = (int)wm->cman->cfg.displays.virtual.resolution.y;
|
|
int hud = wm->cman->cfg.displays.hud.size;
|
|
|
|
LunarWM_wayland_update_virtual_outputs(wm, vw, vh, hud);
|
|
}
|
|
|
|
static int l_exec(lua_State *L)
|
|
{
|
|
char const *cmd = luaL_checkstring(L, 1);
|
|
|
|
pid_t pid;
|
|
char *argv[] = { (char *)"sh", (char *)"-c", (char *)cmd, NULL };
|
|
|
|
int rc = posix_spawnp(&pid, "sh", NULL, NULL, argv, environ);
|
|
if (rc != 0) {
|
|
lua_pushnil(L);
|
|
lua_pushfstring(L, "posix_spawnp failed: %s", strerror(rc));
|
|
return 2;
|
|
}
|
|
|
|
lua_pushinteger(L, (lua_Integer)pid);
|
|
return 1;
|
|
}
|
|
|
|
static int l_cycle_next(lua_State *L)
|
|
{
|
|
LunarWM *wm = &g_wm;
|
|
if (vector_size(wm->wayland.v_toplevels) == 0) {
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
|
|
wm->wayland.current_focus++;
|
|
if (wm->wayland.current_focus >= vector_size(wm->wayland.v_toplevels)) {
|
|
wm->wayland.current_focus = 0;
|
|
}
|
|
|
|
LunarWM_Toplevel *tl = wm->wayland.v_toplevels[wm->wayland.current_focus];
|
|
LunarWM_Toplevel_focus(tl);
|
|
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
|
|
static int l_recenter(lua_State *L)
|
|
{
|
|
(void)L;
|
|
|
|
LunarWM_set_recenter_from_camera(&g_wm);
|
|
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
|
|
static int l_reload_config(lua_State *L)
|
|
{
|
|
LunarWM *wm = &g_wm;
|
|
ConfigManager *cm = wm->cman;
|
|
if (!cm) {
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
|
|
config_manager_reload(cm);
|
|
sync_config(wm);
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
|
|
static int l_quit_compositor(lua_State *L)
|
|
{
|
|
(void)L;
|
|
LunarWM_terminate(&g_wm);
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
|
|
bool LunarWM_init(LunarWM *wm)
|
|
{
|
|
memset(wm, 0, sizeof(*wm));
|
|
wm->xr.session = XR_NULL_HANDLE;
|
|
wm->xr.session_state = XR_SESSION_STATE_UNKNOWN;
|
|
wm->xr.environment_blend_mode = XR_ENVIRONMENT_BLEND_MODE_MAX_ENUM;
|
|
wm->xr.local_space = wm->xr.view_space = XR_NULL_HANDLE;
|
|
wm->xr.hand_tracking_system_properties.type
|
|
= XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT;
|
|
wm->xr.hand_tracking_system_properties.next = NULL;
|
|
wm->xr.hand_tracking_system_properties.supportsHandTracking = XR_FALSE;
|
|
wm->xr.hand_tracking_enabled = false;
|
|
wm->renderer.camera.position = (Vector3) { 0, 0, 0 };
|
|
wm->renderer.camera.target = (Vector3) { 0, 0, 1 };
|
|
wm->renderer.camera.up = (Vector3) { 0, 1, 0 };
|
|
wm->renderer.camera.fovy = 45;
|
|
wm->renderer.camera.projection = CAMERA_PERSPECTIVE;
|
|
wm->xr.recenter_rot = (Quaternion) { 0, 0, 0, 1 };
|
|
wm->xr.recenter_trans = (Vector3) { 0, 0, 0 };
|
|
wm->xr.recenter_active = false;
|
|
wm->counter = 0;
|
|
|
|
wm->wm.active_workspace = 0;
|
|
for (size_t i = 0; i < ARRAY_SZ(wm->wm.workspaces); i++) {
|
|
wm->wm.workspaces[i].v_windows = vector_create();
|
|
}
|
|
|
|
wm->cman = config_manager_create(get_config_path());
|
|
assert(wm->cman);
|
|
|
|
{
|
|
lua_State *L = wm->cman->L;
|
|
|
|
lua_newtable(L);
|
|
lua_pushcfunction(L, l_quit_compositor);
|
|
lua_setfield(L, -2, "quit_compositor");
|
|
lua_pushcfunction(L, l_reload_config);
|
|
lua_setfield(L, -2, "reload_config");
|
|
lua_pushcfunction(L, l_recenter);
|
|
lua_setfield(L, -2, "recenter");
|
|
lua_pushcfunction(L, l_cycle_next);
|
|
lua_setfield(L, -2, "cycle_next");
|
|
lua_pushcfunction(L, l_exec);
|
|
lua_setfield(L, -2, "exec");
|
|
lua_setglobal(L, "lunar");
|
|
|
|
config_manager_reload(wm->cman);
|
|
}
|
|
|
|
// if (getenv("DISPLAY") != NULL || getenv("WAYLAND_DISPLAY") != NULL) {
|
|
// wlr_log(WLR_ERROR, "This compositor can only be ran in DRM mode.");
|
|
// return false;
|
|
// }
|
|
|
|
if (!LunarWM_wayland_init(wm)) {
|
|
wlr_log(WLR_ERROR, "Failed to initialize wlroots");
|
|
return false;
|
|
}
|
|
|
|
EGLSurface draw = eglGetCurrentSurface(EGL_DRAW);
|
|
EGLSurface read = eglGetCurrentSurface(EGL_READ);
|
|
if (eglMakeCurrent(wm->wayland.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
|
wm->wayland.egl_context)
|
|
== EGL_FALSE) {
|
|
wlr_log(WLR_ERROR, "Failed to eglMakeCurrent");
|
|
return false;
|
|
}
|
|
|
|
wm->xr.available = false;
|
|
{
|
|
char *no_xr = getenv("LWM_NO_XR");
|
|
bool xr = true;
|
|
|
|
if (no_xr != NULL && no_xr[0] != '\0')
|
|
xr = false;
|
|
|
|
if (xr) {
|
|
if (!LunarWM_xr_init(wm)) {
|
|
wlr_log(
|
|
WLR_ERROR, "Failed to initialize OpenXR! Disabling XR...");
|
|
LunarWM_xr_cleanup(wm);
|
|
} else {
|
|
wm->xr.available = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
wlr_log(WLR_INFO, "OpenGL ES version: %s", glGetString(GL_VERSION));
|
|
InitWindow(0, 0, "");
|
|
|
|
if (eglMakeCurrent(
|
|
wm->wayland.egl_display, draw, read, wm->wayland.egl_context)
|
|
== EGL_FALSE) {
|
|
wlr_log(WLR_ERROR, "Failed to eglMakeCurrent");
|
|
return false;
|
|
}
|
|
|
|
sync_config(wm);
|
|
|
|
wm->initialized = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
void LunarWM_destroy(LunarWM *wm)
|
|
{
|
|
if (!wm)
|
|
return;
|
|
|
|
eglMakeCurrent(wm->wayland.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
|
wm->wayland.egl_context);
|
|
|
|
LunarWM_xr_cleanup(wm);
|
|
cleanup_raylib_egl(wm);
|
|
LunarWM_wayland_cleanup(wm);
|
|
cleanup_lua_cfg(wm);
|
|
|
|
memset(wm, 0, sizeof(*wm));
|
|
}
|
|
|
|
void LunarWM_terminate(LunarWM *wm)
|
|
{
|
|
wlr_log(WLR_INFO, "Stopping compositor");
|
|
wm->running = false;
|
|
}
|
|
|
|
void LunarWM_run(LunarWM *wm)
|
|
{
|
|
assert(wm);
|
|
assert(wm->initialized);
|
|
|
|
wm->renderer.first_frame = true;
|
|
|
|
if (!wlr_backend_start(wm->wayland.backend)) {
|
|
wlr_log(WLR_ERROR, "Failed to start backend");
|
|
return;
|
|
}
|
|
|
|
char const *socket = wl_display_add_socket_auto(wm->wayland.display);
|
|
if (socket == NULL) {
|
|
wlr_log(WLR_ERROR, "Failed to add wayland socket to display");
|
|
return;
|
|
}
|
|
|
|
setenv("WAYLAND_DISPLAY", socket, 1);
|
|
wlr_log(LOG_INFO, "Running on WAYLAND_DISPLAY=%s", socket);
|
|
|
|
wm->running = true;
|
|
|
|
struct timespec last, now;
|
|
clock_gettime(CLOCK_MONOTONIC, &last);
|
|
while (wm->running) {
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
float dt = (now.tv_sec - last.tv_sec)
|
|
+ (now.tv_nsec - last.tv_nsec) / 1000000000.0f;
|
|
last = now;
|
|
|
|
for (size_t i = 0; i < vector_size(wm->wayland.v_toplevels); i++) {
|
|
LunarWM_Toplevel *tl = wm->wayland.v_toplevels[i];
|
|
if (tl->surface) {
|
|
wlr_surface_send_frame_done(tl->surface, &now);
|
|
}
|
|
}
|
|
|
|
wl_display_flush_clients(wm->wayland.display);
|
|
wl_event_loop_dispatch(wm->wayland.event_loop, 0);
|
|
|
|
EGLSurface draw = eglGetCurrentSurface(EGL_DRAW);
|
|
EGLSurface read = eglGetCurrentSurface(EGL_READ);
|
|
if (eglMakeCurrent(wm->wayland.egl_display, EGL_NO_SURFACE,
|
|
EGL_NO_SURFACE, wm->wayland.egl_context)
|
|
== EGL_FALSE) {
|
|
wlr_log(WLR_ERROR, "Failed to eglMakeCurrent");
|
|
return;
|
|
}
|
|
|
|
WindowShouldClose();
|
|
BeginDrawing();
|
|
|
|
if (wm->xr.available) {
|
|
LunarWM_xr_poll_events(wm);
|
|
|
|
if (!wm->xr.session_running) {
|
|
EndDrawing();
|
|
continue;
|
|
}
|
|
|
|
XrFrameState frame_state = {
|
|
.type = XR_TYPE_FRAME_STATE,
|
|
};
|
|
XrFrameWaitInfo frame_wait_info = {
|
|
.type = XR_TYPE_FRAME_WAIT_INFO,
|
|
};
|
|
if (xrWaitFrame(wm->xr.session, &frame_wait_info, &frame_state)
|
|
!= XR_SUCCESS) {
|
|
wlr_log(WLR_ERROR, "Failed to wait for OpenXR frame");
|
|
return;
|
|
}
|
|
|
|
XrFrameBeginInfo frame_begin_info = {
|
|
.type = XR_TYPE_FRAME_BEGIN_INFO,
|
|
};
|
|
XrResult res = xrBeginFrame(wm->xr.session, &frame_begin_info);
|
|
if (res != XR_FRAME_DISCARDED && res != XR_SUCCESS) {
|
|
wlr_log(WLR_ERROR, "Failed to begin the OpenXR Frame: %d", res);
|
|
return;
|
|
}
|
|
|
|
if (wm->xr.hand_tracking_enabled) {
|
|
for (size_t i = 0; i < 2; i++) {
|
|
LunarWM_Hand *hand = &wm->xr.hands[i];
|
|
bool const unobstructed = true;
|
|
if (!wm->xr.LocateHandJointsEXT
|
|
|| hand->hand_tracker == XR_NULL_HANDLE) {
|
|
continue;
|
|
}
|
|
|
|
XrHandJointsMotionRangeInfoEXT mri = {
|
|
.type = XR_TYPE_HAND_JOINTS_MOTION_RANGE_INFO_EXT,
|
|
};
|
|
mri.handJointsMotionRange = unobstructed
|
|
? XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT
|
|
: XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT;
|
|
|
|
XrHandJointsLocateInfoEXT li = {
|
|
.type = XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT,
|
|
.next = &mri,
|
|
.baseSpace = wm->xr.local_space,
|
|
.time = frame_state.predictedDisplayTime,
|
|
};
|
|
|
|
XrHandJointLocationsEXT hji = {
|
|
.type = XR_TYPE_HAND_JOINT_LOCATIONS_EXT,
|
|
.jointCount = XR_HAND_JOINT_COUNT_EXT,
|
|
.jointLocations = hand->joint_locations,
|
|
};
|
|
|
|
if (wm->xr.LocateHandJointsEXT(
|
|
hand->hand_tracker, &li, &hji)
|
|
!= XR_SUCCESS) {
|
|
wlr_log(WLR_ERROR, "Failed to locate hand joints");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
LunarWM_RenderLayerInfo render_layer_info = {
|
|
.predicted_display_time = frame_state.predictedDisplayTime,
|
|
.layer_projection.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION,
|
|
};
|
|
bool const session_active
|
|
= (wm->xr.session_state == XR_SESSION_STATE_SYNCHRONIZED)
|
|
|| (wm->xr.session_state == XR_SESSION_STATE_VISIBLE)
|
|
|| (wm->xr.session_state == XR_SESSION_STATE_FOCUSED);
|
|
if (session_active && (frame_state.shouldRender != 0u)) {
|
|
bool rendered
|
|
= LunarWM_render_layer(wm, &render_layer_info, dt);
|
|
if (rendered) {
|
|
render_layer_info.layers[render_layer_info.layers_count]
|
|
= (XrCompositionLayerBaseHeader *)&render_layer_info
|
|
.layer_projection;
|
|
}
|
|
}
|
|
|
|
XrFrameEndInfo frame_end_info = {
|
|
.type = XR_TYPE_FRAME_END_INFO,
|
|
.displayTime = frame_state.predictedDisplayTime,
|
|
.environmentBlendMode = wm->xr.environment_blend_mode,
|
|
.layerCount = render_layer_info.layers_count,
|
|
.layers = (XrCompositionLayerBaseHeader const **)
|
|
render_layer_info.layers,
|
|
};
|
|
if (xrEndFrame(wm->xr.session, &frame_end_info) != XR_SUCCESS) {
|
|
wlr_log(WLR_ERROR, "Failed to end OpenXR frame");
|
|
return;
|
|
}
|
|
|
|
EndDrawing();
|
|
} else {
|
|
wm->renderer.camera.fovy = 75;
|
|
}
|
|
}
|
|
}
|