|
// /usr/local/bin/yeet |
|
// needs package libdrm |
|
// compile with gcc -Os -ldrm -I/usr/include/libdrm/ -o yeet yeet.c |
|
#include <fcntl.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <unistd.h> |
|
#include <xf86drm.h> |
|
#include <xf86drmMode.h> |
|
#include <sys/ioctl.h> |
|
#include <linux/kd.h> |
|
|
|
uint64_t get_property_value(int drm_fd, uint32_t object_id, |
|
uint32_t object_type, const char *prop_name) { |
|
drmModeObjectProperties *props = |
|
drmModeObjectGetProperties(drm_fd, object_id, object_type); |
|
for (uint32_t i = 0; i < props->count_props; i++) { |
|
drmModePropertyRes *prop = drmModeGetProperty(drm_fd, props->props[i]); |
|
uint64_t val = props->prop_values[i]; |
|
if (strcmp(prop->name, prop_name) == 0) { |
|
drmModeFreeProperty(prop); |
|
drmModeFreeObjectProperties(props); |
|
return val; |
|
} |
|
drmModeFreeProperty(prop); |
|
} |
|
|
|
printf("Failed to find property %s\n", prop_name); |
|
exit(1); |
|
} |
|
|
|
void add_property(int drm_fd, drmModeAtomicReq *req, uint32_t object_id, |
|
uint32_t object_type, const char *prop_name, uint64_t value) { |
|
uint32_t prop_id = 0; |
|
drmModeObjectProperties *props = |
|
drmModeObjectGetProperties(drm_fd, object_id, object_type); |
|
for (uint32_t i = 0; i < props->count_props; i++) { |
|
drmModePropertyRes *prop = drmModeGetProperty(drm_fd, props->props[i]); |
|
if (strcmp(prop->name, prop_name) == 0) { |
|
prop_id = prop->prop_id; |
|
break; |
|
} |
|
} |
|
|
|
if (prop_id == 0) { |
|
printf("Failed to find property %s\n", prop_name); |
|
exit(1); |
|
} |
|
|
|
drmModeAtomicAddProperty(req, object_id, prop_id, value); |
|
} |
|
|
|
char * get_connector_name(drmModeConnector *conn) { |
|
const char *type_name = drmModeGetConnectorTypeName(conn->connector_type); |
|
if (type_name == NULL) return NULL; |
|
|
|
char *str = NULL; |
|
asprintf(&str, "%s-%d", type_name, conn->connector_type_id); |
|
return str; |
|
} |
|
|
|
int main(int argc, char *argv[]) { |
|
if (argc != 2) { |
|
printf("usage: yeet <CONNECTOR>\n"); |
|
return 1; |
|
} |
|
|
|
char *conn_name = argv[1]; |
|
|
|
printf("Opening DRM fd\n"); |
|
int drm_fd = open("/dev/dri/card1", O_RDWR | O_NONBLOCK); |
|
if (drm_fd < 0) { |
|
perror("open failed"); |
|
return 1; |
|
} |
|
|
|
if (drmSetClientCap(drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) != 0) { |
|
perror("drmSetClientCap(UNIVERSAL_PLANES) failed"); |
|
return 1; |
|
} |
|
if (drmSetClientCap(drm_fd, DRM_CLIENT_CAP_ATOMIC, 1) != 0) { |
|
perror("drmSetClientCap(ATOMIC) failed"); |
|
return 1; |
|
} |
|
|
|
printf("Finding connector %s\n", conn_name); |
|
|
|
drmModeRes *resources = drmModeGetResources(drm_fd); |
|
drmModeConnector *conn = NULL; |
|
drmModeCrtc *crtc = NULL; |
|
for (int i = 0; i < resources->count_connectors; i++) { |
|
uint32_t conn_id = resources->connectors[i]; |
|
conn = drmModeGetConnector(drm_fd, conn_id); |
|
|
|
if (conn->connection != DRM_MODE_CONNECTED) { |
|
drmModeFreeConnector(conn); |
|
conn = NULL; |
|
continue; |
|
} |
|
|
|
char *name = get_connector_name(conn); |
|
if (name == NULL || strcmp(name, conn_name) != 0) { |
|
free(name); |
|
drmModeFreeConnector(conn); |
|
conn = NULL; |
|
continue; |
|
} |
|
|
|
uint64_t crtc_id = get_property_value(drm_fd, conn_id, DRM_MODE_OBJECT_CONNECTOR, "CRTC_ID"); |
|
crtc = drmModeGetCrtc(drm_fd, crtc_id); |
|
if (crtc == NULL) { |
|
printf("Connector %s has no CRTC\n", name); |
|
free(name); |
|
drmModeFreeConnector(conn); |
|
conn = NULL; |
|
return 1; |
|
} |
|
|
|
printf("Connector %s has CRTC %zd\n", name, crtc_id); |
|
free(name); |
|
break; |
|
} |
|
|
|
drmModeFreeResources(resources); |
|
|
|
// Yeet the CRTC |
|
printf("Yeeting the CRTC\n"); |
|
|
|
drmModeAtomicReq *req = drmModeAtomicAlloc(); |
|
|
|
add_property(drm_fd, req, conn->connector_id, DRM_MODE_OBJECT_CONNECTOR, "CRTC_ID", 0); |
|
add_property(drm_fd, req, crtc->crtc_id, DRM_MODE_OBJECT_CRTC, "MODE_ID", 0); |
|
add_property(drm_fd, req, crtc->crtc_id, DRM_MODE_OBJECT_CRTC, "ACTIVE", 0); |
|
|
|
uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET; |
|
int ret = drmModeAtomicCommit(drm_fd, req, flags, NULL); |
|
if (ret != 0) { |
|
printf("Failed to yeet the CRTC\n"); |
|
return 1; |
|
} |
|
|
|
printf("Restoring text mode\n"); |
|
int fd = open("/dev/tty", O_RDWR | O_NOCTTY); |
|
ioctl(fd, KDSETMODE, KD_TEXT); |
|
|
|
return 0; |
|
} |
Errno 13 is
EPERM, aka permission denied.yeetneeds to run as root, and only works if no other application uses that DRM device already, that means on a raw tty, before plymouth, sddm, or wayland/X11.If you run
yeetvia thefix-mode.serviceabove, double-check that the getty / sddm / plymouth services on Ubuntu are named the same way as they are in the service file (that file was made for the Arch Linux service names). Otherwise you might get race conditions and a conflicting service could start before yeet does.It might also be that on Ubuntu you need
/dev/dri/card0instead of/dev/dri/card1, (which one it is depends on whether your distro uses simpledrm before loading the i915 graphics driver, I'm not sure if Ubuntu does that).